垂直打印日志,不同于 Java 配置某个类日志级别这种水平切面的日志组件,g 的日志级别是跟着上下文的,根据规则从入口到出口打印一次请求内所产生的日志。
例如,在 level=info 的生产环境中,把 ID=123 的用户行为日志 level 设置成 debug,从 HTTP Request 到 Service 再到 DAO,所有 debug 日志全部打出来,以便调试或分析用户行为。
*** 实现方式
在入口处,根据规则,把 日志级别 设置在 Context 中,打印日志时,优先判断 Context 中的日志级别,决定是否输出该条日志。
go get github.com/omigo/g要求 Go 1.18 及以上。
package main
import (
"context"
"github.com/omigo/g"
)
func main() {
ctx := g.WithTraceId(context.Background()) // 生成 traceId 并放进 ctx
g.Info(ctx, "服务启动", 8080) // 普通日志
g.Debugf(ctx, "调试信息 %d", 42)
doWork(ctx)
}
func doWork(ctx context.Context) {
defer g.Cost(ctx, "doWork")() // 打印 start 与 cost
// 只对这一次请求打开 DEBUG
ctx = g.WithLevel(ctx, g.Ldebug)
g.Debug(ctx, "这条日志在全局 INFO 下也会输出")
}| 常量 | 值 | 说明 |
|---|---|---|
| Lstack | 0 | 打印堆栈 |
| Lfatal | 1 | 致命错误 |
| Lerror | 2 | 错误 |
| Lwarn | 3 | 警告 |
| Linfo | 4 | 常规信息 |
| Ldebug | 5 | 调试 |
| Ltrace | 6 | 最详细 |
注意:数值越大表示越详细,与 zap、slog 等库的方向相反。比较级别时不要想当然。
默认级别是 INFO。用法:
g.SetLevel(g.Ldebug) // 代码设置
g.SetLevelString("debug") // 字符串设置,大小写不敏感,自动去首尾空白非法级别不会生效,会保持原值并向 stderr 输出一条警告。
WithLevel 把级别写进 context,优先级高于全局级别;ctx 中的级别只影响该请求链路:
ctx = g.WithLevel(ctx, g.Ldebug)ctx := g.WithTraceId(ctx) // 没有则生成,已有则沿用
ctx = g.SetTraceId(ctx, "request-123") // 使用调用方生成的 IDWithTraceId 的 traceId 起点使用随机数,进程内单调递增。跨进程唯一性由入口(网关或调用方)通过 SetTraceId 保证。
defer g.Cost(ctx, "method", "arg")()
defer g.Costf(ctx, "method%d", 2)()Cost 与 Costf 目前不受级别过滤控制,无论级别如何都会输出,这一点与普通日志不同,后续版本会修正。
默认由环境变量决定:优先读 G_LOG_FORMAT,没有设置时兼容旧的 ENVIRONMENT。值为 dev、develop、development 时使用开发格式,prod、production 或未设置时使用生产格式。也可以在运行时切换:
g.SetFormat(g.FormatDevelop)
g.SetFormat(g.FormatProduction)生产格式示例:
[2026-11-08 16:21:50.050] [g/example/main.go:main:22] [INFO] [14077769] 消息
开发格式示例:
2026-11-08 16:21:50.050 14077769 INFO g/example/main.go:22 main 消息
type MyLogger struct {
g.HertzFullLogger
}
func (l MyLogger) SetLevel(level hlog.Level) {
l.HertzFullLogger.SetLevel(int(level))
}
func init() {
hlog.SetLogger(&MyLogger{})
}说明:
- 不引入 hertz 依赖,所以
HertzFullLogger.SetLevel接收 int,直接注册需要上面的薄包装。 Trace、Debug等不带 ctx 的方法无法带 traceId,日志中显示为-;需要链路 traceId 时请使用CtxXxxf系列。HertzFullLogger的调用点会自动跳过库内帧与 hlog 适配层,hlog.Info包级函数和hlog.DefaultLogger().Info()两种调用方式都能定位到用户代码。- 本包没有 NOTICE 档,hlog 的 notice 归入 warn,以保证 hlog 级别设为 Notice 时 Info 日志被正确过滤;副作用是 Warn 阈值下 Notice 仍会输出。
HertzFullLogger.SetLevel直接修改本包默认 Logger,hlog.SetLevel与g.SetLevel会互相影响,接入后请只在一处设置级别。- hlog 默认级别是 Trace,接入后会受本包默认 INFO 级别过滤,需要调试日志请显式调低级别。
- 默认级别是 INFO。
Fatal一定输出,不受级别过滤;默认在输出后执行os.Exit(99)。- 不希望库退出进程时,可以替换退出行为:
g.SetExit(nil) // 只打印,不退出
g.SetExit(func(code int) {}) // 拦截退出码,自行处理Stack只打印当前 goroutine 的调用栈,并按实际长度写入,不会带出缓冲区的零字节。- 传入 nil context 会按
context.Background()处理,不会 panic。 - 非法级别不会导致 panic,
Level.String()返回 LEVEL(n)。 - 输出目标传 nil 时日志会被丢弃,不会崩溃。
- typed nil(nil 指针、nil map、nil slice 等)统一输出 nil;
SetTraceId(ctx, nil)把 traceId 记为-。 - 自定义 Writer 不要在 Write 内部回调同一个 Logger 打日志(会自死锁);读取
GetLevel等状态方法是安全的。
Logger.Level、Logger.Buffer字段已私有化,改用GetLevel、SetLevel、SetOutput。- 默认级别从 DEBUG 改为 INFO,生产环境需要 DEBUG 时请显式设置。
Fatal默认在输出后执行os.Exit(99);需要拦截时使用SetExit。Stack不再打印所有 goroutine,只打印当前 goroutine。- hertz 级别映射修正为逐项对应:之前 debug、info 两档存在错位,notice 现在归入 warn。
- 输出格式可以通过
SetFormat在运行时切换;环境变量优先读G_LOG_FORMAT。 - 日志中的级别名固定为大写(STACK、FATAL、ERROR、WARN、INFO、DEBUG、TRACE),
SetLevelString输入大小写不敏感。 - typed nil 统一输出 nil(旧版本输出 null 或 )。
- 非法级别、非法格式串等配置错误会输出 stderr 警告,不再静默。