This commit is contained in:
215
internal/logger/logger.go
Normal file
215
internal/logger/logger.go
Normal file
@@ -0,0 +1,215 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user