我正在开发一个应用程序来处理 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。
这里的解决方案是什么?预先感谢。
希望这个示例片段能有所帮助。 由于您正在全局初始化记录器,因此在处理并发请求时可能会导致错误行为。相反,我们可以将该记录器与本地范围的字段一起使用。
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"}