71 lines
1.7 KiB
Go
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
|
|
}
|