logrus.setlevel()必须在首次日志输出前调用,否则因lazy初始化而不生效;自定义logger需调用实例的setlevel();json输出需显式设置jsonformatter且setoutput要在setformatter之前。

logrus 不是开箱即用的库,不显式配置 SetLevel 和 SetOutput,90% 的情况会静默丢日志——不是没打,是你根本看不到。
logrus.SetLevel() 为什么总不生效?
根本原因:level 是 lazy 初始化的,首次调用 logrus.Info() 等函数时才真正绑定当前 level;之后再调用 SetLevel() 对已初始化的 handler 无效。
- 必须在任何
logrus.Xxx()(包括Info、Debug、WithFields)之前调用SetLevel() - 如果你用了自定义实例
l := logrus.New(),得对l.SetLevel(),而不是logrus.SetLevel() - 验证是否生效:设置后立刻执行
logrus.Debug("test"),看终端或文件里有没有输出 - 默认级别是
InfoLevel,所以Debug日志不出现,大概率是 level 设置晚了,或压根没设
logrus.WithFields() 字段不显示在 JSON 输出里?
logrus 默认用 TextFormatter,WithFields() 只参与字符串拼接,不会变成 JSON key-value。字段“存在”,但格式器不认它。
- 必须显式调用
logrus.SetFormatter(&logrus.JSONFormatter{})才能结构化输出 - 注意顺序:
SetOutput()必须在SetFormatter()之前,否则 v1.9+ 可能 panic 或空输出 - 字段值类型非法(如
map[interface{}]interface{}、chan、含循环引用的 struct)会被静默跳过 - 字段名别用点号(
"user.id")或空格,Loki/Fluent Bit 解析时会截断
写文件报 write app.log: bad file descriptor?
这是最典型的生命周期错误:文件句柄在日志第一次写入前就被关掉了。
- 绝对不要在
init()函数里用defer f.Close()——init返回即关闭,后续所有日志都失败 - 正确做法:把
os.OpenFile()和defer f.Close()放进main(),确保句柄存活到程序退出 - 打开模式用
os.O_CREATE | os.O_APPEND | os.O_WRONLY,权限用0644(非0666) - 如果要用轮转,别自己实现,直接上
gopkg.in/natefinch/lumberjack.v2封装io.Writer
logrus.Fatal() 后 defer 不执行,是 bug 吗?
不是 bug,是设计行为。logrus.Fatal() 底层调用 os.Exit(1),进程立即终止,绕过所有 defer、panic 恢复和 runtime cleanup。
- 它和
panic()+recover()完全不同,不可替代 - 若需清理资源(如关闭连接、释放锁),不能依赖
defer,得在Fatal()前手动做 - 高频路径慎用
Fatal(),尤其在 HTTP handler 中——它会让整个 goroutine 瞬间消失,可能掩盖更深层问题
最易被忽略的点:JSON 格式下时间戳默认是浮点秒("ts": 1747941498.123),而 ELK/Loki 等平台普遍要求 ISO8601 字符串格式;不显式配 TimestampFormat 和 TimeKey,日志就进不了采集管道。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











