在Golang日志中记录请求ID。

huangapple go评论71阅读模式
英文:

Log request id in golang logs

问题

我正在处理一个使用Go编写的现有应用程序,使用了像gin、middleware这样的框架。该应用程序使用https://pkg.go.dev/log进行日志记录。

我正在尝试为API调用跟踪向日志添加一个请求ID。

main.go

// 默认情况下创建一个没有任何中间件的路由器
r := gin.New()
r.Use(middleware.RequestID())

middleware.go

func (m *Middleware) CheckApiToken(allowWithoutAccessKey bool, validateTonce ...bool) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 验证
		...
		...
		logger.InitializeContext(c)
		c.Next()
	}
}

// RequestID是一个中间件,将一个'RequestID'注入到每个请求的上下文和头部中。
func (m *Middleware) RequestID() gin.HandlerFunc {
	return func(c *gin.Context) {
		xRequestID := uuid.NewV4().String()
		c.Request.Header.Set(logger.XRequestIDKey, xRequestID)
		fmt.Printf("[GIN-debug] %s [%s] - \"%s %s\"\n", time.Now().Format(time.RFC3339), xRequestID, c.Request.Method, c.Request.URL.Path)
		c.Next()
	}
}

logger.go

const (
	XRequestIDKey = "X-Request-ID"
)

var (
	infoLogger  *log.Logger
	errorLogger *log.Logger
	context     *gin.Context
)

func init() {
	infoLogger = log.New(os.Stdout, "", 0)
	errorLogger = log.New(os.Stderr, "", 0)
}

// InitializeContext将全局gin上下文初始化为日志记录器
func InitializeContext(c *gin.Context) {
	context = c
}

// 检查上下文中是否存在请求ID。
func getRequestID() interface{} {
	if context != nil {
		if context.Request != nil {
			requestID := context.Request.Header.Get(XRequestIDKey)
			if requestID != "" {
				return requestID
			}
		}
	}
	return ""
}

func Info(entry Input) {
	infoLogger.Println(getJSON(getRequestID(), msg))
}

这在多线程环境下不起作用。我该如何修复这个问题以适应多线程环境?

英文:

I am working on an existing application which is written in Go using framework such as gin, middleware. This application uses https://pkg.go.dev/log for logging.

I am trying to add a request id to the log for the API call trace.

main.go

		// Creates a router without any middleware by default
		r := gin.New()
		r.Use(middleware.RequestID())

middleware.go


func (m *Middleware) CheckApiToken(allowWithoutAccessKey bool, validateTonce ...bool) gin.HandlerFunc {
	return func(c *gin.Context) {

            // Validate
            .....
            .....
			logger.InitializeContext(c)
			c.Next()
		}
	}
}


//RequestID is a middleware that injects a 'RequestID' into the context and header of each request.
func (m *Middleware) RequestID() gin.HandlerFunc {
	return func(c *gin.Context) {
		xRequestID := uuid.NewV4().String()
		c.Request.Header.Set(logger.XRequestIDKey, xRequestID)
		fmt.Printf("[GIN-debug] %s [%s] - \"%s %s\"\n", time.Now().Format(time.RFC3339), xRequestID, c.Request.Method, c.Request.URL.Path)
		c.Next()
	}
}

logger.go


const (
	XRequestIDKey = "X-Request-ID"
)

var (
	infoLogger  *log.Logger
	errorLogger *log.Logger
	context     *gin.Context
)

func init() {

	infoLogger = log.New(os.Stdout, "", 0)
	errorLogger = log.New(os.Stderr, "", 0)
}

// InitializeContext initialize golbal gin context to logger
func InitializeContext(c *gin.Context) {
	context = c
}

//Check if the request id present in the context.
func getRequestID() interface{} {
	if context != nil {
		if context.Request != nil {
			requestID := context.Request.Header.Get(XRequestIDKey)
			if requestID != "" {
				return requestID
			}
		}
	}
	return ""
}

func Info(entry Input) {
	infoLogger.Println(getJSON(getRequestID(), msg))
}

This does not work in multi-threaded environment. How do I fix this solution to fix this in multi-threaded environment.

答案1

得分: 2

你不能将上下文保存在全局变量中。上下文在定义上是局部的,而且在任何给定的时刻,都会有多个上下文存在。

你可以将生成的ID存储在gin上下文中:

func (m *Middleware) RequestID() gin.HandlerFunc {
    return func(c *gin.Context) {
        xRequestID := uuid.NewV4().String()
        c.Set("requestId", xRequestID)
        fmt.Printf("[GIN-debug] %s [%s] - \"%s %s\"\n", time.Now().Format(time.RFC3339), xRequestID, c.Request.Method, c.Request.URL.Path)
        c.Next()
    }
}

然后,你可以使用存储在上下文中的ID与自定义日志格式化程序一起使用:

router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
    return fmt.Sprintf("%s ...", param.Keys["requestId"], ...)
}))

或者,如果你需要使用不同的日志记录库,你可以编写一个包装器:

func LogInfo(ctx *gin.Context, msg string) {
    id := ctx.Get("requestId")
    // 使用ID记录日志消息
}

许多日志库提供了使用某些预设参数获取记录器的方法。例如,如果你使用zerolog:

logger := log.Info().With().Str("requestId", ctx.Get("requestId")).Logger()

logger.Info().Msg("This log msg will contain the request id")
英文:

You cannot save the context in a global variable. Context is by definition local to that execution, and at any given moment, there will be multiple of them.

You can store the generated ID in the gin context:

func (m *Middleware) RequestID() gin.HandlerFunc {
    return func(c *gin.Context) {
        xRequestID := uuid.NewV4().String()
        c.Set("requestId",xRequestID)
        fmt.Printf("[GIN-debug] %s [%s] - \"%s %s\"\n", time.Now().Format(time.RFC3339), xRequestID, c.Request.Method, c.Request.URL.Path)
        c.Next()
    }
}

Then you can use the ID stored in the context with the custom log formatter:

router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {

		return fmt.Sprintf("%s ...",
				param.Keys["requestId"],
               ...
		)
	}))


Or if you need to use a different logging library, you can write a wrapper:


func LogInfo(ctx *gin.Context,msg string) {
   id:=ctx.Get("requestId")
   // Log msg with ID
}

Many log libraries offer methods to get a logger with some parameters already set. For instance, if you use zerolog:

logger:=log.Info().With().Str("requestId",ctx.Get("requestId")).Logger()

logger.Info().Msg("This log msg will contain the request id")

huangapple
  • 本文由 发表于 2022年4月12日 11:59:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/71836926.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定