必须用 apmgodriver 替换原生 database/sql 驱动,因为 Elastic APM Go Agent 默认不拦截标准库调用,仅手动埋点无法捕获 SQL 执行时长、参数和错误详情;不导入 _ "go.elastic.co/apm/module/apmgodriver" 将导致 APM UI 中仅显示无上下文的空白 span。

Go 服务接入 Elastic APM 后,性能瓶颈不会自动暴露——你需要正确配置采集粒度、启用关键插件,并确认 span 上报链路完整,否则看到的只是断开的“孤点”。
为什么 apmgodriver 必须替换原生 database/sql 驱动
Elastic APM 的 Go Agent 默认不拦截标准库 database/sql 调用。若只调用 apm.DefaultTracer().StartSpan 手动埋点,SQL 查询会丢失执行时长、参数和错误详情。
- 必须用
apmgodriver替换导入路径:import _ "go.elastic.co/apm/module/apmgodriver" - 初始化 DB 时仍用
sql.Open("postgres", dsn),但底层驱动已被劫持 - 若忘记导入该匿名包,APM UI 中只会显示
db.query的空白 span,无db.statement、db.type等上下文
HTTP handler 埋点失效的三个常见原因
直接 wrap http.HandleFunc 或用 apmhttp.Wrap 后仍看不到请求 span?大概率是 tracer 初始化时机或 context 传递出了问题。
-
apm.DefaultTracer()必须在main()开头调用,且apmhttp.Wrap要包裹最终 handler(不是中间件链里的某一层) - 若使用 Gin/echo 等框架,不能只 wrap
http.ServeMux;需用对应插件如apmgin,否则 request body 解析、panic 捕获等逻辑会绕过 tracer - 手动创建的 goroutine 中发起 HTTP 调用(如异步发通知),必须显式传递带 span 的
context.Context,否则子请求无 parent span
apm.StartTransaction 和 apm.StartSpan 的适用边界
事务(Transaction)代表一次外部请求入口,span 是其内部操作单元。混用会导致嵌套错乱或 span 丢失。
- HTTP 入口、gRPC 方法、消息队列消费函数 —— 用
apm.StartTransaction - DB 查询、HTTP 客户端调用、缓存读写、CPU 密集计算 —— 用
apm.StartSpan,且必须传入当前 transaction 的 context - 不要在 defer 中调用
span.End()后再修改 span 的Outcome或Result,这些字段只在End()时序列化上报
最易被忽略的是采样率与日志耦合:默认 SampleRate=1.0 会全量上报,但高并发下可能压垮 APM Server;设为 0.1 后,若没同时开启 apm.SetLogger,失败的采样决策(如被丢弃的慢查询)将完全不可见。



















