108 lines
2.2 KiB
Go
108 lines
2.2 KiB
Go
package logging
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
// Logger provides structured logging
|
|
type Logger struct {
|
|
level string
|
|
fields map[string]interface{}
|
|
}
|
|
|
|
// NewLogger creates a new logger
|
|
func NewLogger(level string) *Logger {
|
|
return &Logger{
|
|
level: level,
|
|
fields: make(map[string]interface{}),
|
|
}
|
|
}
|
|
|
|
// WithField adds a field to the logger
|
|
func (l *Logger) WithField(key string, value interface{}) *Logger {
|
|
newLogger := &Logger{
|
|
level: l.level,
|
|
fields: make(map[string]interface{}),
|
|
}
|
|
for k, v := range l.fields {
|
|
newLogger.fields[k] = v
|
|
}
|
|
newLogger.fields[key] = value
|
|
return newLogger
|
|
}
|
|
|
|
// Info logs an info message
|
|
func (l *Logger) Info(ctx context.Context, message string) {
|
|
l.log(ctx, "info", message, nil)
|
|
}
|
|
|
|
// Error logs an error message
|
|
func (l *Logger) Error(ctx context.Context, message string, err error) {
|
|
l.log(ctx, "error", message, map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
|
|
// Warn logs a warning message
|
|
func (l *Logger) Warn(ctx context.Context, message string) {
|
|
l.log(ctx, "warn", message, nil)
|
|
}
|
|
|
|
// Debug logs a debug message
|
|
func (l *Logger) Debug(ctx context.Context, message string) {
|
|
l.log(ctx, "debug", message, nil)
|
|
}
|
|
|
|
// log logs a message with structured fields
|
|
func (l *Logger) log(ctx context.Context, level, message string, extraFields map[string]interface{}) {
|
|
entry := map[string]interface{}{
|
|
"timestamp": time.Now().UTC().Format(time.RFC3339),
|
|
"level": level,
|
|
"message": message,
|
|
}
|
|
|
|
// Add logger fields
|
|
for k, v := range l.fields {
|
|
entry[k] = v
|
|
}
|
|
|
|
// Add extra fields
|
|
if extraFields != nil {
|
|
for k, v := range extraFields {
|
|
entry[k] = v
|
|
}
|
|
}
|
|
|
|
// Sanitize PII
|
|
entry = sanitizePII(entry)
|
|
|
|
// Output as JSON
|
|
jsonBytes, err := json.Marshal(entry)
|
|
if err != nil {
|
|
log.Printf("Failed to marshal log entry: %v", err)
|
|
return
|
|
}
|
|
|
|
fmt.Fprintln(os.Stdout, string(jsonBytes))
|
|
}
|
|
|
|
// sanitizePII removes or masks PII from log entries
|
|
func sanitizePII(entry map[string]interface{}) map[string]interface{} {
|
|
sanitized := make(map[string]interface{})
|
|
for k, v := range entry {
|
|
// Mask sensitive fields
|
|
if k == "password" || k == "api_key" || k == "token" {
|
|
sanitized[k] = "***REDACTED***"
|
|
} else {
|
|
sanitized[k] = v
|
|
}
|
|
}
|
|
return sanitized
|
|
}
|
|
|