package jaeger import ( "context" "encoding/json" "strconv" "strings" "sync" "github.com/gogf/gf/contrib/trace/otlphttp/v2" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/net/gtrace" "go.opentelemetry.io/otel/attribute" ) var ( ShutDown func(ctx context.Context) initOnce sync.Once ) // Init 初始化 Jaeger 链路追踪(延迟初始化,首次调用时执行) func Init() { initOnce.Do(func() { ctx := context.Background() jaegerAgent := g.Cfg().MustGet(ctx, "jaeger.addr").String() serverName := g.Cfg().MustGet(ctx, "server.name").String() if jaegerAgent == "" { g.Log().Warning(ctx, "⚠️ Jaeger 配置未找到,跳过初始化") ShutDown = func(ctx context.Context) {} // 空函数,避免 nil panic return } shutdown, err := otlphttp.Init(serverName, jaegerAgent, "/v1/traces") if err != nil { g.Log().Errorf(ctx, "Jaeger 初始化失败: %v", err) ShutDown = func(ctx context.Context) {} return } ShutDown = shutdown g.Log().Infof(ctx, "✅ Jaeger 初始化成功: %s", jaegerAgent) }) } func init() { // 默认自动初始化(保持向后兼容) Init() } func NewTracer(r *ghttp.Request) { _, span := gtrace.NewSpan(r.Context(), r.GetServeHandler().GetMetaTag("summary")) defer span.End() span.SetAttributes(attribute.String("request", getParams(r))) r.Middleware.Next() // 清理响应字符串,确保 UTF-8 有效(处理二进制数据如 ZIP 文件) response := r.Response.BufferString() cleanResponse := strings.ToValidUTF8(response, "") // 如果响应太大(如文件下载),只记录前 1000 字符 if len(cleanResponse) > 1000 { cleanResponse = cleanResponse[:1000] + "... (truncated)" } span.SetAttributes(attribute.String("response", cleanResponse)) } func getParams(r *ghttp.Request) string { params := map[string]interface{}{} if r.Method == "POST" { json.Unmarshal(r.GetBody(), ¶ms) //获取raw传参 } if r.Method == "GET" { r.Request.ParseForm() form := r.Form for k, v := range form { if vl, e := strconv.Atoi(v[0]); e == nil { params[k] = vl } else { params[k] = v[0] } } } rp, _ := json.Marshal(¶ms) return string(rp) }