You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 13:40:30