logrus.SetLevel()需在首次日志输出前调用,否则不生效;WithFields()字段需配合JSONFormatter才结构化输出;Fatal()调用os.Exit()导致defer不执行;logrus本身并发安全,但自定义handler需自行加锁。

logrus.SetLevel() 不生效?检查日志输出前是否已初始化
logrus 默认级别是 InfoLevel,但如果你调用 logrus.SetLevel(logrus.DebugLevel) 却看不到 Debug 日志,大概率是因为你在日志第一次输出(比如 logrus.Info())之后才设置的级别。logrus 的 level 是 lazy 初始化的——首次调用日志函数时会根据当前 level 决定是否输出,之后再改 level 对已缓存的 handler 无影响。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 确保
logrus.SetLevel()在任何logrus.Xxx()调用之前执行 - 如果用了自定义
logrus.Logger实例(非默认全局实例),要对那个实例调用SetLevel(),而不是全局的logrus - 验证方式:在设置后立刻打一行
logrus.Debug("test"),看是否输出
为什么 logrus.WithFields() 的字段不显示在 JSON 输出里?
logrus 默认输出是文本格式,即使你用了 logrus.WithFields(),字段也只参与拼接字符串,不会自动转成结构化字段。要让字段真正变成 JSON 的 key-value,必须显式设置输出格式为 logrus.JSONFormatter。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
logrus.SetFormatter(&logrus.JSONFormatter{})切换格式 - 注意:
JSONFormatter默认会加时间戳、level、msg 字段;自定义字段会平级写入,不嵌套在fields下 - 如果想控制时间格式或去掉某些字段,可传
&logrus.JSONFormatter{TimestampFormat: "2006-01-02T15:04:05Z07:00"}
logrus.Fatal() 之后 defer 不执行?这是设计行为,不是 bug
logrus.Fatal() 底层调用的是 os.Exit(1),它会立即终止进程,绕过所有 defer、panic 恢复和 runtime cleanup。这和 panic() + recover() 完全不同。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 不要指望
logrus.Fatal()前注册的defer能执行,比如关文件、释放锁、上报指标——它们都会被跳过 - 若需清理,改用
logrus.Error()+ 显式os.Exit(1),把清理逻辑放在 exit 前 - 或者封装一个
SafeFatal()函数,统一处理清理再 exit
多 goroutine 写日志崩溃?logrus 本身是并发安全的,但 handler 可能不是
logrus 的 Logger 实例本身支持并发调用(内部用 mutex 保护状态),但它的 handler(比如自定义的文件写入、网络发送)如果不加锁,就可能在多 goroutine 同时写时出错,典型现象是 panic 报 concurrent write to fd 或日志内容错乱。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 使用官方
logrus.WriterHook或第三方成熟 handler(如rotatelogs)通常已处理并发 - 自己实现
logrus.Hook时,务必在Fired()方法内加锁,或用带缓冲的 channel 异步写入 - 避免直接用
os.File.Write(),改用bufio.NewWriter+Flush()并配锁


















