Go中slog日志包装器AddSource显示错误调用方信息求助
解决slog包装器中AddSource显示错误调用位置的问题
问题原因
当基于slog实现日志包装器并开启AddSource后,日志输出的调用方信息指向包装器文件(如slog_logger.go)而非实际调用日志方法的位置。这是因为slog默认从调用其Log/Info/Error等核心方法的位置开始向上查找调用栈,而你的包装方法(Debugf/Infof/Errorf)相当于在实际调用代码和slog核心逻辑之间多了一层,导致默认的调用栈查找停在了包装器内部。
解决方案:设置调用栈跳过深度
slog提供了WithCallerSkip(n int)选项,用于指定需要跳过的调用栈层数,让slog定位到更上层的实际调用位置。只需修改包装器的日志方法,在调用slog核心方法时传入该选项,跳过包装器自身的调用层即可。
修改后的完整代码
package logger import ( "context" "fmt" "sync" "gopkg.in/natefinch/lumberjack.v2" "golang.org/x/exp/slog" ) type SlogLogger struct { *slog.Logger logFile *lumberjack.Logger } var factory = &LoggerFactory{ loggers: make(map[string]*SlogLogger), } type LoggerFactory struct { mu sync.Mutex loggers map[string]*SlogLogger } type LoggerConfig struct { LogFilePath string LogFormat string } // TODO: Read all the values from config func NewDefaultLogger() *SlogLogger { logFilePath := "./cs_runner.log" factory.mu.Lock() defer factory.mu.Unlock() // Check if a logger instance for this log file already exists if logger, ok := factory.loggers[logFilePath]; ok { return logger } lumberjack := &lumberjack.Logger{ Filename: logFilePath, MaxSize: 1, // megabytes MaxBackups: 5, MaxAge: 28, //days Compress: true, // disabled by default } l := slog.New(slog.NewTextHandler(lumberjack, &slog.HandlerOptions{ Level: slog.LevelDebug, AddSource: true, })) logger := &SlogLogger{l, lumberjack} factory.loggers[logFilePath] = logger return logger } func (l *SlogLogger) Close() { // TODO: Check for any other cleanup activities l.logFile.Close() } func (l *SlogLogger) Debugf(format string, args ...interface{}) { msg := fmt.Sprintf(format, args...) l.Log(context.Background(), slog.LevelDebug, msg, slog.WithCallerSkip(1)) } func (l *SlogLogger) Infof(format string, args ...interface{}) { msg := fmt.Sprintf(format, args...) l.Log(context.Background(), slog.LevelInfo, msg, slog.WithCallerSkip(1)) } func (l *SlogLogger) Errorf(format string, args ...interface{}) { msg := fmt.Sprintf(format, args...) l.Log(context.Background(), slog.LevelError, msg, slog.WithCallerSkip(1)) }
关键修改点
- 引入
context和fmt包:fmt用于格式化带参数的日志消息,context是slog.Log方法的必填参数。 - 重写
Debugf/Infof/Errorf方法:- 先用
fmt.Sprintf处理带占位符的日志格式与参数 - 调用
slog.Logger的Log方法,传入对应日志级别,并通过slog.WithCallerSkip(1)指定跳过当前包装方法的调用层,让slog定位到实际调用日志方法的代码位置。
- 先用
效果验证
修改后,日志输出的source字段将指向实际调用Debugf/Infof/Errorf的文件和行号,示例如下:
time=2023-08-27T10:41:36.805+05:30 level=INFO source=/home/user1/myproject/main.go:25 msg="This is a Debug log message"
注意事项
如果包装器存在多层嵌套(比如包装器又调用了其他中间方法),需要根据实际层数调整WithCallerSkip的数值,确保跳过所有中间层,准确定位到最上层的实际调用代码。
内容的提问来源于stack exchange,提问作者pavan kumar
相关产品推荐
相关产品推荐

