package logging import ( "context" "encoding/json" "fmt" "log" "os" "time" ) type Logger struct { level string fields map[string]interface{} } func NewLogger(level string) *Logger { return &Logger{level: level, fields: make(map[string]interface{})} } func (l *Logger) WithField(key string, value interface{}) *Logger { out := &Logger{level: l.level, fields: make(map[string]interface{})} for k, v := range l.fields { out.fields[k] = v } out.fields[key] = value return out } func (l *Logger) Info(ctx context.Context, message string) { l.log(ctx, "info", message, nil) } func (l *Logger) Error(ctx context.Context, message string, err error) { l.log(ctx, "error", message, map[string]interface{}{"error": err.Error()}) } func (l *Logger) Warn(ctx context.Context, message string) { l.log(ctx, "warn", message, nil) } func (l *Logger) Debug(ctx context.Context, message string) { l.log(ctx, "debug", message, nil) } func (l *Logger) log(ctx context.Context, level, message string, extra map[string]interface{}) { entry := map[string]interface{}{ "timestamp": time.Now().UTC().Format(time.RFC3339), "level": level, "message": message, } for k, v := range l.fields { entry[k] = v } if extra != nil { for k, v := range extra { entry[k] = v } } entry = sanitizePII(entry) jsonBytes, err := json.Marshal(entry) if err != nil { log.Printf("marshal log: %v", err) return } fmt.Fprintln(os.Stdout, string(jsonBytes)) } func sanitizePII(entry map[string]interface{}) map[string]interface{} { out := make(map[string]interface{}) for k, v := range entry { if k == "password" || k == "api_key" || k == "token" { out[k] = "***REDACTED***" } else { out[k] = v } } return out }