关于go:Go-十年了终于想起要统一-log-库了

35次阅读

共计 3557 个字符,预计需要花费 9 分钟才能阅读完成。

本文参加了思否技术征文,欢送正在浏览的你也退出。

大家好,我是煎鱼。

在日常工作中,打日志是很常见的动作。毕竟不打日志,从外部来讲,一旦出问题,定位、排查都会变的十分艰难。谁也不想大半夜在那靠猜解决问题。

在其余方面,对日志的存储的内容、时长、平安均有不同水平的合规要求,应答客户诉求和提单上门的事件。

日志好不好用,就成了重要的诉求了。

规范库 log 很痛

思考一个问题: 平时你在写 Go 工程时,是否很少间接应用官网规范库 log?

在正式我的项目中,大多是优先应用几个爆款第三方库,例如:Logrus、Zap、zerolog。而规范库 log,在长期调试,屏幕输入的场景居多,占比拟少。

这问题出在了哪里?次要集中在以下方面:

  • 没有日志分级。 不便于分类、定位、排查问题,例如:Error、Warn、Info、Debug 等。
  • 没有结构化日志。 只提供格式化日志,不提供结构化,不便于程序读取、解析,例如:Json 格局。
  • 没有扩展性,灵便度差。 规范库 log 的日志输入都是固定格局,没有一个 Logger 接口标准,让大家都恪守,以至于当初社区纯天然演进,难相互兼容。

除此之外,在用户场景上,有着不蕴含上下文(context)信息、性能不够强劲、无奈引入自定义插件等扩大诉求。基本上第三方库均有实现的,根本都用户的痛点之一。

为什么不早点解决

你可能会想,规范库 log 作为 Go 生态里的外围库,为什么不早点解决?

实际上在 2017 年时,有在社区进行了大规模探讨,惋惜放弃了。起因是:“咱们还没有找到足够多的导入和应用具体 Logger 的 Go 库,因而没有理由持续发展这项工作”。

如下图:

持续摆烂。

救星 slog 库诞生

探讨和指标

在 2022 年 8 月,Go 团队的 @
Jonathan Amsterdam 发动了 discussion: structured, leveled logging 的探讨,试图与这个乱象再度一决雌雄。

提案(含探讨)的指标是:

  • 使用方便。 对现有 Logger 库的考察阐明,开发人员更想要一个简洁且易懂的日志 API。
  • 高性能。 新的 API 心愿做到最大限度的缩小内存调配和锁定。
  • 与运行时跟踪集成。Go 团队正在开发和改良运行时跟踪零碎,基于新 Logger 库的日志将能够无缝连接到这个跟踪零碎中,开发人员可能实现程序操作与运行时的行为相关联。

指标涵盖了前文背景中提到的痛点。我关注到上述的第三点,来自 Go 团队本人的需要,果然最优先要做的需要都是本人想要 PUSH 的需要?雾了雾了。

毕竟曾经 10 年了,本探讨中失去了许多人的倡议和推动,胜利孵化。

疾速 Demo

该库目前曾经通过“石锤”阶段,进入了试验库,导入地址是:golang.org/x/exp/slog。

咱们先上手新日志库 slog 的疾速 Demo,便于大家疾速理解和相熟。

如下代码:

import "log/slog"

func main() {slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr)))
    slog.Info("hello", "name", "Al")
    slog.Error("oops", net.ErrClosed, "status", 500)
    slog.LogAttrs(slog.ErrorLevel, "oops",
        slog.Int("status", 500), slog.Any("err", net.ErrClosed))
}

如果不设置 slog.SetDefault 将会默认输入到规范输入。因为上述程序设置了 os.Stderr,因而会在此输入。

程序后果如下:

time=2022-10-24T16:05:48.054-04:00 level=INFO msg=hello name=Al
time=2022-10-24T16:05:48.054-04:00 level=ERROR msg=oops status=500 err="use of closed network connection"
time=2022-10-24T16:05:48.054-04:00 level=ERROR msg=oops status=500 err="use of closed network connection"

咱们曾经看到了日志分级(Level)、自定义字段追加、设置输出地等个性。在输入格局上,新的 slog 库,将会采取与 logfmt 库相似的形式来实现,内置至多两种格局。

默认的 logfmt 音讯格局:

foo=bar a=14 baz="hello kitty" cool%story=bro f %^asdf

如果想调整为 JSON 格局,可进行设置:

slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stderr)))

会应用 JSON 格局输入:

{"foo": "bar", "a": 14, "baz": "hello kitty", "cool%story": "bro", "f": true, "%^asdf": true}

设计思路

作者将 slog 库的设计分为:前端、后端。

前端,slog 认为你罕用且能看得见的 API 都是前端,例如:Info、Debug 等日志分级的,设置上下文内容的 Context 和自定义字段注入等都蕴含在前端的领域内。

如下办法:

后端,slog 认为理论干具体业务逻辑的 Handler 是后端,并将其形象成了 Handler 接口,只须要实现 Handler 接口,就能够注入自定义 Handler。

如下 Handler 接口:

type Handler interface {
  // 启用记录的日志级别
    Enabled(Level) bool
  // 具体的解决办法,须要 Enabled 返回 true
    Handle(r Record) error

    WithAttrs(attrs []Attr) Handler

    WithGroup(name string) Handler
}

其中你能够看到 Handle 函数有一个 Record 属性,它是一个外围的数据结构。

如下代码:

type Record struct {
    Time time.Time

    Message string

    Level Level

    Context context.Context
}

新的 slog 的外部流程如下:

  1. 前端办法(例如:Info)将所传属性封装为 Record 类型的变量。
  2. 将 Record 类型的变量传递给后端办法(例如:Handle)。
  3. 后端 Handle 办法依据所得 Record,进行对应的格式化、办法调用、日志输入。

与其余 Logger 交互

那回到最开始的问题?

如果咱们当初要写一个公有的 Logger,或是复用 Zap。要怎么做?

后端办法,有两条路(同一条路):

  1. 要不走 Record,调用 NewRecord 将其包装成 Record 类型的变量,再往下传。
  2. 要不走 Handle,将解决逻辑写到自定义 Handle 中去实现。

如果是想在前端办法来解决,很遗憾,Go 没有打算将 slog 前端凋谢。确保了前端稳态,后端可变可扩大的灵活性。

如果有趣味理解如何实现自定义 Handle,能够查看 TextHandler 和 JSONHandler 即可,是官网最佳实际。

上下文注入

经典的 context 场景,slog 库间接内置了相干的函数进行反对。

如下代码:

func FromContext(ctx context.Context) Logger
    FromContext returns the Logger stored in ctx by NewContext, or the default
    Logger if there is none.

func NewContext(ctx context.Context, l Logger) context.Context
    NewContext returns a context that contains the given Logger. Use FromContext
    to retrieve the Logger.

具体的 Demo:

func handle(w http.ResponseWriter, r *http.Request) {rlogger := slog.FromContext(r.Context()).With(
        "method", r.Method,
        "url", r.URL,
        "traceID", getTraceID(r),
    )
    ctx := slog.NewContext(r.Context(), rlogger)
    // ... use slog.FromContext(ctx) ...
}

还是比拟不便的。

总结

在此刻,Go 社区中的 log 库们曾经根本成熟,格局已定的 7788。此时 Go 官网的 slog 库推出,很显著汲取了前者的大量丰盛教训(提案有申明)。

我置信在将来 slog 库,会和更多的 Go 生态的工具链买通,提供更丰盛的关联场景。解决 Go 没有一个靠谱 log 库的痛点。

你感觉这个新库对你有帮忙吗?欢送一起交换。

正文完
 0