beego日志必须用logs.setlogger配置文件输出,因其不走os.stdout等标准流;需在首次logs调用前执行,支持adapterfile、adaptermultifile及自定义writer。

Beego 默认把日志写进控制台,但生产环境必须落盘;直接改 log.Output 或用 freopen 会失效——因为 Beego 自带日志模块完全绕过 std::cout 和 os.Stdout,它用的是独立的 logs.BeeLogger 实例。
Beego 的日志输出不走标准流,os.Stdout 重定向无效
很多人试过在 main() 开头调用 os.Stdout = file 或 syscall.Dup2,结果日志还是打在终端。这是因为 Beego 的 logs 包默认使用自己封装的 console 类型 writer,底层调用 fmt.Fprintln(os.Stderr, ...),但其行为由 logs.SetLogger 控制,和 Go 标准库的 os.Stdout 完全解耦。
- Beego v2.x 中,所有
logs.Info()、logs.Error()调用都经由logs.GetBeeLogger()分发,不经过os.Stdout -
fmt.Println()这类标准输出仍可被重定向,但 Beego 日志不会跟着变 - 试图用
log.SetOutput(file)(标准库log)也无效,因为 Beego 不用那个实例
正确做法:用 logs.SetLogger 切换为文件驱动
Beego 提供了开箱即用的文件日志后端,只需一行配置就能把全部日志写入文件,且支持轮转、异步写入和分级过滤。
- 在
main()初始化 Beego 前调用:logs.SetLogger(logs.AdapterFile, `{"filename":"logs/app.log"}`) - JSON 配置中可加
"level": 7(Debug 级)、"maxlines": 1000000、"daily": true等参数 - 路径支持相对或绝对:
"filename": "./logs/app.log"或"filename": "/var/log/myapp/app.log" - 若需同时输出到控制台和文件,用逗号分隔多个 adapter:
logs.SetLogger(logs.AdapterMultiFile, `{"console":true,"file":"logs/app.log"}`)
常见错误:没初始化日志就调用 logs.Xxx()
Beego 日志是懒初始化的,第一次调用 logs.Info() 才触发默认 console logger 创建。如果在此之前调用 SetLogger,会被忽略;如果之后才调,又可能漏掉启动阶段的关键日志(如路由注册、配置加载)。
- 务必在
beego.Run()之前、且在任何logs.Xxx()调用之前执行SetLogger - 推荐位置:在
init()函数里,或main()最开头(早于beego.Router或beego.LoadAppConfig) - 验证是否生效:检查
app.log文件是否生成,以及内容里是否有[INFO]开头的启动日志行
高级控制:自定义 Writer 实现双写或过滤
如果内置 adapter 不够用(比如要加 trace_id、过滤特定模块、或写入网络日志服务),可以传入自定义 io.Writer。
- 实现一个结构体,满足
Write([]byte) (int, error)方法,并内部持有一个*os.File和sync.Mutex - 构造时打开文件并设置
os.O_CREATE | os.O_WRONLY | os.O_APPEND - 在
Write方法里加锁、预处理(如注入时间戳)、再调用file.Write - 传给
SetLogger:logs.SetLogger(logs.AdapterCustom, `{"writer":{}}`),然后用logs.SetLevel()控制级别 - 注意:不要在
Write里调用logs.Xxx(),否则递归死锁
真正容易被忽略的是日志初始化时机和 Beego 对标准流的“无视”——它不是没重定向成功,而是压根没走那条路。所有调试都应该从 logs.GetBeeLogger().GetAdapter() 返回值开始查起,而不是盯着 os.Stdout。











