Golang中无法获取panic的trace ID问题

Golang中无法获取panic的trace ID问题 我经常收到关于panic数量的警报,但查看panic定义时,我只能知道panic发生的位置,却无法识别是哪个请求导致了这次panic。

2 回复

你能分享你的代码吗?

更多关于Golang中无法获取panic的trace ID问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang中,当panic发生时,确实很难直接获取到当前请求的trace ID。这是因为panic处理机制与常规的请求上下文是分离的。不过,我们可以通过一些技术手段来解决这个问题。

一种常见的方法是在每个请求的上下文中设置trace ID,并在panic发生时通过recover机制来获取这个trace ID。下面是一个示例实现:

package main

import (
    "context"
    "fmt"
    "net/http"
    "runtime/debug"
)

type traceIDKey struct{}

func WithTraceID(ctx context.Context, traceID string) context.Context {
    return context.WithValue(ctx, traceIDKey{}, traceID)
}

func GetTraceID(ctx context.Context) string {
    if id, ok := ctx.Value(traceIDKey{}).(string); ok {
        return id
    }
    return ""
}

func PanicRecoverMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                traceID := GetTraceID(r.Context())
                stack := debug.Stack()
                
                fmt.Printf("Panic occurred!\n")
                fmt.Printf("Trace ID: %s\n", traceID)
                fmt.Printf("Error: %v\n", err)
                fmt.Printf("Stack trace:\n%s\n", stack)
                
                http.Error(w, "Internal Server Error", http.StatusInternalServerError)
            }
        }()
        
        next.ServeHTTP(w, r)
    })
}

func TraceIDMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        traceID := r.Header.Get("X-Trace-ID")
        if traceID == "" {
            traceID = generateTraceID() // 实现自己的trace ID生成逻辑
        }
        
        ctx := WithTraceID(r.Context(), traceID)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func generateTraceID() string {
    return "trace-123456" // 简化示例,实际应该使用UUID等
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/panic", func(w http.ResponseWriter, r *http.Request) {
        panic("something went wrong")
    })
    
    handler := TraceIDMiddleware(PanicRecoverMiddleware(mux))
    http.ListenAndServe(":8080", handler)
}

在这个示例中,我们创建了两个中间件:TraceIDMiddleware用于为每个请求设置trace ID,PanicRecoverMiddleware用于捕获panic并记录trace ID。当panic发生时,我们可以在recover函数中通过GetTraceID(r.Context())获取到当前请求的trace ID。

对于更复杂的应用,你可能需要集成更完整的追踪系统,比如OpenTelemetry。下面是一个使用OpenTelemetry的示例:

package main

import (
    "context"
    "net/http"
    
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/trace"
)

var tracer = otel.Tracer("myapp")

func PanicRecoverWithOTel(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                span := trace.SpanFromContext(r.Context())
                if span.IsRecording() {
                    span.RecordError(err.(error))
                    span.SetAttributes(
                        attribute.String("panic.error", fmt.Sprintf("%v", err)),
                    )
                }
                span.End()
                
                http.Error(w, "Internal Server Error", http.StatusInternalServerError)
            }
        }()
        
        ctx, span := tracer.Start(r.Context(), "http.request")
        defer span.End()
        
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

使用OpenTelemetry可以让你获得更完整的分布式追踪能力,包括自动的trace ID生成和跨服务的追踪。

回到顶部