package logger import ( "encoding/json" "fmt" "io" "os" "sync" "time" ) // Level represents log level type Level int const ( LevelDebug Level = iota LevelInfo LevelWarn LevelError ) var levelNames = map[Level]string{ LevelDebug: "DEBUG", LevelInfo: "INFO", LevelWarn: "WARN", LevelError: "ERROR", } // Logger provides structured logging type Logger struct { mu sync.Mutex level Level output io.Writer jsonMode bool prefix string } // LogEntry represents a structured log entry type LogEntry struct { Timestamp string `json:"timestamp"` Level string `json:"level"` Message string `json:"message"` Fields map[string]interface{} `json:"fields,omitempty"` Error string `json:"error,omitempty"` } // New creates a new logger func New(level Level, output io.Writer, jsonMode bool) *Logger { if output == nil { output = os.Stdout } return &Logger{ level: level, output: output, jsonMode: jsonMode, } } // SetLevel sets the log level func (l *Logger) SetLevel(level Level) { l.mu.Lock() defer l.mu.Unlock() l.level = level } // SetOutput sets the output writer func (l *Logger) SetOutput(w io.Writer) { l.mu.Lock() defer l.mu.Unlock() l.output = w } // Debug logs a debug message func (l *Logger) Debug(msg string, fields ...map[string]interface{}) { l.log(LevelDebug, msg, nil, fields...) } // Info logs an info message func (l *Logger) Info(msg string, fields ...map[string]interface{}) { l.log(LevelInfo, msg, nil, fields...) } // Warn logs a warning message func (l *Logger) Warn(msg string, fields ...map[string]interface{}) { l.log(LevelWarn, msg, nil, fields...) } // Error logs an error message func (l *Logger) Error(msg string, err error, fields ...map[string]interface{}) { l.log(LevelError, msg, err, fields...) } // log writes a log entry func (l *Logger) log(level Level, msg string, err error, fields ...map[string]interface{}) { if level < l.level { return } l.mu.Lock() defer l.mu.Unlock() entry := LogEntry{ Timestamp: time.Now().Format(time.RFC3339), Level: levelNames[level], Message: msg, } if err != nil { entry.Error = err.Error() } // Merge fields if len(fields) > 0 { entry.Fields = make(map[string]interface{}) for _, f := range fields { for k, v := range f { entry.Fields[k] = v } } } var output string if l.jsonMode { jsonData, jsonErr := json.Marshal(entry) if jsonErr != nil { // Fallback to text format if JSON fails output = fmt.Sprintf("%s [%s] %s", entry.Timestamp, entry.Level, msg) if err != nil { output += fmt.Sprintf(" error=%v", err) } } else { output = string(jsonData) } } else { // Text format output = fmt.Sprintf("%s [%s] %s", entry.Timestamp, entry.Level, msg) if err != nil { output += fmt.Sprintf(" error=%v", err) } if len(entry.Fields) > 0 { for k, v := range entry.Fields { output += fmt.Sprintf(" %s=%v", k, v) } } } fmt.Fprintln(l.output, output) } // WithFields returns a logger with additional fields func (l *Logger) WithFields(fields map[string]interface{}) *Logger { return &Logger{ level: l.level, output: l.output, jsonMode: l.jsonMode, prefix: l.prefix, } } // ParseLevel parses a log level string func ParseLevel(s string) Level { switch s { case "DEBUG", "debug": return LevelDebug case "INFO", "info": return LevelInfo case "WARN", "warn", "WARNING", "warning": return LevelWarn case "ERROR", "error": return LevelError default: return LevelInfo } } // Default logger instance var defaultLogger *Logger func init() { levelStr := os.Getenv("ATLAS_LOG_LEVEL") level := ParseLevel(levelStr) jsonMode := os.Getenv("ATLAS_LOG_FORMAT") == "json" defaultLogger = New(level, os.Stdout, jsonMode) } // Debug logs using default logger func Debug(msg string, fields ...map[string]interface{}) { defaultLogger.Debug(msg, fields...) } // Info logs using default logger func Info(msg string, fields ...map[string]interface{}) { defaultLogger.Info(msg, fields...) } // Warn logs using default logger func Warn(msg string, fields ...map[string]interface{}) { defaultLogger.Warn(msg, fields...) } // Error logs using default logger func Error(msg string, err error, fields ...map[string]interface{}) { defaultLogger.Error(msg, err, fields...) } // SetLevel sets the default logger level func SetLevel(level Level) { defaultLogger.SetLevel(level) } // GetLogger returns the default logger func GetLogger() *Logger { return defaultLogger }