登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go slog.NewLogLogger 怎样把结构化日志接入旧 log.Logger

来源:17golang原创

时间:2026-10-27 21:00:32 109浏览 收藏

如果旧模块只能调用 log.Logger,但新代码已经统一使用 log/slog,可以用 slog.NewLogLogger 做一层兼容桥接。它接收一个 slog.Handler 和一个固定的 slog.Level,返回旧式 *log.Logger;旧 Logger 每次写入时,消息会被交给这个 Handler 处理。

官方文档:https://pkg.go.dev/log/slog#NewLogLogger

关键点是:桥接 Logger 负责把旧 API 的文本调用转成 slog 记录,真正决定是否输出、输出到哪里以及怎样排版的,仍然是 Handler。下面按这个边界接入。

先看清 slog.NewLogLogger 的适配关系

NewLogLogger 不是把旧日志重新包装成带任意字段的结构化事件,而是把旧 Logger 的 Output 调用送入指定 Handler。旧模块可以继续使用 Print、Printf 等方法,新模块则继续使用 slog.Logger 的属性写法。

slog Handler 与旧 log.Logger 的适配结构说明图
图1:slog Handler 与旧 log.Logger 的适配结构说明图,不是截图或运行证据。

创建文本 Handler 与桥接 Logger

先准备一个输出目标,再创建 slog.TextHandler。桥接时把 Handler 和固定级别传入 slog.NewLogLogger,返回值就可以交给还没有迁移的旧组件。

package main

import (
    "log"
    "log/slog"
    "os"
)

func main() {
    // 文本 Handler 负责把 slog 记录写到标准输出。
    handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelInfo,
    })

    // 固定级别表示旧 Logger 的每次 Output 以该级别交给 Handler。
    legacyLogger := slog.NewLogLogger(handler, slog.LevelInfo)

    // 旧代码无需改成 slog API,仍可使用 log.Logger 的方法。
    legacyLogger.SetPrefix("legacy: ")
    legacyLogger.SetFlags(log.LstdFlags)
    legacyLogger.Printf("订单 %d 已进入兼容层", 42)
}

这里有两个级别概念:NewLogLogger 的第二个参数给桥接记录指定级别,HandlerOptions.Level 决定 Handler 是否接收该级别。两者不一致时,最终以 Handler 的 Enabled 判断为准。

level=INFO msg="订单 42 已进入兼容层"

固定级别怎样影响旧 API 调用

旧 log.Logger 的 Print 和 Printf 没有 slog 的 Info、Warn、Error 方法区分,所以桥接函数只能为它们统一指定一个级别。例如传入 slog.LevelWarn,这些旧调用都会按 Warn 级别进入 Handler。

package main

import (
    "log/slog"
    "os"
)

func main() {
    // 只接收 Warn 及更高等级,便于控制旧模块的日志噪声。
    handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelWarn,
    })

    // 旧 Logger 的所有输出都被桥接为 Warn 级别。
    legacyLogger := slog.NewLogLogger(handler, slog.LevelWarn)
    legacyLogger.Print("兼容模块发生可关注事件")
}

因此,不要把桥接 Logger 当成完整的结构化日志替代品。如果同一个旧模块需要区分 Info 和 Error,单个 NewLogLogger 无法从原始 Print 调用中推断语义,应在迁移后改用 slog.Logger.Log、Info 或 Error。

旧消息与新属性字段如何分工

桥接层能保留旧调用产生的消息文本,但旧 API 本身没有传递 slog 属性的参数位。需要业务字段时,让新代码直接调用 slog;两种 Logger 可以共用一个 Handler 和输出目标。

package main

import (
    "log/slog"
    "os"
)

func main() {
    // 同一个 Handler 同时服务旧文本调用和新结构化调用。
    handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelInfo,
    })
    legacyLogger := slog.NewLogLogger(handler, slog.LevelInfo)
    modernLogger := slog.New(handler)

    // 旧调用只有一条可格式化的消息。
    legacyLogger.Print("旧模块开始同步")
    // 新调用可以携带可检索的键值属性。
    modernLogger.Info("新模块开始同步", "job_id", "sync-42", "retry", 0)
}

使用 JSON Handler 时,旧消息仍然只是记录中的消息字段;它不会自动变成 module、job_id 等键值。若要给旧组件补充固定上下文,可以在边界外统一设置前缀,或在迁移后改为使用 slog.Logger.With。

兼容适配后的日志级别与输出边界说明图
图2:兼容适配后的日志级别与输出边界说明图,不是截图或运行证据。

没有输出时按三层边界排查

  1. 先看 Handler:确认传入的 Handler 不是空值,并确认它绑定的 Writer 没有被替换或关闭。
  2. 再看级别:核对 NewLogLogger 的固定级别与 HandlerOptions.Level。如果 Handler 的最低级别高于桥接级别,Enabled 会拒绝该记录。
  3. 最后看调用路径:确认旧组件使用的是当前返回的桥接 Logger,而不是仍指向旧的全局 Logger;检查 SetPrefix 和 SetFlags 只是旧 Logger 的格式选项,不会改变 Handler 的级别映射。

常见误区是只修改 Handler 的级别,却忘记桥接 Logger 本身对所有旧调用使用同一个固定级别。若需要不同级别的输出,应该拆分迁移边界,而不是依赖旧文本内容猜测日志级别。

小结

对象负责什么不负责什么
slog.NewLogLogger把旧 Logger 的 Output 调用桥接成 slog 记录不能从 Print 文本推断多个业务级别
slog.Handler判断级别、序列化记录并写入目标不会凭空补出旧 API 没有的属性
旧 log.Logger保持旧模块的 Print/Printf 调用方式不提供 slog 风格的键值属性接口

相关问题

NewLogLogger 的第二个参数应该怎么选?

按旧模块整体语义选择一个固定级别。无法区分 Info 和 Error 时,不要把消息文本当作可靠的级别协议。

为什么桥接后日志还是没有输出?

优先检查 Handler 的最低级别、Writer 是否有效,以及旧模块是否真的拿到了桥接 Logger。

旧 Logger 能直接写入 slog 的结构化字段吗?

不能自动做到。旧调用只能提供消息文本;需要键值字段时,应让新代码直接调用 slog Logger。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>