根本原因是skywalking-go探针必须在main函数首行初始化,晚于HTTP server或goroutine启动会导致Span无法注入上下文;需首行调用sw.Init,用HttpServerInterceptor包裹handler,显式传递context跨goroutine,数据库操作须用官方插件包装。

Go服务启动时没上报Span到SkyWalking OAP
根本原因是 skywalking-go 的探针必须在 main 函数最开头完成初始化,且不能晚于任何 HTTP server 或 goroutine 启动。一旦监听端口或启动协程后才调用 sw.Init,后续的 Span 就不会被自动注入上下文,OAP 收不到数据。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 在
main()第一行就调用sw.Init,传入正确配置(如service.name、backend.grpc.host) - 避免在
init()函数中初始化探针——此时flag.Parse()可能未执行,导致配置读取失败 - 确认 OAP 地址可连通:
telnet your-oap-host 11800(gRPC 默认端口),不通则检查网络策略或 SkyWalking 版本兼容性(v9+ OAP 要求 agent v4+) - 启用 debug 日志:设置环境变量
SW_AGENT_LOG_LEVEL=DEBUG,查看是否输出"reporter: send span to backend"
HTTP Handler 中 Span 不继承父链路(TraceID 断掉)
Go 标准库 http.ServeMux 和大多数框架(如 Gin、Echo)默认不解析 sw8 或 traceparent 头,导致进来的请求无法还原上下文,新 Span 的 parentSegmentId 为空,链路断裂。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 使用
sw.HttpServerInterceptor包裹 handler:对 net/http,直接http.Handle("/", sw.HttpServerInterceptor(yourHandler)) - Gin 用户需注册中间件:
router.Use(sw.GinMiddleware("your-service-name"));注意该中间件必须放在所有其他中间件之前 - 若用自定义路由或 fasthttp,必须手动从请求头提取
sw8并调用sw.ContinuedFromHeader构建 context - 验证是否生效:打印请求中的
sw8头值,并检查生成的 Span 是否有parentSegmentId字段
数据库操作没出现在 SkyWalking 链路图里
skywalking-go 不自动拦截 database/sql,即使用了 sql.Open,也不会产生 DB 类型的 Span。必须显式包装 driver 或使用支持插件的封装库。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 优先使用官方维护的插件:
github.com/SkyAPM/go2sky-plugins/sql,替换sql.Open为plugin.Open - 确保
plugin.Open在探针初始化之后调用,否则会 panic 报错"agent not started" - 如果用 GORM,需开启
PrepareStmt: true,否则预处理语句可能被合并,丢失 SQL 细节;同时配合plugin.WrapGorm注册回调 - 注意 MySQL 连接参数中不要带
parseTime=true,它与插件内部 time 解析冲突,会导致 Span 上报失败
多协程/异步任务中 Span 丢失或父子关系错乱
Go 的 goroutine 不继承父 context,sw.Context 也不会自动传播。直接在新 goroutine 里调用 sw.CreateLocalSpan,会产生孤立 Span,和上游无关联。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 跨 goroutine 必须显式传递 context:
go doWork(sw.ContextWithSpan(ctx, span)) - 若使用
sw.CreateEntrySpan或CreateExitSpan,需先用sw.GetContext提取当前 Span,再通过sw.ContinueFromContext注入新 goroutine 的 context - 慎用
context.Background()启动子任务——它彻底切断链路;改用sw.ContextWithSpan(ctx, parentSpan) - 第三方库(如
workerpool、ants)需在任务函数入口手动恢复 Span,不能依赖框架自动注入
最易被忽略的是异步场景下的 context 传递时机:Span 对象本身不是线程安全的,必须在 goroutine 启动前完成 sw.ContextWithSpan 封装,而不是在 goroutine 内部临时创建或复用外部变量。


















