216 lines
4.5 KiB
Go
216 lines
4.5 KiB
Go
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
|
|
}
|