Beego应用启用OpenTelemetry指标采集需手动注册MeterProvider并在控制器、ORM等关键路径埋点,v2.x版本才兼容;必须显式初始化并注入resource属性(如service.name),避免init中初始化或使用context.Background(),Prometheus导出需单独挂载/metrics且启用gzip。

Beego 应用如何启用 OpenTelemetry 指标采集
Beego 本身不内置 OpenTelemetry 支持,必须通过手动注册 otel.GetMeterProvider() 并在关键路径(如控制器、中间件、数据库操作)中调用指标 API 才能采集指标。自动埋点仅覆盖 HTTP 请求层(需配合 OpenTelemetry.Instrumentation.Http),但 Beego 的路由分发、Handler 执行、ORM 操作等均需自行埋点。
核心前提是:Beego v2.x(基于 Go modules)才能与 OpenTelemetry Go SDK 兼容;v1.x 因依赖旧版 github.com/astaxie/beego 且无 context 透传机制,基本无法可靠集成。
- 确认 Beego 版本:
go list -m github.com/beego/beego/v2,非 v2 不建议继续 - 安装 OpenTelemetry Go SDK:
go get go.opentelemetry.io/otel@v1.28.0(2026 年稳定版) - 必须显式初始化 MeterProvider(不能依赖全局默认):
otel.SetMeterProvider(meterProvider) - 避免在
func init()中初始化 OTel——Beego 的启动顺序可能导致app.Run()前 meter 尚未就绪
在 Beego Controller 中记录自定义指标的正确姿势
Beego 的 Controller 是指标埋点最常用位置,但直接在 Get()、Post() 方法里写指标逻辑容易污染业务代码,也难复用。推荐封装一个带 context.Context 的指标工具函数,并确保每次请求都携带 trace context。
示例:记录每个 API 的响应状态码分布和延迟
// metrics.go
var (
httpRequests = otel.Meter("beego.http").Int64Counter(
"http.server.requests",
instrument.WithDescription("Total HTTP requests by status and path"),
)
httpLatency = otel.Meter("beego.http").Float64Histogram(
"http.server.duration",
instrument.WithDescription("HTTP request duration in seconds"),
instrument.WithUnit("s"),
)
)
func RecordHTTPMetrics(ctx context.Context, c *beego.Controller, statusCode int, durationSec float64) {
attrs := []attribute.KeyValue{
attribute.String("http.method", c.Ctx.Input.Method()),
attribute.String("http.route", c.Ctx.Input.URL()),
attribute.Int("http.status_code", statusCode),
}
httpRequests.Add(ctx, 1, attrs...)
httpLatency.Record(ctx, durationSec, attrs...)
}
- 务必传入
c.Ctx.Request.Context()而非context.Background(),否则指标将丢失 trace 关联 - 不要在
Finish()或Prepare()中直接调用指标——Finish()可能被多次触发;应在Get()/Post()结束前或自定义defer中调用 - 避免在
Render()后记录状态码——此时c.Ctx.ResponseWriter.Status可能尚未写入,应改用c.Data["json"]或显式c.Abort()判断
Beego ORM 操作如何打点并关联 trace
Beego 默认 ORM(orm.RegisterDriver("mysql", orm.DRMySQL))不支持 OpenTelemetry 自动插桩。若想监控 SQL 执行耗时、错误率、慢查询,必须包装 orm.QueryTable() 或 o.Read() 等方法,并注入 span context。
关键难点在于:Beego ORM 不暴露底层 *sql.DB 或 context.Context 参数,所以不能直接套用 otel/sql 的 wrapper。可行方案是拦截 orm.Debug 日志 + 时间戳,或改用原生 database/sql 配合 go.opentelemetry.io/contrib/instrumentation/database/sql。
- 若坚持用 Beego ORM,可重写
orm.QuerySeter接口实现,在One()/All()前后手动 start/end span,并用span.SetAttributes()记录 SQL 模板和参数长度 - 更推荐方案:弃用
beego/orm,改用ent或gorm(二者均有成熟 OTel 插件) - 切勿在
orm.NewOrm()中创建新otel.Tracer实例——会导致 span 上下文断裂;应从请求 context 中提取 parent span
为什么 Beego + OTel 指标导出到 Prometheus 容易失败
常见现象是 Prometheus 抓不到指标,或 otel-collector 日志报 rpc error: code = Unavailable desc = transport is closing。根本原因不是配置错,而是 Beego 启动模型与 OTel Exporter 生命周期不匹配。
Beego app.Run() 是阻塞调用,而 OTel 的 PeriodicReader(用于 Prometheus exporter)需要后台 goroutine 持续运行并定期 flush。若未在 app.Run() 前启动 reader,或未用 runtime.GC() 触发 finalizer 清理,exporter 会静默失效。
- 必须在
beego.BeeApp.Run()之前初始化controller.NewController()和metric.NewPeriodicReader() - Prometheus exporter endpoint(如
/metrics)不能由 Beego 自动注册——需单独起 http.Server 或用promhttp.Handler()挂载到 Beego 的beego.Router() - 检查
otel-collector的 receivers 是否启用了prometheusremotewrite,而非仅otlp;Beego 指标若走 OTLP 推送,collector 需配exporters: prometheusremotewrite并转发至 Prometheus 的/api/v1/write - Beego 默认禁用
gzip响应压缩,而 Prometheus client 期望Content-Encoding: gzip,导致 scrape 失败;需在 Beego 的app.Config中设EnableGzip = true
resource.WithAttributes() 注入 service.name 和 telemetry.sdk.language。


















