Code sync, security fixes
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
// Package observability provides structured logging, request/job correlation
|
||||
// IDs, and Prometheus-compatible application metrics.
|
||||
//
|
||||
// Logging policy: never log passwords, session tokens, API credentials, whole
|
||||
// uploaded documents or embedding vectors. Content fields are recorded as
|
||||
// lengths and hashes, not values.
|
||||
package observability
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ctxKey int
|
||||
|
||||
const (
|
||||
ctxKeyRequestID ctxKey = iota
|
||||
ctxKeyJobID
|
||||
ctxKeyLogger
|
||||
)
|
||||
|
||||
// NewLogger builds the application logger.
|
||||
func NewLogger(level string, asJSON bool) *slog.Logger {
|
||||
var lv slog.Level
|
||||
switch strings.ToLower(level) {
|
||||
case "debug":
|
||||
lv = slog.LevelDebug
|
||||
case "warn":
|
||||
lv = slog.LevelWarn
|
||||
case "error":
|
||||
lv = slog.LevelError
|
||||
default:
|
||||
lv = slog.LevelInfo
|
||||
}
|
||||
opts := &slog.HandlerOptions{Level: lv}
|
||||
var h slog.Handler
|
||||
if asJSON {
|
||||
h = slog.NewJSONHandler(os.Stdout, opts)
|
||||
} else {
|
||||
h = slog.NewTextHandler(os.Stdout, opts)
|
||||
}
|
||||
return slog.New(h)
|
||||
}
|
||||
|
||||
// WithRequestID attaches a request correlation ID to ctx.
|
||||
func WithRequestID(ctx context.Context, id string) context.Context {
|
||||
return context.WithValue(ctx, ctxKeyRequestID, id)
|
||||
}
|
||||
|
||||
// RequestID returns the correlation ID attached to ctx, if any.
|
||||
func RequestID(ctx context.Context) string {
|
||||
v, _ := ctx.Value(ctxKeyRequestID).(string)
|
||||
return v
|
||||
}
|
||||
|
||||
// WithJobID attaches a job correlation ID to ctx.
|
||||
func WithJobID(ctx context.Context, id string) context.Context {
|
||||
return context.WithValue(ctx, ctxKeyJobID, id)
|
||||
}
|
||||
|
||||
// JobID returns the job ID attached to ctx, if any.
|
||||
func JobID(ctx context.Context) string {
|
||||
v, _ := ctx.Value(ctxKeyJobID).(string)
|
||||
return v
|
||||
}
|
||||
|
||||
// WithLogger attaches a logger to ctx.
|
||||
func WithLogger(ctx context.Context, l *slog.Logger) context.Context {
|
||||
return context.WithValue(ctx, ctxKeyLogger, l)
|
||||
}
|
||||
|
||||
// LoggerFrom returns the context logger, enriched with any correlation IDs.
|
||||
// It never returns nil.
|
||||
func LoggerFrom(ctx context.Context) *slog.Logger {
|
||||
l, _ := ctx.Value(ctxKeyLogger).(*slog.Logger)
|
||||
if l == nil {
|
||||
l = slog.Default()
|
||||
}
|
||||
if id := RequestID(ctx); id != "" {
|
||||
l = l.With("request_id", id)
|
||||
}
|
||||
if id := JobID(ctx); id != "" {
|
||||
l = l.With("job_id", id)
|
||||
}
|
||||
return l
|
||||
}
|
||||
Reference in New Issue
Block a user