mirror of
https://github.com/fosrl/newt.git
synced 2026-02-07 21:46:39 +00:00
Make logger extensible
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,3 +6,4 @@ nohup.out
|
|||||||
*.iml
|
*.iml
|
||||||
certs/
|
certs/
|
||||||
newt_arm64
|
newt_arm64
|
||||||
|
key
|
||||||
167
examples/README.md
Normal file
167
examples/README.md
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
# Extensible Logger
|
||||||
|
|
||||||
|
This logger package provides a flexible logging system that can be extended with custom log writers.
|
||||||
|
|
||||||
|
## Basic Usage (Current Behavior)
|
||||||
|
|
||||||
|
The logger works exactly as before with no changes required:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "your-project/logger"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Use default logger
|
||||||
|
logger.Info("This works as before")
|
||||||
|
logger.Debug("Debug message")
|
||||||
|
logger.Error("Error message")
|
||||||
|
|
||||||
|
// Or create a custom instance
|
||||||
|
log := logger.NewLogger()
|
||||||
|
log.SetLevel(logger.INFO)
|
||||||
|
log.Info("Custom logger instance")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom Log Writers
|
||||||
|
|
||||||
|
To use a custom log backend, implement the `LogWriter` interface:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type LogWriter interface {
|
||||||
|
Write(level LogLevel, timestamp time.Time, message string)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: OS Log Writer (macOS/iOS)
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "your-project/logger"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create an OS log writer
|
||||||
|
osWriter := logger.NewOSLogWriter(
|
||||||
|
"net.pangolin.Pangolin.PacketTunnel",
|
||||||
|
"PangolinGo",
|
||||||
|
"MyApp",
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a logger with the OS log writer
|
||||||
|
log := logger.NewLoggerWithWriter(osWriter)
|
||||||
|
log.SetLevel(logger.DEBUG)
|
||||||
|
|
||||||
|
// Use it just like the standard logger
|
||||||
|
log.Info("This message goes to os_log")
|
||||||
|
log.Error("Error logged to os_log")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: Custom Writer
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
"your-project/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CustomWriter writes logs to a custom destination
|
||||||
|
type CustomWriter struct {
|
||||||
|
// your custom fields
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *CustomWriter) Write(level logger.LogLevel, timestamp time.Time, message string) {
|
||||||
|
// Your custom logging logic
|
||||||
|
fmt.Printf("[CUSTOM] %s [%s] %s\n", timestamp.Format(time.RFC3339), level.String(), message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
customWriter := &CustomWriter{}
|
||||||
|
log := logger.NewLoggerWithWriter(customWriter)
|
||||||
|
log.Info("Custom logging!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: Multi-Writer (Log to Multiple Destinations)
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
"your-project/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MultiWriter writes to multiple log writers
|
||||||
|
type MultiWriter struct {
|
||||||
|
writers []logger.LogWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMultiWriter(writers ...logger.LogWriter) *MultiWriter {
|
||||||
|
return &MultiWriter{writers: writers}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *MultiWriter) Write(level logger.LogLevel, timestamp time.Time, message string) {
|
||||||
|
for _, writer := range w.writers {
|
||||||
|
writer.Write(level, timestamp, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Log to both standard output and OS log
|
||||||
|
standardWriter := logger.NewStandardWriter()
|
||||||
|
osWriter := logger.NewOSLogWriter("com.example.app", "Main", "App")
|
||||||
|
|
||||||
|
multiWriter := NewMultiWriter(standardWriter, osWriter)
|
||||||
|
log := logger.NewLoggerWithWriter(multiWriter)
|
||||||
|
|
||||||
|
log.Info("This goes to both stdout and os_log!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### Creating Loggers
|
||||||
|
|
||||||
|
- `NewLogger()` - Creates a logger with the default StandardWriter
|
||||||
|
- `NewLoggerWithWriter(writer LogWriter)` - Creates a logger with a custom writer
|
||||||
|
|
||||||
|
### Built-in Writers
|
||||||
|
|
||||||
|
- `NewStandardWriter()` - Standard writer that outputs to stdout (default)
|
||||||
|
- `NewOSLogWriter(subsystem, category, prefix string)` - OS log writer for macOS/iOS (example)
|
||||||
|
|
||||||
|
### Logger Methods
|
||||||
|
|
||||||
|
- `SetLevel(level LogLevel)` - Set minimum log level
|
||||||
|
- `SetOutput(output *os.File)` - Set output file (StandardWriter only)
|
||||||
|
- `Debug(format string, args ...interface{})` - Log debug message
|
||||||
|
- `Info(format string, args ...interface{})` - Log info message
|
||||||
|
- `Warn(format string, args ...interface{})` - Log warning message
|
||||||
|
- `Error(format string, args ...interface{})` - Log error message
|
||||||
|
- `Fatal(format string, args ...interface{})` - Log fatal message and exit
|
||||||
|
|
||||||
|
### Global Functions
|
||||||
|
|
||||||
|
For convenience, you can use global functions that use the default logger:
|
||||||
|
|
||||||
|
- `logger.Debug(format, args...)`
|
||||||
|
- `logger.Info(format, args...)`
|
||||||
|
- `logger.Warn(format, args...)`
|
||||||
|
- `logger.Error(format, args...)`
|
||||||
|
- `logger.Fatal(format, args...)`
|
||||||
|
- `logger.SetOutput(output *os.File)`
|
||||||
|
|
||||||
|
## Migration Guide
|
||||||
|
|
||||||
|
No changes needed! The logger maintains 100% backward compatibility. Your existing code will continue to work without modifications.
|
||||||
|
|
||||||
|
If you want to switch to a custom writer:
|
||||||
|
1. Create your writer implementing `LogWriter`
|
||||||
|
2. Use `NewLoggerWithWriter()` instead of `NewLogger()`
|
||||||
|
3. That's it!
|
||||||
161
examples/logger_examples.go
Normal file
161
examples/logger_examples.go
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
// Example usage patterns for the extensible logger
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fosrl/newt/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Example 1: Using the default logger (works exactly as before)
|
||||||
|
func exampleDefaultLogger() {
|
||||||
|
logger.Info("Starting application")
|
||||||
|
logger.Debug("Debug information")
|
||||||
|
logger.Warn("Warning message")
|
||||||
|
logger.Error("Error occurred")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 2: Using a custom logger instance with standard writer
|
||||||
|
func exampleCustomInstance() {
|
||||||
|
log := logger.NewLogger()
|
||||||
|
log.SetLevel(logger.INFO)
|
||||||
|
log.Info("This is from a custom instance")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 3: Custom writer that adds JSON formatting
|
||||||
|
type JSONWriter struct{}
|
||||||
|
|
||||||
|
func (w *JSONWriter) Write(level logger.LogLevel, timestamp time.Time, message string) {
|
||||||
|
fmt.Printf("{\"time\":\"%s\",\"level\":\"%s\",\"message\":\"%s\"}\n",
|
||||||
|
timestamp.Format(time.RFC3339),
|
||||||
|
level.String(),
|
||||||
|
message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exampleJSONLogger() {
|
||||||
|
jsonWriter := &JSONWriter{}
|
||||||
|
log := logger.NewLoggerWithWriter(jsonWriter)
|
||||||
|
log.Info("This will be logged as JSON")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 4: File writer
|
||||||
|
type FileWriter struct {
|
||||||
|
file *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFileWriter(filename string) (*FileWriter, error) {
|
||||||
|
file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &FileWriter{file: file}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *FileWriter) Write(level logger.LogLevel, timestamp time.Time, message string) {
|
||||||
|
fmt.Fprintf(w.file, "[%s] %s: %s\n",
|
||||||
|
timestamp.Format("2006-01-02 15:04:05"),
|
||||||
|
level.String(),
|
||||||
|
message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *FileWriter) Close() error {
|
||||||
|
return w.file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func exampleFileLogger() {
|
||||||
|
fileWriter, err := NewFileWriter("/tmp/app.log")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer fileWriter.Close()
|
||||||
|
|
||||||
|
log := logger.NewLoggerWithWriter(fileWriter)
|
||||||
|
log.Info("This goes to a file")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 5: Multi-writer to log to multiple destinations
|
||||||
|
type MultiWriter struct {
|
||||||
|
writers []logger.LogWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMultiWriter(writers ...logger.LogWriter) *MultiWriter {
|
||||||
|
return &MultiWriter{writers: writers}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *MultiWriter) Write(level logger.LogLevel, timestamp time.Time, message string) {
|
||||||
|
for _, writer := range w.writers {
|
||||||
|
writer.Write(level, timestamp, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func exampleMultiWriter() {
|
||||||
|
// Log to both stdout and a file
|
||||||
|
standardWriter := logger.NewStandardWriter()
|
||||||
|
fileWriter, _ := NewFileWriter("/tmp/app.log")
|
||||||
|
|
||||||
|
multiWriter := NewMultiWriter(standardWriter, fileWriter)
|
||||||
|
log := logger.NewLoggerWithWriter(multiWriter)
|
||||||
|
|
||||||
|
log.Info("This goes to both stdout and file!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 6: Conditional writer (only log errors to a specific destination)
|
||||||
|
type ErrorOnlyWriter struct {
|
||||||
|
writer logger.LogWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewErrorOnlyWriter(writer logger.LogWriter) *ErrorOnlyWriter {
|
||||||
|
return &ErrorOnlyWriter{writer: writer}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *ErrorOnlyWriter) Write(level logger.LogLevel, timestamp time.Time, message string) {
|
||||||
|
if level >= logger.ERROR {
|
||||||
|
w.writer.Write(level, timestamp, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func exampleConditionalWriter() {
|
||||||
|
errorWriter, _ := NewFileWriter("/tmp/errors.log")
|
||||||
|
errorOnlyWriter := NewErrorOnlyWriter(errorWriter)
|
||||||
|
|
||||||
|
log := logger.NewLoggerWithWriter(errorOnlyWriter)
|
||||||
|
log.Info("This won't be logged")
|
||||||
|
log.Error("This will be logged to errors.log")
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Example 7: OS Log Writer (macOS/iOS only)
|
||||||
|
// Uncomment on Darwin platforms
|
||||||
|
|
||||||
|
func exampleOSLogWriter() {
|
||||||
|
osWriter := logger.NewOSLogWriter(
|
||||||
|
"net.pangolin.Pangolin.PacketTunnel",
|
||||||
|
"PangolinGo",
|
||||||
|
"MyApp",
|
||||||
|
)
|
||||||
|
|
||||||
|
log := logger.NewLoggerWithWriter(osWriter)
|
||||||
|
log.Info("This goes to os_log and can be viewed with Console.app")
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("=== Example 1: Default Logger ===")
|
||||||
|
exampleDefaultLogger()
|
||||||
|
|
||||||
|
fmt.Println("\n=== Example 2: Custom Instance ===")
|
||||||
|
exampleCustomInstance()
|
||||||
|
|
||||||
|
fmt.Println("\n=== Example 3: JSON Logger ===")
|
||||||
|
exampleJSONLogger()
|
||||||
|
|
||||||
|
fmt.Println("\n=== Example 4: File Logger ===")
|
||||||
|
exampleFileLogger()
|
||||||
|
|
||||||
|
fmt.Println("\n=== Example 5: Multi-Writer ===")
|
||||||
|
exampleMultiWriter()
|
||||||
|
|
||||||
|
fmt.Println("\n=== Example 6: Conditional Writer ===")
|
||||||
|
exampleConditionalWriter()
|
||||||
|
}
|
||||||
86
examples/oslog_writer_example.go
Normal file
86
examples/oslog_writer_example.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
//go:build darwin
|
||||||
|
// +build darwin
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CFLAGS: -I../PacketTunnel
|
||||||
|
#include "../PacketTunnel/OSLogBridge.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OSLogWriter is a LogWriter implementation that writes to Apple's os_log
|
||||||
|
type OSLogWriter struct {
|
||||||
|
subsystem string
|
||||||
|
category string
|
||||||
|
prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOSLogWriter creates a new OSLogWriter
|
||||||
|
func NewOSLogWriter(subsystem, category, prefix string) *OSLogWriter {
|
||||||
|
writer := &OSLogWriter{
|
||||||
|
subsystem: subsystem,
|
||||||
|
category: category,
|
||||||
|
prefix: prefix,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the OS log bridge
|
||||||
|
cSubsystem := C.CString(subsystem)
|
||||||
|
cCategory := C.CString(category)
|
||||||
|
defer C.free(unsafe.Pointer(cSubsystem))
|
||||||
|
defer C.free(unsafe.Pointer(cCategory))
|
||||||
|
|
||||||
|
C.initOSLogBridge(cSubsystem, cCategory)
|
||||||
|
|
||||||
|
return writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write implements the LogWriter interface
|
||||||
|
func (w *OSLogWriter) Write(level LogLevel, timestamp time.Time, message string) {
|
||||||
|
// Get caller information (skip 3 frames to get to the actual caller)
|
||||||
|
_, file, line, ok := runtime.Caller(3)
|
||||||
|
if !ok {
|
||||||
|
file = "unknown"
|
||||||
|
line = 0
|
||||||
|
} else {
|
||||||
|
// Get just the filename, not the full path
|
||||||
|
for i := len(file) - 1; i > 0; i-- {
|
||||||
|
if file[i] == '/' {
|
||||||
|
file = file[i+1:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formattedTime := timestamp.Format("2006-01-02 15:04:05.000")
|
||||||
|
fullMessage := fmt.Sprintf("[%s] [%s] [%s] %s:%d - %s",
|
||||||
|
formattedTime, level.String(), w.prefix, file, line, message)
|
||||||
|
|
||||||
|
cMessage := C.CString(fullMessage)
|
||||||
|
defer C.free(unsafe.Pointer(cMessage))
|
||||||
|
|
||||||
|
// Map Go log levels to os_log levels:
|
||||||
|
// 0=DEBUG, 1=INFO, 2=DEFAULT (WARN), 3=ERROR
|
||||||
|
var osLogLevel C.int
|
||||||
|
switch level {
|
||||||
|
case DEBUG:
|
||||||
|
osLogLevel = 0 // DEBUG
|
||||||
|
case INFO:
|
||||||
|
osLogLevel = 1 // INFO
|
||||||
|
case WARN:
|
||||||
|
osLogLevel = 2 // DEFAULT
|
||||||
|
case ERROR, FATAL:
|
||||||
|
osLogLevel = 3 // ERROR
|
||||||
|
default:
|
||||||
|
osLogLevel = 2 // DEFAULT
|
||||||
|
}
|
||||||
|
|
||||||
|
C.logToOSLog(osLogLevel, cMessage)
|
||||||
|
}
|
||||||
@@ -2,8 +2,6 @@ package logger
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -11,7 +9,7 @@ import (
|
|||||||
|
|
||||||
// Logger struct holds the logger instance
|
// Logger struct holds the logger instance
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
logger *log.Logger
|
writer LogWriter
|
||||||
level LogLevel
|
level LogLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,10 +18,18 @@ var (
|
|||||||
once sync.Once
|
once sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewLogger creates a new logger instance
|
// NewLogger creates a new logger instance with the default StandardWriter
|
||||||
func NewLogger() *Logger {
|
func NewLogger() *Logger {
|
||||||
return &Logger{
|
return &Logger{
|
||||||
logger: log.New(os.Stdout, "", 0),
|
writer: NewStandardWriter(),
|
||||||
|
level: DEBUG,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLoggerWithWriter creates a new logger instance with a custom LogWriter
|
||||||
|
func NewLoggerWithWriter(writer LogWriter) *Logger {
|
||||||
|
return &Logger{
|
||||||
|
writer: writer,
|
||||||
level: DEBUG,
|
level: DEBUG,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,9 +55,11 @@ func (l *Logger) SetLevel(level LogLevel) {
|
|||||||
l.level = level
|
l.level = level
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOutput sets the output destination for the logger
|
// SetOutput sets the output destination for the logger (only works with StandardWriter)
|
||||||
func (l *Logger) SetOutput(w io.Writer) {
|
func (l *Logger) SetOutput(output *os.File) {
|
||||||
l.logger.SetOutput(w)
|
if sw, ok := l.writer.(*StandardWriter); ok {
|
||||||
|
sw.SetOutput(output)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// log handles the actual logging
|
// log handles the actual logging
|
||||||
@@ -60,24 +68,8 @@ func (l *Logger) log(level LogLevel, format string, args ...interface{}) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get timezone from environment variable or use local timezone
|
|
||||||
timezone := os.Getenv("LOGGER_TIMEZONE")
|
|
||||||
var location *time.Location
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if timezone != "" {
|
|
||||||
location, err = time.LoadLocation(timezone)
|
|
||||||
if err != nil {
|
|
||||||
// If invalid timezone, fall back to local
|
|
||||||
location = time.Local
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
location = time.Local
|
|
||||||
}
|
|
||||||
|
|
||||||
timestamp := time.Now().In(location).Format("2006/01/02 15:04:05")
|
|
||||||
message := fmt.Sprintf(format, args...)
|
message := fmt.Sprintf(format, args...)
|
||||||
l.logger.Printf("%s: %s %s", level.String(), timestamp, message)
|
l.writer.Write(level, time.Now(), message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug logs debug level messages
|
// Debug logs debug level messages
|
||||||
@@ -128,6 +120,6 @@ func Fatal(format string, args ...interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetOutput sets the output destination for the default logger
|
// SetOutput sets the output destination for the default logger
|
||||||
func SetOutput(w io.Writer) {
|
func SetOutput(output *os.File) {
|
||||||
GetLogger().SetOutput(w)
|
GetLogger().SetOutput(output)
|
||||||
}
|
}
|
||||||
|
|||||||
54
logger/writer.go
Normal file
54
logger/writer.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogWriter is an interface for writing log messages
|
||||||
|
// Implement this interface to create custom log backends (OS log, syslog, etc.)
|
||||||
|
type LogWriter interface {
|
||||||
|
// Write writes a log message with the given level, timestamp, and formatted message
|
||||||
|
Write(level LogLevel, timestamp time.Time, message string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StandardWriter is the default log writer that writes to an io.Writer
|
||||||
|
type StandardWriter struct {
|
||||||
|
output *os.File
|
||||||
|
timezone *time.Location
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStandardWriter creates a new standard writer with the default configuration
|
||||||
|
func NewStandardWriter() *StandardWriter {
|
||||||
|
// Get timezone from environment variable or use local timezone
|
||||||
|
timezone := os.Getenv("LOGGER_TIMEZONE")
|
||||||
|
var location *time.Location
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if timezone != "" {
|
||||||
|
location, err = time.LoadLocation(timezone)
|
||||||
|
if err != nil {
|
||||||
|
// If invalid timezone, fall back to local
|
||||||
|
location = time.Local
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
location = time.Local
|
||||||
|
}
|
||||||
|
|
||||||
|
return &StandardWriter{
|
||||||
|
output: os.Stdout,
|
||||||
|
timezone: location,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOutput sets the output destination
|
||||||
|
func (w *StandardWriter) SetOutput(output *os.File) {
|
||||||
|
w.output = output
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write implements the LogWriter interface
|
||||||
|
func (w *StandardWriter) Write(level LogLevel, timestamp time.Time, message string) {
|
||||||
|
formattedTime := timestamp.In(w.timezone).Format("2006/01/02 15:04:05")
|
||||||
|
fmt.Fprintf(w.output, "%s: %s %s\n", level.String(), formattedTime, message)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user