package logger import ( "encoding/json" "fmt" "io" "log" "os" "strings" "sync" "time" ) type Level int const ( DEBUG Level = iota INFO WARN ERROR ) var levelStrings = map[Level]string{ DEBUG: "DEBUG", INFO: "INFO", WARN: "WARN", ERROR: "ERROR", } var stringToLevel = map[string]Level{ "debug": DEBUG, "info": INFO, "warn": WARN, "error": ERROR, } type Logger struct { logger *log.Logger level Level format string mu sync.RWMutex jsonWriter *jsonLogWriter } type jsonLogWriter struct { out io.Writer } type Config struct { Level string Format string File string } var defaultLogger *Logger func init() { defaultLogger = New(Config{ Level: "info", Format: "text", }) } func New(config Config) *Logger { var output io.Writer = os.Stdout if config.File != "" { file, err := os.OpenFile(config.File, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err == nil { output = io.MultiWriter(file, os.Stdout) } } level := stringToLevel[strings.ToLower(config.Level)] format := strings.ToLower(config.Format) l := &Logger{ level: level, format: format, } if format == "json" { l.jsonWriter = &jsonLogWriter{out: output} l.logger = log.New(l.jsonWriter, "", 0) } else { l.logger = log.New(output, "", log.LstdFlags|log.Lshortfile) } return l } func (w *jsonLogWriter) Write(p []byte) (n int, err error) { entry := map[string]interface{}{ "timestamp": time.Now().Format(time.RFC3339), "message": strings.TrimSpace(string(p)), } jsonData, err := json.Marshal(entry) if err != nil { return 0, err } return w.out.Write(append(jsonData, '\n')) } func (l *Logger) log(level Level, format string, v ...interface{}) { l.mu.RLock() defer l.mu.RUnlock() if level < l.level { return } msg := fmt.Sprintf(format, v...) if l.format == "json" { entry := map[string]interface{}{ "timestamp": time.Now().Format(time.RFC3339), "level": levelStrings[level], "message": msg, } jsonData, _ := json.Marshal(entry) l.logger.Print(string(jsonData)) } else { l.logger.Printf("[%s] %s", levelStrings[level], msg) } } func (l *Logger) Debug(format string, v ...interface{}) { l.log(DEBUG, format, v...) } func (l *Logger) Info(format string, v ...interface{}) { l.log(INFO, format, v...) } func (l *Logger) Warn(format string, v ...interface{}) { l.log(WARN, format, v...) } func (l *Logger) Error(format string, v ...interface{}) { l.log(ERROR, format, v...) } // Global logger functions func Debug(format string, v ...interface{}) { defaultLogger.Debug(format, v...) } func Info(format string, v ...interface{}) { defaultLogger.Info(format, v...) } func Warn(format string, v ...interface{}) { defaultLogger.Warn(format, v...) } func Error(format string, v ...interface{}) { defaultLogger.Error(format, v...) } func Configure(config Config) { defaultLogger = New(config) }