Files
lemma/server/internal/logging/logger.go

139 lines
3.0 KiB
Go

// Package logging provides a simple logging interface for the server.
package logging
import (
"log/slog"
"os"
"reflect"
)
// Logger represents the interface for logging operations
type Logger interface {
Debug(msg string, args ...any)
Info(msg string, args ...any)
Warn(msg string, args ...any)
Error(msg string, args ...any)
WithGroup(name string) Logger
With(args ...any) Logger
}
// Implementation of the Logger interface using slog
type logger struct {
logger *slog.Logger
}
// Logger is the global logger instance
var defaultLogger Logger
// LogLevel represents the log level
type LogLevel slog.Level
// Log levels
const (
DEBUG LogLevel = LogLevel(slog.LevelDebug)
INFO LogLevel = LogLevel(slog.LevelInfo)
WARN LogLevel = LogLevel(slog.LevelWarn)
ERROR LogLevel = LogLevel(slog.LevelError)
)
// Setup initializes the logger with the given minimum log level
func Setup(minLevel LogLevel) {
opts := &slog.HandlerOptions{
Level: slog.Level(minLevel),
}
defaultLogger = &logger{
logger: slog.New(slog.NewTextHandler(os.Stdout, opts)),
}
}
// ParseLogLevel converts a string to a LogLevel
func ParseLogLevel(level string) LogLevel {
switch level {
case "debug":
return DEBUG
case "warn":
return WARN
case "error":
return ERROR
default:
return INFO
}
}
// Redact redacts sensitive fields from a struct based on the `log` struct tag
// if the tag is set to "redact" the field value is replaced with "[REDACTED]"
func Redact(v any) map[string]any {
result := make(map[string]any)
val := reflect.ValueOf(v)
typ := val.Type()
for i := 0; i < val.NumField(); i++ {
field := typ.Field(i)
if tag := field.Tag.Get("log"); tag != "" {
switch tag {
case "redact":
result[field.Name] = "[REDACTED]"
default:
result[field.Name] = val.Field(i).Interface()
}
}
}
return result
}
// Implementation of Logger interface methods
func (l *logger) Debug(msg string, args ...any) {
l.logger.Debug(msg, args...)
}
func (l *logger) Info(msg string, args ...any) {
l.logger.Info(msg, args...)
}
func (l *logger) Warn(msg string, args ...any) {
l.logger.Warn(msg, args...)
}
func (l *logger) Error(msg string, args ...any) {
l.logger.Error(msg, args...)
}
func (l *logger) WithGroup(name string) Logger {
return &logger{logger: l.logger.WithGroup(name)}
}
func (l *logger) With(args ...any) Logger {
return &logger{logger: l.logger.With(args...)}
}
// Debug logs a debug message
func Debug(msg string, args ...any) {
defaultLogger.Debug(msg, args...)
}
// Info logs an info message
func Info(msg string, args ...any) {
defaultLogger.Info(msg, args...)
}
// Warn logs a warning message
func Warn(msg string, args ...any) {
defaultLogger.Warn(msg, args...)
}
// Error logs an error message
func Error(msg string, args ...any) {
defaultLogger.Error(msg, args...)
}
// WithGroup adds a group to the logger context
func WithGroup(name string) Logger {
return defaultLogger.WithGroup(name)
}
// With adds key-value pairs to the logger context
func With(args ...any) Logger {
return defaultLogger.With(args...)
}