beego logs 不支持按 level、module 等字段多通道分流,仅支持多引擎并行写入;如需分流,须自行封装 multilogger 或改用 opentelemetry。

Beego 本身不支持日志“多通道分流至不同后端”——它的 logs 模块只允许同时注册多个 logger 实例(如 console + file + conn),但所有实例共享同一组日志级别和写入内容,无法按字段(如 level、module、trace_id)做条件路由。真要实现分流,得绕开 Beego 日志模块的默认行为,自己组装逻辑。
Beego logs 多实例 ≠ 多通道分流
很多人误以为调用多次 log.SetLogger() 就能实现“按 error 写 ES、debug 写文件、info 推 Loki”,其实不是:
-
log.SetLogger("file", ...)和log.SetLogger("conn", ...)是并行写入,不是条件写入 - 所有
log.Error()、log.Info()都会发给每个已注册的 logger,无法拦截或过滤 - 没有内置的 processor 或 hook 机制来 inspect 日志 entry 的
map[string]interface{}字段 - 异步模式(
log.Async())下更难控制分发时机,容易丢字段或错乱
手动封装 multi-backend logger 的最小可行方式
如果你必须用 Beego 的 logs 做基础,又需要分流,只能自己包一层:用一个统一入口接收日志,再根据 level 或自定义 context 字段决定投递目标。关键点:
- 禁用 Beego 默认的全局 logger:
logs.Reset(),避免污染 - 为每个后端创建独立
logs.BeeLogger实例(如esLogger、lokiLogger),各自配置SetLogger - 写一个
MultiLogger结构体,暴露Info()、Error()等方法,内部按规则分发 - 若需字段级路由(比如含
"service":"auth"的日志才推 Loki),必须在调用时显式传入map[string]interface{},并在MultiLogger中解析
示例片段:
type MultiLogger struct {
esLogger *logs.BeeLogger
lokiLogger *logs.BeeLogger
}
func (m *MultiLogger) Error(msg string, v ...interface{}) {
m.esLogger.Error(msg, v...)
// 只有含 trace_id 的 error 才推 Loki
if hasTraceID(v) {
m.lokiLogger.Error("[loki]", msg, v...)
}
}
更推荐的替代路径:跳过 Beego logs,直连 OpenTelemetry
Beego 日志模块设计初衷是开发期调试输出,不是生产级日志管道。一旦涉及 Loki/ES/ClickHouse 多后端、字段增强、重试、背压,硬改 Beego 日志只会越陷越深。正确做法是:
- 用
otelcol作为日志接收与分发中枢,Beego 应用只负责用 OTLP 协议上报(例如用go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc+logsexporter) - 在 otelcol 配置里启用
exporter.loki和exporter.elasticsearch,并通过processor.attributes插入trace_id、service.name等顶层字段 - Beego 中不再调用
log.Info(),而是用log.Record()构造结构化日志,通过 OTLP client 异步发送 - 这样既保留 Beego 的 MVC 控制流,又把日志路由、失败恢复、协议适配交给专业组件
真正卡住的不是代码怎么写,而是要不要承认 Beego logs 的定位边界:它适合快速启动和本地调试,不适合承担生产环境日志编排的职责。分流逻辑一旦开始写 if-else 判断 level 或 module,就该停下来评估是否已在重复造轮子。











