链路追踪与日志关联
微服务排查慢请求时,最缺的就是“这次请求到底经过了哪些服务、每步花了多久”。GoFrame v2 的链路追踪基于 OpenTelemetry 标准实现,官方文档“服务链路跟踪”系列章节给出了完整实践:服务间由拦截器自动创建 span,进程内方法间由开发者手动埋点。
基本概念
- Span:一次调用的一段记录(名称、起止时间、属性);首个 Span 为 Root Span。
- 分布式请求场景下,客户端/服务端拦截器会自动创建 Root Span 并沿网络传递,开发者无需手工创建。
- 官方提供社区模块封装了上报器初始化:
contrib/trace/otlphttp、contrib/trace/otlpgrpc(Jaeger 封装官方提示即将于 v2.6 移除,建议改用 OTLP 上报),以及框架内置的gtrace工具。 - HTTP 服务开启追踪后,框架会自动为每个请求生成 span(路由表里可见
internalMiddlewareServerTracing这类内置中间件)。
初始化并启动上报
package main
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/contrib/trace/otlphttp/v2" // 官方 OTLP HTTP 封装
)
func main() {
var (
ctx = gctx.New()
serviceName = "user-service"
endpoint = "http://127.0.0.1:4318" // OTLP HTTP 采集端
)
shutdown, err := otlphttp.Init(serviceName, endpoint, "")
if err != nil {
g.Log().Fatal(ctx, err)
}
defer shutdown() // 进程退出前刷新并关闭上报器
s := g.Server()
s.Group("/api", func(group *ghttp.RouterGroup) {
group.Bind(new(v1.Controller))
})
s.Run()
}
官方对具体初始化函数签名(otlphttp.Init(serviceName, endpoint, path))与不同版本可能略有出入,接入时以对应版本官方文档为准。
方法间手动埋点
进程内方法调用不会自动产生 span,官方示范的固定写法是用 gtrace.NewSpan 创建并 defer span.End():
import "github.com/gogf/gf/v2/net/gtrace"
// 方法入口:创建子 Span,名称为方法名,便于在链路中识别
func GetUser(ctx context.Context, id int) (m g.Map, err error) {
ctx, span := gtrace.NewSpan(ctx, "GetUser")
defer span.End()
info, err := GetInfo(ctx, id) // ctx 携带 span 一路传递
if err != nil {
return nil, err
}
return info, nil
}
关键点只有一个:把 ctx 作为第一个参数层层传递,span 的父子关系与上下文传播就自然成立。ORM/Redis/日志等组件在拿到带 span 的 ctx 后会自动把自己的耗时与信息挂到当前链路。
日志关联
GoFrame 的日志组件会自动从 ctx 中识别链路信息:带上 trace ctx 打印日志时,输出会附带链路标识(日志中形如 {189cwi9ngk...} 的 TraceId 片段),这样在日志系统里按 TraceId 搜索,就能捞出同一请求的全部日志:
g.Log().Infof(ctx, "get user: %d", id) // ctx 来自请求或上层方法
排查时先到追踪系统(如 Jaeger/云厂商 APM)按 TraceId 看调用链耗时,再回日志系统用同一个 TraceId 捞上下文,效率远高于大海捞针。
注意点
ctx必须贯穿始终:中间件、service、dao 一路传ctx,链路才不断。- 上报器是全局资源,
defer shutdown()保证退出时数据落盘,配合进程优雅退出使用。 - 采样策略(如只采 10%)在采集端或初始化参数中配置,避免全量采样成本过高(具体能力以官方文档为准)。
- 本地没有采集后端时链路功能不报错,只是没有数据可看;可用官方文档推荐的 Jaeger all-in-one 容器快速起一个本地看板。
小结
链路追踪的接入成本其实很低:启动时初始化 OTLP 上报器,代码里保证 ctx 贯穿 + 关键方法用 gtrace.NewSpan 埋点,服务间的传播由框架拦截器完成。配上 TraceId 日志,分布式排障从“猜”变成“看”。组件支持范围以官方文档 goframe.org 为准。