20251209
This commit is contained in:
164
archive/bbs-go/server/internal/pkg/gormlogs/gorm.go
Normal file
164
archive/bbs-go/server/internal/pkg/gormlogs/gorm.go
Normal file
@@ -0,0 +1,164 @@
|
||||
// see https://github.com/orandin/slog-gorm/tree/master
|
||||
|
||||
package gormlogs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
gormlogger "gorm.io/gorm/logger"
|
||||
"gorm.io/gorm/utils"
|
||||
)
|
||||
|
||||
type LogType string
|
||||
|
||||
const (
|
||||
ErrorLogType LogType = "sql_error"
|
||||
SlowQueryLogType LogType = "slow_query"
|
||||
DefaultLogType LogType = "default"
|
||||
|
||||
SourceField = "file"
|
||||
ErrorField = "error"
|
||||
QueryField = "query"
|
||||
DurationField = "duration"
|
||||
SlowQueryField = "slow_query"
|
||||
RowsField = "rows"
|
||||
)
|
||||
|
||||
// New creates a new logger for gorm.io/gorm
|
||||
func New(options ...Option) *logger {
|
||||
l := logger{
|
||||
ignoreRecordNotFoundError: true,
|
||||
errorField: ErrorField,
|
||||
sourceField: SourceField,
|
||||
|
||||
// log levels
|
||||
logLevel: map[LogType]slog.Level{
|
||||
ErrorLogType: slog.LevelError,
|
||||
SlowQueryLogType: slog.LevelWarn,
|
||||
DefaultLogType: slog.LevelInfo,
|
||||
},
|
||||
}
|
||||
|
||||
// Apply options
|
||||
for _, option := range options {
|
||||
option(&l)
|
||||
}
|
||||
|
||||
if l.slogger == nil {
|
||||
// If no slogger is defined, use the default Logger
|
||||
l.slogger = slog.Default()
|
||||
}
|
||||
|
||||
return &l
|
||||
}
|
||||
|
||||
type logger struct {
|
||||
slogger *slog.Logger
|
||||
ignoreTrace bool
|
||||
ignoreRecordNotFoundError bool
|
||||
traceAll bool
|
||||
slowThreshold time.Duration
|
||||
logLevel map[LogType]slog.Level
|
||||
contextKeys map[string]string
|
||||
|
||||
sourceField string
|
||||
errorField string
|
||||
}
|
||||
|
||||
// LogMode log mode
|
||||
func (l logger) LogMode(_ gormlogger.LogLevel) gormlogger.Interface {
|
||||
// log level is set by slog
|
||||
return l
|
||||
}
|
||||
|
||||
// Info logs info
|
||||
func (l logger) Info(ctx context.Context, msg string, args ...any) {
|
||||
l.log(l.slogger.InfoContext, ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Warn logs warn messages
|
||||
func (l logger) Warn(ctx context.Context, msg string, args ...any) {
|
||||
l.log(l.slogger.WarnContext, ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Error logs error messages
|
||||
func (l logger) Error(ctx context.Context, msg string, args ...any) {
|
||||
l.log(l.slogger.ErrorContext, ctx, msg, args...)
|
||||
}
|
||||
|
||||
// log adds context attributes and logs a message with the given slog function
|
||||
func (l logger) log(f func(ctx context.Context, msg string, args ...any), ctx context.Context, msg string, args ...any) {
|
||||
|
||||
// Append context attributes
|
||||
args = l.appendContextAttributes(ctx, args)
|
||||
|
||||
// Call slog
|
||||
f(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Trace logs sql message
|
||||
func (l logger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
|
||||
if l.ignoreTrace {
|
||||
return // Silent
|
||||
}
|
||||
|
||||
elapsed := time.Since(begin)
|
||||
switch {
|
||||
case err != nil && (!errors.Is(err, gorm.ErrRecordNotFound) || !l.ignoreRecordNotFoundError):
|
||||
sql, rows := fc()
|
||||
|
||||
// Append context attributes
|
||||
attributes := l.appendContextAttributes(ctx, []any{
|
||||
slog.Any(l.errorField, err),
|
||||
slog.String(QueryField, sql),
|
||||
slog.Duration(DurationField, elapsed),
|
||||
slog.Int64(RowsField, rows),
|
||||
slog.String(l.sourceField, utils.FileWithLineNum()),
|
||||
})
|
||||
|
||||
l.slogger.Log(ctx, l.logLevel[ErrorLogType], err.Error(), attributes...)
|
||||
|
||||
case l.slowThreshold != 0 && elapsed > l.slowThreshold:
|
||||
sql, rows := fc()
|
||||
|
||||
// Append context attributes
|
||||
attributes := l.appendContextAttributes(ctx, []any{
|
||||
slog.Bool(SlowQueryField, true),
|
||||
slog.String(QueryField, sql),
|
||||
slog.Duration(DurationField, elapsed),
|
||||
slog.Int64(RowsField, rows),
|
||||
slog.String(l.sourceField, utils.FileWithLineNum()),
|
||||
})
|
||||
l.slogger.Log(ctx, l.logLevel[SlowQueryLogType], fmt.Sprintf("slow sql query [%s >= %v]", elapsed, l.slowThreshold), attributes...)
|
||||
|
||||
case l.traceAll:
|
||||
sql, rows := fc()
|
||||
|
||||
// Append context attributes
|
||||
attributes := l.appendContextAttributes(ctx, []any{
|
||||
slog.String(QueryField, sql),
|
||||
slog.Duration(DurationField, elapsed),
|
||||
slog.Int64(RowsField, rows),
|
||||
slog.String(l.sourceField, utils.FileWithLineNum()),
|
||||
})
|
||||
|
||||
l.slogger.Log(ctx, l.logLevel[DefaultLogType], fmt.Sprintf("SQL query executed [%s]", elapsed), attributes...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l logger) appendContextAttributes(ctx context.Context, args []any) []any {
|
||||
if args == nil {
|
||||
args = []any{}
|
||||
}
|
||||
for k, v := range l.contextKeys {
|
||||
if value := ctx.Value(v); value != nil {
|
||||
args = append(args, slog.Any(k, value))
|
||||
}
|
||||
}
|
||||
return args
|
||||
}
|
||||
Reference in New Issue
Block a user