Golang 上下文变量被另一个或并行请求覆盖

问题描述 投票:0回答:1

我正在开发一个应用程序来处理 Webhook,对于每个请求,我们都会将唯一的 TraceID 附加到

slog
。这是我的中间件:

func InitSlog(ctx context.Context, tracer *string, parentTracer string) (string, string) {
    logger := slog.New(
        slogFormatter.NewFormatterHandler(
            slogFormatter.HTTPRequestFormatter(false),
            slogFormatter.HTTPResponseFormatter(false),
        )(
            slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
                AddSource: true,
            }),
        ),
    )
    //logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
    //  AddSource: true,
    //}))
    if tracer == nil || *tracer == "" {
        s := generateCorrelationID()
        tracer = &s
    }
    newLogger := logger.With("traceID", tracer, "parentTraceID", parentTracer)
    slog.SetDefault(newLogger)

    return *tracer, parentTracer
}

这里

parent traceID
表示在我们创建谷歌云任务时跟踪单个Webhook的不同进程。

func Handlewebhook(c *gin.Context) {
    ctx := context.Background()
    traceID, _ := utils.InitSlog(c, nil, "")
    ctx = context.WithValue(ctx, constants.TraceIDKey, traceID)
}

这是我处理 webhook 的处理程序。当我们同时收到多个 Webhook 时,

parent trace ID
会被另一个请求覆盖。

我将 ctx 传递给不同的函数,并使用代码

traceId, _ := ctx.Value(constants.TraceIDKey).(string)
获取 TraceID 并传递给 gcloud 任务来处理 webhook。

这里的解决方案是什么?预先感谢。

go request
1个回答
0
投票

希望这个示例片段能有所帮助。 由于您正在全局初始化记录器,因此在处理并发请求时可能会导致错误行为。相反,我们可以将该记录器与本地范围的字段一起使用。

package main

import (
    "context"
    "fmt"
    "log/slog"
    "os"
    "time"

    "github.com/gin-gonic/gin"
)

const contextParentTracerkey = "parent-tracer-span"

func generateCorrelationID() string {
    return fmt.Sprintf("trace-%v", time.Now().UnixMicro())
}

func init() {
    logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
    slog.SetDefault(logger)
}

func ContextLogger(ctx context.Context) (*slog.Logger, context.Context) {
    // get/check the previous tracer id for setting the parent tracer
    parentTracer, _ := ctx.Value(contextParentTracerkey).(string)
    tracer := generateCorrelationID() // new tracer id will generate each time

    // set the tracer in context, as this can be the parent for another one
    ctx = context.WithValue(ctx, contextParentTracerkey, tracer)
    return slog.With("traceID", tracer, "parentTraceID", parentTracer), ctx
}

func Handlewebhook(c *gin.Context) {
    // first level
    log, ctx := ContextLogger(context.Background())
    log.Info("Level - 1")

    // Calling another function with context
    func(ctx context.Context) {
        log, ctx := ContextLogger(ctx)
        log.Info("Level - 2 (parent level 1)")

        // Calling another function with context
        func(ctx context.Context) {
            log, ctx := ContextLogger(ctx)
            log.Info("Level - 3 (parent level 2)")

            // Calling another function with context
            func(ctx context.Context) {
                log, ctx := ContextLogger(ctx)
                log.Info("Level - 4 (parent level 2)")
            }(ctx)

        }(ctx)
    }(ctx)
}

func main() {
    r := gin.New()
    r.GET("/webhook", Handlewebhook)
    r.Run(":8080")
}

输出

{"level":"INFO","msg":"Level - 1","traceID":"trace-1709877837339750","parentTraceID":""}
{"level":"INFO","msg":"Level - 2 (parent level 1)","traceID":"trace-1709877837339844","parentTraceID":"trace-1709877837339750"}
{"level":"INFO","msg":"Level - 3 (parent level 2)","traceID":"trace-1709877837339851","parentTraceID":"trace-1709877837339844"}
{"level":"INFO","msg":"Level - 4 (parent level 2)","traceID":"trace-1709877837339856","parentTraceID":"trace-1709877837339851"}

© www.soinside.com 2019 - 2024. All rights reserved.