sw.init()必须在main()第一行调用,且gorm.open()、preparestmt启用、go2sky插件包装driver、span操作在end前完成、中间件顺序正确——否则db span静默消失。

sw.Init() 必须在 GORM Open 之前调用
Go 没有 Java Agent 那种字节码注入能力,sw.Init() 是整个链路追踪的起点。如果先 gorm.Open() 再初始化探针,后续所有 SQL 操作都不会生成 DB 类型的 Span——OAP 里完全看不到数据库节点。
常见错误是把 sw.Init() 放在配置加载之后、DB 初始化之前,但漏掉了 flag.Parse() 或环境变量读取时机,导致实际执行顺序错乱。
-
sw.Init()必须是main()函数第一行,参数至少含service.name和backend.grpc.host - 不要在
init()函数里调用sw.Init()——此时flag.Parse()可能还没运行,配置项读不到 - 确认
gorm.Open()在sw.Init()之后,且不是在 goroutine 中异步执行
GORM 必须启用 PrepareStmt 并使用 go2sky 插件包装 driver
原生 gorm.Open() 不会触发 SkyWalking 的 SQL 埋点。即使探针已启动,GORM 默认走的是未插桩的 database/sql 底层,不会自动上报 SQL 执行耗时、参数、错误等信息。
官方推荐路径是:用 github.com/SkyAPM/go2sky-plugins/sql 替换标准 driver,并显式开启 PrepareStmt: true ——否则预编译语句不生效,插件无法捕获完整 SQL 上下文。
- 替换
sql.Open()为plugin.Open("mysql", dsn)(或postgres等) - GORM 连接时传入该插件返回的
*sql.DB,而非原始 DSN 字符串 - 必须设置
&gorm.Config{PrepareStmt: true},否则插件拿不到完整 SQL 文本 - 避免复用全局
*gorm.DB实例跨 service ——不同 service 应该用不同service.name初始化探针
Span Tag 和 Error 信息要在事务结束前写入
GORM 的事务生命周期(Begin → Commit/Rollback)和 SkyWalking 的 Span 生命周期不自动对齐。如果你在 tx.Commit() 之后才调用 span.Tag() 或 span.Error(),这些元数据根本不会上报。
更隐蔽的问题是:GORM 的 First()、Create() 等方法内部可能提前结束 Span(尤其带 context 超时的场景),导致你手动追加的 Tag 丢失。
- 所有
span.Tag()、span.Log()、span.Error()必须在span.End()前完成 - 若需标记慢查询,建议在 GORM
Callback中拦截process阶段,用sw.ContextCarrier提取当前 Span - 不要依赖
gorm.DB.Debug()日志来验证埋点 —— 它和 SkyWalking 的 Span 上报是两套机制
HTTP 请求链路里查不到 DB Span?检查中间件顺序和 req.Body 读取
即便 GORM 埋点配置正确,你在 SkyWalking UI 里看到的 Trace 仍可能只有 HTTP Span、没有 DB 子 Span。根本原因往往是 HTTP handler 中间件顺序错误,或提前读取了 req.Body 导致 sw.HttpServerInterceptor 失效。
例如 Gin 用户用了 gin.Recovery() 或自定义日志中间件,且它们在 sw.GinMiddleware() 之前执行并调用了 c.Request.Body.Read(),后续插件就无法解析请求体上下文,整个链路从 HTTP 层就断开了。
-
sw.GinMiddleware("your-service-name")必须是第一个注册的中间件 - 禁止在任何中间件中调用
c.Request.Body.Read()或c.ShouldBind()—— 让sw.GinMiddleware自己处理 - 验证方法:打印
c.Request.Header.Get("sw8"),再查对应 Trace 的 Span 列表里是否有component=MySQL或component=PostgreSQL的条目











