You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang中如何将请求头X-Trace-ID传入LokiLogs添加至日志

如何将API请求的X-Trace-ID传递给Loki日志函数

要实现把每个API请求的X-Trace-ID头值传递给LokiLogs函数,核心是通过**请求上下文(context)**在Gin请求链路、数据库操作、日志打印之间传递traceId,具体步骤如下:

1. 在Gin中间件中把traceId存入请求context

除了设置请求头,还要将traceId存入请求的context,这样后续数据库操作和日志打印就能获取到:

import (
    "context"
    "github.com/gin-gonic/gin"
    "github.com/google/uuid"
)

func TraceIdMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        traceID := uuid.NewString() // 替换成你实际生成traceId的逻辑
        c.Request.Header.Set("X-Trace-ID", traceID)
        // 将traceId存入请求context
        ctx := context.WithValue(c.Request.Context(), "traceId", traceID)
        c.Request = c.Request.WithContext(ctx)
        c.Next()
    }
}

// 在main中注册中间件
func main() {
    router := gin.Default()
    router.Use(TraceIdMiddleware())
    // ...其他路由注册
    db.LogMode(true)
    db.SetLogger(&StdoutLogger{os.Stdout})
    router.Run()
}

2. 修改StdoutLogger的Print方法,提取context中的traceId

GORM在执行带context的数据库操作时,会把context作为第一个参数传入Logger的Print方法,我们可以从中提取traceId:

import "context"

type StdoutLogger struct {
    io.Writer
}

func (l *StdoutLogger) Print(v ...interface{}) {
    traceId := ""
    // 尝试从参数中获取context并提取traceId
    if len(v) > 0 {
        if ctx, ok := v[0].(context.Context); ok {
            if tid, ok := ctx.Value("traceId").(string); ok {
                traceId = tid
            }
        }
    }

    vv := LogFormatter(v...)
    log.Println(vv)

    if len(vv.Message) > 0 {
        slice := vv.Message[0]
        logMsg := slice.Sql + " " + slice.SqlMessage
        // 把traceId传递给LokiLogs
        LokiLogs(logMsg, traceId)
    }
}

3. 更新LokiLogs函数,接收并拼接traceId

修改LokiLogs的函数签名,新增traceId参数,将其拼接到日志消息中:

func LokiLogs(msg string, traceId string) {
    v := strconv.FormatInt(time.Now().UnixNano(), 10)

    // 仅当traceId不为空时拼接
    if traceId != "" {
        msg = msg + " traceId=" + traceId
    }

    Lokijson := map[string]interface{}{
        "streams": []interface{}{
            map[string]interface{}{
                "stream": map[string]interface{}{
                    "message":   msg,
                    "client_ip": GetOutboundIP(),
                    "level":     "info",
                    "job":       "Test",
                },
                "values": []interface{}{
                    []interface{}{
                        string(v),
                        msg,
                    },
                },
            },
        },
    }

    jsonValue, _ := json.Marshal(Lokijson)
    url := os.Getenv("LOKI_URL")
    resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonValue))
    fmt.Println(resp, err)
}

4. 确保数据库操作使用带traceId的context

在Gin的请求处理函数中,执行数据库操作时必须传入请求的context,这样GORM才会把context传递给Logger:

router.GET("/users/:id", func(c *gin.Context) {
    var user User
    // 使用c.Request.Context(),包含traceId的上下文
    err := db.WithContext(c.Request.Context()).First(&user, c.Param("id")).Error
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, user)
})

内容的提问来源于stack exchange,提问作者Hari Prasanth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 03:49:58