package database import ( "context" "errors" "log/slog" "time" "gorm.io/gorm" gormlogger "gorm.io/gorm/logger" ) type structuredGORMLogger struct { logger *slog.Logger level gormlogger.LogLevel slowThreshold time.Duration } func newStructuredGORMLogger(logger *slog.Logger) gormlogger.Interface { return structuredGORMLogger{logger: logger, level: gormlogger.Warn, slowThreshold: 500 * time.Millisecond} } func (l structuredGORMLogger) LogMode(level gormlogger.LogLevel) gormlogger.Interface { l.level = level return l } func (l structuredGORMLogger) Info(_ context.Context, message string, args ...any) { if l.level >= gormlogger.Info { l.logger.Info("gorm", "message", message, "args", args) } } func (l structuredGORMLogger) Warn(_ context.Context, message string, args ...any) { if l.level >= gormlogger.Warn { l.logger.Warn("gorm", "message", message, "args", args) } } func (l structuredGORMLogger) Error(_ context.Context, message string, args ...any) { if l.level >= gormlogger.Error { l.logger.Error("gorm", "message", message, "args", args) } } func (l structuredGORMLogger) Trace(_ context.Context, begin time.Time, query func() (sql string, rowsAffected int64), err error) { elapsed := time.Since(begin) if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) && l.level >= gormlogger.Error { _, rows := query() l.logger.Error("database query failed", "error", err, "rows", rows, "duration_ms", elapsed.Milliseconds()) return } if elapsed > l.slowThreshold && l.level >= gormlogger.Warn { _, rows := query() l.logger.Warn("slow database query", "rows", rows, "duration_ms", elapsed.Milliseconds()) } }