ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

深入解析 jwalterweatherman:面向 Go 的终端输出与文件日志一体化日志库

深入解析 jwalterweatherman:面向 Go 的终端输出与文件日志一体化日志库 深入解析 jwalterweatherman面向 Go 的终端输出与文件日志一体化日志库【免费下载链接】inngestThe leading workflow orchestration platform. Run stateful step functions and AI workflows on serverless, servers, or the edge.项目地址: https://gitcode.com/GitHub_Trending/in/inngest导读jwalterweathermanJWW是一个以 Go 标准库log为核心的轻量级封装库让开发者能用接近fmt.Println的简洁方式同时完成「打印到终端stdout」与「写入文件 / 任意io.Writer」两类输出并通过统一的分级TRACE 到 FATAL 共 7 级与阈值机制实现一套代码既面向用户反馈、又面向日志归档的日志方案。它由 Hugo 作者 Steve Franciaspf13编写最初服务于 Hugo 静态站点引擎在本仓库中作为github.com/spf13/viper的间接依赖被 vendor 进项目见 go.mod其源码位于 vendor/github.com/spf13/jwalterweatherman/。读完本文你将掌握 JWW 的开箱即用用法、7 级日志体系、双阈值配置原理以及 Notepad、LogListener 等底层机制。JWW 是什么比标准 log 多解决什么JWW 本质上是 Go 标准库log的封装primarily a wrapper around the excellent standard log library。相比单独使用标准库它提供了三个核心优势开箱即用Ready to go out of the box无需任何初始化或配置import 之后即可调用全局日志函数一库双用同一套 API 同时负责终端打印与文件/io.Writer日志不必在项目中同时维护两套打印与日志代码日志目的地灵活可以极方便地将日志写到临时文件或任意指定的文件/Writer。设计者最初的目标是让这个库无缝完成以下事情用更有用的分级调用替换代码中散落的println、printf让使用者轻松控制「哪些级别打印到 stdout」让使用者轻松控制「哪些级别写入日志」提供像fmt.Println一样简单的机制向用户打印信息同时这些信息也能被方便地记入日志基于上述两条阈值控制天然支持 verbose详细模式输出与日志没有任何多余的初始化样板cruft拿来就用。第一步直接使用零初始化JWW 的全局使用方式极其简单在你的源码中按反馈类型放置对应调用即可不需要任何 setup。库提供 7 个全局 logger均为基于标准库log的*log.Logger用法与标准库一致Logger级别典型语义jww.TRACETRACE最细粒度的追踪信息jww.DEBUGDEBUG调试信息jww.INFOINFO常规运行信息jww.WARNWARN警告可能不符合预期jww.ERRORERROR较严重的错误用户应知晓jww.CRITICALCRITICAL严重错误jww.FATALFATAL致命错误标准示例出自 README.mdimport ( jww github.com/spf13/jwalterweatherman ) // ... if err ! nil { // 严重错误用户应该知道。默认阈值下既打印到终端也会写入日志。 jww.ERROR.Println(err) } if err2 ! nil { // 这个错误不会实质性地改变应用行为但可能不是用户所预期的。 // 默认阈值下 Warn 会写入日志但不会打印到终端。 jww.WARN.Println(err2) } // 与当前运行相关的信息但对用户不太重要。 // 默认阈值下这条信息会被直接丢弃。 jww.INFO.Printf(information %q, response)非全局方式Notepad 实例如果不希望使用全局状态可以创建自己的NotepadNotepad is where you leave a note!实例notepad : jww.NewNotepad( jww.LevelInfo, // stdout 阈值Info 及以上打印到终端 jww.LevelTrace, // 日志阈值Trace 及以上写入日志 os.Stdout, // 终端输出 Writer ioutil.Discard, // 日志输出 Writer此处丢弃 , // 前缀 log.Ldate|log.Ltime, // 标准库 log flag ) notepad.WARN.Println(Some warning)为什么是 7 级也许你会觉得 7 个级别对任何应用都太多了——作者在 README 中也认同这一点存在 7 个级别不代表你必须全部使用。请为你的项目挑选合适的级别组合级别只需对你的项目有意义即可。第二步可选配置默认阈值JWW 开箱即用的默认阈值行为同时这也是 default_notepad.go 中init()的真实取值DEBUG、TRACE、INFO→ 写入/dev/nullioutil.Discard即被丢弃WARN 及以上→ 写入日志当提供了日志文件 /io.Writer时ERROR 及以上→ 打印到终端stdout。对应源码即defaultNotepad NewNotepad(LevelError, LevelWarn, os.Stdout, ioutil.Discard, , log.Ldate|log.Ltime)即SetStdoutThreshold默认LevelError、SetLogThreshold默认LevelWarn、日志输出默认ioutil.Discard、默认 flag 为log.Ldate|log.Ltime。修改阈值verbose 模式的实现方式阈值可以在任何时候修改但只会影响修改之后执行的调用。这非常适合实现应用的 verbose 模式——你可以自定义 verbose 的含义甚至设置多个不同层次的详细程度import ( jww github.com/spf13/jwalterweatherman ) if Verbose { jww.SetLogThreshold(jww.LevelTrace) // 日志记录所有级别 jww.SetStdoutThreshold(jww.LevelInfo) // 终端打印 Info 及以上 }注意JWW 自身的内部输出也使用日志级别因此如果你希望看到 JWW 内部在做什么应在发起其他调用之前先设置日志级别。设置日志文件JWW 可以写入任意io.Writerjww.SetLogOutput(customWriter)例如传入os.Stdout、打开的文件句柄、bytes.Buffer、网络 Writer 等即可把日志重定向到对应目的地。源码级原理Notepad 与阈值分发机制级别定义与阈值在 notepad.go 中7 个级别通过iota定义顺序即严重程度递增const ( LevelTrace Threshold iota LevelDebug LevelInfo LevelWarn LevelError LevelCritical LevelFatal )对应的前缀映射为TRACE、DEBUG、INFO、WARN、ERROR、CRITICAL、FATAL并以此作为每个 logger 的行前缀。阈值如何决定输出目的地Notepad内部为 7 个级别各持有一个*log.Loggerloggers [7]**log.Logger分别指向TRACE/DEBUG/INFO/WARN/ERROR/CRITICAL/FATAL字段并在init()notepad.go中按当前两个阈值决定每个 logger 写入哪个 Writer级别同时 ≥ 日志阈值与 stdout 阈值 → 写入io.MultiWriter(outHandle, logHandle)同时输出到终端和日志仅 ≥ 日志阈值 → 只写入logHandle日志仅 ≥ stdout 阈值 → 只写入outHandle终端均不满足 → 写入ioutil.Discard直接丢弃。这意味着SetLogThreshold/SetStdoutThreshold/SetLogOutput等配置修改后都会触发init()重建各 logger从而让修改即时生效这正是 README 所述只影响修改之后的调用的实现机制。每个 Notepad 附带 LOG 与 FEEDBACK除了 7 个级别 logger每个 Notepad 还提供两个辅助入口notepad.goLOG固定带LOG: 前缀、携带完整日志元信息日期、时间等的 loggerFEEDBACK一个Feedback类型它同时向终端输出纯文本不带前缀与元信息并向日志写入带标准信息的完整日志适合既要让用户看到简明反馈、又要完整留痕的场景支持Println/Printf/Print。附加能力LogListener 与 CounterLogListenernotepad.go允许为每个日志级别注入额外的io.Writer。它会被每个日志级别各调用一次不感兴趣的级别返回nil即可。关键特性是即使当前配置将该级别丢弃不打印不记录Listener 依然能收到对应事件——因此可以在单元测试中统计 ERROR 数量即使它没有输出到控制台。配套的 log_counter.go 提供了现成的Counter一个原子的io.Writer每次Write自增计数与LogCounter(counter, threshold)工厂用于统计 ≥ 某阈值的日志条数典型用途是测试断言counter : jww.Counter{} jww.SetLogListeners(jww.LogCounter(counter, jww.LevelError)) // ... 执行可能产生 ERROR 的代码 ... if got : counter.Count(); got ! expected { t.Errorf(expected %d errors, got %d, expected, got) }全局 API 与 Notepad API 的对应全局函数default_notepad.go都只是操作包级默认defaultNotepad的薄封装修改后通过reloadDefaultNotepad()刷新全局 logger 指针全局函数对应 Notepad 方法作用SetLogThresholdSetLogThreshold设置日志阈值SetStdoutThresholdSetStdoutThreshold设置终端输出阈值SetLogOutputSetLogOutput设置日志输出 WriterSetStdoutOutput直接操作 outHandle设置终端输出 WriterSetPrefixSetPrefix设置行前缀空则不显示SetFlagsSetFlags设置标准库 log flagSetLogListeners直接操作 logListeners注册日志监听器LogThreshold/GetLogThresholdGetLogThreshold查询当前日志阈值StdoutThreshold/GetStdoutThresholdGetStdoutThreshold查询当前输出阈值在当前仓库中的定位在本仓库中github.com/spf13/jwalterweatherman v1.1.0以间接依赖// indirect的形式出现在 go.mod通过github.com/spf13/vipergo.mod被引入源码被 vendor 到 vendor/github.com/spf13/jwalterweatherman/。也就是说inngest 项目的代码并未直接调用 JWW 的 API但它作为 viper 配置加载链路的底层依赖参与构建如果你在自己的 Go 项目中直接引入 viper同样会连带获得 JWW 的能力。若想独立使用可按上述示例直接import jww github.com/spf13/jwalterweatherman并运行go mod tidy。小结JWW 的价值在于以极小的心智负担统一了终端用户反馈与结构化日志归档两个需求7 级分级覆盖从 TRACE 到 FATAL 的全部场景双阈值stdout 阈值 日志阈值让 verbose 模式只需两行代码io.Writer抽象让日志可以落到文件、缓冲、网络等任意目的地Notepad实例与LogListener/Counter则分别满足了多实例隔离与测试断言等进阶需求。作为一个early release的轻量库其 API 在设计上追求直接、零样板——这正是它作为 Hugo 与 viper 生态基础组件长期被广泛使用的根本原因。【免费下载链接】inngestThe leading workflow orchestration platform. Run stateful step functions and AI workflows on serverless, servers, or the edge.项目地址: https://gitcode.com/GitHub_Trending/in/inngest创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表