package logger import ( "fmt" "io" "log" "os" "runtime" "strings" "time" ) // LogLevel represents the severity level of a log message type LogLevel int const ( // DebugLevel for detailed troubleshooting DebugLevel LogLevel = iota // InfoLevel for general operational information InfoLevel // WarnLevel for potentially harmful situations WarnLevel // ErrorLevel for error events that might still allow the application to continue ErrorLevel // FatalLevel for severe error events that will lead the application to abort FatalLevel ) // String returns the string representation of the log level func (l LogLevel) String() string { switch l { case DebugLevel: return "DEBUG" case InfoLevel: return "INFO" case WarnLevel: return "WARN" case ErrorLevel: return "ERROR" case FatalLevel: return "FATAL" default: return "UNKNOWN" } } // Field represents a key-value pair for structured logging type Field struct { Key string Value interface{} } // F creates a new Field func F(key string, value interface{}) Field { return Field{Key: key, Value: value} } // Logger provides structured logging capabilities type Logger struct { level LogLevel writer io.Writer fields []Field context map[string]interface{} } // New creates a new Logger with the specified log level and writer func New(level LogLevel, writer io.Writer) *Logger { if writer == nil { writer = os.Stdout } return &Logger{ level: level, writer: writer, fields: []Field{}, context: make(map[string]interface{}), } } // Debug logs a message at debug level func (l *Logger) Debug(msg string, fields ...Field) { if l.level <= DebugLevel { l.log(DebugLevel, msg, fields...) } } // Info logs a message at info level func (l *Logger) Info(msg string, fields ...Field) { if l.level <= InfoLevel { l.log(InfoLevel, msg, fields...) } } // Warn logs a message at warn level func (l *Logger) Warn(msg string, fields ...Field) { if l.level <= WarnLevel { l.log(WarnLevel, msg, fields...) } } // Error logs a message at error level func (l *Logger) Error(msg string, fields ...Field) { if l.level <= ErrorLevel { l.log(ErrorLevel, msg, fields...) } } // Fatal logs a message at fatal level and then calls os.Exit(1) func (l *Logger) Fatal(msg string, fields ...Field) { if l.level <= FatalLevel { l.log(FatalLevel, msg, fields...) os.Exit(1) } } // WithFields returns a new logger with the given fields added func (l *Logger) WithFields(fields ...Field) *Logger { newLogger := &Logger{ level: l.level, writer: l.writer, fields: append(l.fields, fields...), context: l.context, } return newLogger } // WithContext returns a new logger with the given context added func (l *Logger) WithContext(ctx map[string]interface{}) *Logger { newContext := make(map[string]interface{}) for k, v := range l.context { newContext[k] = v } for k, v := range ctx { newContext[k] = v } newLogger := &Logger{ level: l.level, writer: l.writer, fields: l.fields, context: newContext, } return newLogger } // SetLevel sets the log level func (l *Logger) SetLevel(level LogLevel) { l.level = level } // log formats and writes a log message func (l *Logger) log(level LogLevel, msg string, fields ...Field) { timestamp := time.Now().Format(time.RFC3339) // Get caller information _, file, line, ok := runtime.Caller(2) caller := "unknown" if ok { parts := strings.Split(file, "/") if len(parts) > 2 { caller = fmt.Sprintf("%s:%d", parts[len(parts)-1], line) } else { caller = fmt.Sprintf("%s:%d", file, line) } } // Format fields allFields := append(l.fields, fields...) fieldStr := "" for _, field := range allFields { fieldStr += fmt.Sprintf(" %s=%v", field.Key, field.Value) } // Format context contextStr := "" for k, v := range l.context { contextStr += fmt.Sprintf(" %s=%v", k, v) } // Format log message logMsg := fmt.Sprintf("%s [%s] %s %s%s%s\n", timestamp, level.String(), caller, msg, fieldStr, contextStr) // Write log message _, err := l.writer.Write([]byte(logMsg)) if err != nil { log.Printf("Error writing log message: %v", err) } } // Global logger instance var defaultLogger = New(InfoLevel, os.Stdout) // SetDefaultLogger sets the global logger instance func SetDefaultLogger(logger *Logger) { defaultLogger = logger } // SetDefaultLevel sets the log level for the default logger func SetDefaultLevel(level LogLevel) { defaultLogger.SetLevel(level) } // LogDebug logs a message at debug level using the default logger func LogDebug(msg string, fields ...Field) { defaultLogger.Debug(msg, fields...) } // LogInfo logs a message at info level using the default logger func LogInfo(msg string, fields ...Field) { defaultLogger.Info(msg, fields...) } // LogWarn logs a message at warn level using the default logger func LogWarn(msg string, fields ...Field) { defaultLogger.Warn(msg, fields...) } // LogError logs a message at error level using the default logger func LogError(msg string, fields ...Field) { defaultLogger.Error(msg, fields...) } // LogFatal logs a message at fatal level using the default logger and then calls os.Exit(1) func LogFatal(msg string, fields ...Field) { defaultLogger.Fatal(msg, fields...) } // WithFields returns a new logger with the given fields added using the default logger func WithFields(fields ...Field) *Logger { return defaultLogger.WithFields(fields...) } // WithContext returns a new logger with the given context added using the default logger func WithContext(ctx map[string]interface{}) *Logger { return defaultLogger.WithContext(ctx) }