Add full monorepo: virtual-banker, backend, frontend, docs, scripts, deployment
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
107
backend/logging/logger.go
Normal file
107
backend/logging/logger.go
Normal file
@@ -0,0 +1,107 @@
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user