Files
2026-03-02 12:14:13 -08:00

71 lines
1.7 KiB
Go

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
}