Major changes: - Add internal/logging package with field constants, context propagation, sensitive data auto-redaction, and per-component log levels - Add worker timeout constants (TimeoutQuickOp, TimeoutHealthCheck, etc.) - Extend SDLC with callback handlers, generate endpoints, and executor - Add new cookbook trees for aeries and slackpath progression - Add skeleton templates for queue, realtime, and microservices - Add worker component template with async job processing - Refactor services and handlers to use new logging infrastructure - Split component.go into component_infra.go and component_listing.go Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
153 lines
3.8 KiB
Go
153 lines
3.8 KiB
Go
package logging
|
|
|
|
import (
|
|
"io"
|
|
"log/slog"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
// Logger wraps slog.Logger with convenience methods and standard fields.
|
|
type Logger struct {
|
|
*slog.Logger
|
|
config Config
|
|
}
|
|
|
|
// New creates a new Logger from the given config.
|
|
func New(cfg Config) *Logger {
|
|
return NewWithWriter(cfg, os.Stdout)
|
|
}
|
|
|
|
// NewWithWriter creates a new Logger writing to the given writer.
|
|
func NewWithWriter(cfg Config, w io.Writer) *Logger {
|
|
var handler slog.Handler
|
|
|
|
opts := &slog.HandlerOptions{
|
|
Level: cfg.Level.SlogLevel(),
|
|
AddSource: cfg.AddSource,
|
|
}
|
|
|
|
if cfg.Format == FormatText {
|
|
handler = slog.NewTextHandler(w, opts)
|
|
} else {
|
|
handler = slog.NewJSONHandler(w, opts)
|
|
}
|
|
|
|
// Wrap with redacting handler if enabled
|
|
if cfg.RedactEnabled {
|
|
handler = NewRedactingHandler(handler)
|
|
}
|
|
|
|
return &Logger{
|
|
Logger: slog.New(handler),
|
|
config: cfg,
|
|
}
|
|
}
|
|
|
|
// With returns a new Logger with the given attributes.
|
|
func (l *Logger) With(args ...any) *Logger {
|
|
return &Logger{
|
|
Logger: l.Logger.With(args...),
|
|
config: l.config,
|
|
}
|
|
}
|
|
|
|
// WithComponent returns a new Logger with the component field set.
|
|
func (l *Logger) WithComponent(name string) *Logger {
|
|
return l.With(FieldComponent, name)
|
|
}
|
|
|
|
// WithHandler returns a new Logger with the handler field set.
|
|
func (l *Logger) WithHandler(name string) *Logger {
|
|
return l.With(FieldHandler, name)
|
|
}
|
|
|
|
// WithService returns a new Logger with the service field set.
|
|
func (l *Logger) WithService(name string) *Logger {
|
|
return l.With(FieldService, name)
|
|
}
|
|
|
|
// WithWorker returns a new Logger with the worker field set.
|
|
func (l *Logger) WithWorker(name string) *Logger {
|
|
return l.With(FieldWorker, name)
|
|
}
|
|
|
|
// WithAdapter returns a new Logger with the adapter field set.
|
|
func (l *Logger) WithAdapter(name string) *Logger {
|
|
return l.With(FieldAdapter, name)
|
|
}
|
|
|
|
// WithRequestID returns a new Logger with the request_id field set.
|
|
func (l *Logger) WithRequestID(id string) *Logger {
|
|
return l.With(FieldRequestID, id)
|
|
}
|
|
|
|
// WithProjectID returns a new Logger with the project_id field set.
|
|
func (l *Logger) WithProjectID(id string) *Logger {
|
|
return l.With(FieldProjectID, id)
|
|
}
|
|
|
|
// WithUserID returns a new Logger with the user_id field set.
|
|
func (l *Logger) WithUserID(id string) *Logger {
|
|
return l.With(FieldUserID, id)
|
|
}
|
|
|
|
// WithError returns a new Logger with the error field set.
|
|
// Always use "error" as the field name, never "err" or "e".
|
|
func (l *Logger) WithError(err error) *Logger {
|
|
if err == nil {
|
|
return l
|
|
}
|
|
return l.With(FieldError, err.Error())
|
|
}
|
|
|
|
// WithOperation returns a new Logger with the operation field set.
|
|
func (l *Logger) WithOperation(name string) *Logger {
|
|
return l.With(FieldOperation, name)
|
|
}
|
|
|
|
// Timed returns a function that logs the duration when called.
|
|
// Usage: defer log.Timed("operation_name")()
|
|
func (l *Logger) Timed(operation string) func() {
|
|
start := time.Now()
|
|
return func() {
|
|
duration := time.Since(start)
|
|
l.Info("operation completed",
|
|
FieldOperation, operation,
|
|
FieldDuration, duration.Milliseconds(),
|
|
)
|
|
}
|
|
}
|
|
|
|
// TimedWithLevel returns a function that logs the duration at the specified level.
|
|
// Usage: defer log.TimedWithLevel("operation_name", LevelDebug)()
|
|
func (l *Logger) TimedWithLevel(operation string, level Level) func() {
|
|
start := time.Now()
|
|
return func() {
|
|
duration := time.Since(start)
|
|
msg := "operation completed"
|
|
args := []any{FieldOperation, operation, FieldDuration, duration.Milliseconds()}
|
|
|
|
switch level {
|
|
case LevelDebug:
|
|
l.Debug(msg, args...)
|
|
case LevelInfo:
|
|
l.Info(msg, args...)
|
|
case LevelWarn:
|
|
l.Warn(msg, args...)
|
|
case LevelError:
|
|
l.Error(msg, args...)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Slog returns the underlying slog.Logger for compatibility.
|
|
func (l *Logger) Slog() *slog.Logger {
|
|
return l.Logger
|
|
}
|
|
|
|
// Config returns the logger's configuration.
|
|
func (l *Logger) Config() Config {
|
|
return l.config
|
|
}
|