Go语言slog库开启AddSource后如何仅输出filename:line或项目相对路径格式的日志源信息
Go语言slog库开启AddSource后如何仅输出filename:line或项目相对路径格式的日志源信息
我之前也碰到过这个糟心的问题——slog开启AddSource后默认输出完整的文件绝对路径,日志里一大串路径看着特别冗余,完全不如zap那种精简的格式清爽。后来摸索出一个最直接的解决办法:通过包装slog的Handler来修改源信息的路径格式,不用动slog的底层代码,还能灵活切换“仅文件名”和“项目相对路径”两种模式。
核心思路
slog的Record结构体里包含了Source字段(对应日志的文件路径和行号),我们可以自定义一个包装Handler,在处理日志记录的时候,把Source.File的绝对路径替换成我们想要的格式,再交给原Handler输出。
完整实现代码
先定义包装Handler的结构体和相关方法:
import ( "context" "log/slog" "path/filepath" "os" ) // TrimSourceHandler 包装原slog.Handler,用于裁剪日志源的路径格式 type TrimSourceHandler struct { slog.Handler projectRoot string // 项目根目录绝对路径 useRelative bool // true: 输出项目相对路径;false: 仅输出文件名 } // NewTrimSourceHandler 创建裁剪路径的Handler // projectRoot传空字符串的话,会自动取当前工作目录作为项目根 func NewTrimSourceHandler(handler slog.Handler, projectRoot string, useRelative bool) (*TrimSourceHandler, error) { var absRoot string var err error if projectRoot == "" { // 自动获取当前工作目录 absRoot, err = os.Getwd() if err != nil { return nil, err } } else { // 确保传入的项目根是绝对路径 absRoot, err = filepath.Abs(projectRoot) if err != nil { return nil, err } } return &TrimSourceHandler{ Handler: handler, projectRoot: absRoot, useRelative: useRelative, }, nil } // Handle 实现slog.Handler接口,处理日志记录时修改源路径 func (h *TrimSourceHandler) Handle(ctx context.Context, r slog.Record) error { if r.Source != nil { if h.useRelative { // 尝试转换为项目相对路径 relPath, err := filepath.Rel(h.projectRoot, r.Source.File) if err == nil && relPath != "" { r.Source.File = relPath } else { // 转换失败(比如日志文件不在项目根目录下),降级为仅输出文件名 r.Source.File = filepath.Base(r.Source.File) } } else { // 直接仅保留文件名 r.Source.File = filepath.Base(r.Source.File) } } // 交给原Handler输出 return h.Handler.Handle(ctx, r) } // WithAttrs 实现slog.Handler接口,传递属性到原Handler func (h *TrimSourceHandler) WithAttrs(attrs []slog.Attr) slog.Handler { return &TrimSourceHandler{ Handler: h.Handler.WithAttrs(attrs), projectRoot: h.projectRoot, useRelative: h.useRelative, } } // WithGroup 实现slog.Handler接口,传递分组到原Handler func (h *TrimSourceHandler) WithGroup(name string) slog.Handler { return &TrimSourceHandler{ Handler: h.Handler.WithGroup(name), projectRoot: h.projectRoot, useRelative: h.useRelative, } }
如何使用
比如我们要创建一个输出项目相对路径的TextHandler:
func main() { // 创建默认的TextHandler,开启AddSource textHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ AddSource: true, Level: slog.LevelDebug, }) // 创建裁剪路径的Handler:useRelative设为true用相对路径,false用仅文件名 // 如果运行时不在项目根目录,可以手动传入项目根的绝对路径,比如"/home/user/my-go-project" trimmedHandler, err := NewTrimSourceHandler(textHandler, "", true) if err != nil { panic(err) } // 用包装后的Handler创建logger logger := slog.New(trimmedHandler) logger.Info("这是一条带裁剪后源路径的测试日志") }
补充说明
- 如果你编译后的二进制文件是在项目外运行的,记得手动传入项目根目录的绝对路径,或者通过环境变量读取(比如
os.Getenv("PROJECT_ROOT")),避免相对路径转换失败。 - 这个方法对JSONHandler同样有效,只需要把原Handler换成
slog.NewJSONHandler(os.Stdout, opts)就行,完全通用。
内容来源于stack exchange
相关产品推荐
相关产品推荐

