When developing microservices using Go language, you need to track the access links of each request. There is currently no good solution in Go.
Solving this problem in Java is relatively simple. You can use MDC to share a requested RequestId within a process.
There are two ideas for implementing link tracking in Go: one is to use a global map in the project, the key is the only Id of the goroutine, the value is the RequestId, and the other idea can be implemented using .
The following code is implemented based on the gin framework.
1. Use global map to implement
Using the map scheme requires maintaining a map globally. When a request comes in, a RequestId will be generated for each request. Then, every time when printing the log, the RequestId is obtained from this map through goid and printed into the log.
The implementation of the code is very simple:
var requestIdMap = make(map[int64]string) // Global Map func main() { r := () (Logger()) // Use middleware ("/index", func(c *) { Info("main goroutine") // Print log (200, { "message": "index", }) }) () } func Logger() { return func(c *) { requestIdMap[()] = ().String() // Set for each request in the log middleware () } } func Info(msg string) { now := () nowStr := ("2006-01-02 15:04:05") ("%s [%s] %s\n", nowStr, requestIdMap[()], msg) // Print log}
This implementation is simple, but there are many problems.
The first problem is that in a Go program, a request may involve multiple goroutines at a time, and it is difficult to pass a RequestId between multiple goroutines in this way.
In the following code, if a new goroutine is started, the RequestId will not be retrieved from the log:
func main() { r := () (Logger()) ("/index", func(c *) { Info("main goroutine") go func() { // Here are newly launched goroutines Info("goroutine1") }() (200, { "message": "index", }) }) () }
Getting goroutine id is not a common practice. It usually needs to be obtained through hack. This practice is no longer recommended. Moreover, for concurrency security, this global map can also be used in actual use, which will inevitably affect performance in the case of high concurrency.
At the end of each request, you also need to manually delete the requestId from the map, otherwise it will cause memory leaks.
Overall, using map is not very good.
2. Use Context to implement
In the above code, we use a hack to get the goroutine id. This method has long been not recommended. It is also recommended to use Context. For the content of Context, you can read my previous article, and I won’t say much about it here.
In the scenario where RequestId is passed, Context can also be implemented. The benefits of using Context are obvious. The life cycle of the Context is the same as that of the request and does not require manual destruction. Moreover, Context is exclusive to each request, so there is no need to worry about concurrency security issues. Context can also be passed between goroutines.
The code implemented using Context is as follows:
func main() { r := () (Logger()) ("/index", func(c *) { ctx, _ := ("ctx") Info(ctx.() , "main goroutine") go func() { Info(ctx.(), "goroutine1") }() (200, { "message": "index", }) }) () } func Logger() { return func(c *) { valueCtx := ((), "RequestId", ().String()) ("ctx", valueCtx) () } } func Info(ctx , msg string) { now := () nowStr := ("2006-01-02 15:04:05") ("%s [%s] %s\n", nowStr, ("RequestId"), msg) }
In this way, all getroutines can get the same RequestId in one request, and there is no need to worry about memory leaks and concurrency security.
However, there is also a problem with using Context, which requires passing Context every time, and many people are not used to using this method. In fact, Go has long recommended using Context, and it usually uses Context as the first parameter of the function. If the function uses a structure as a parameter, you can also directly use Context as a field of the structure.
In addition to using RequestIds that can be used to pass the RequestId, Context can also be used to control the life cycle of goroutine. These contents were explained in detail in the previous Context article. If you are interested, you can take a look.
3. Summary
Getting goroutine id should be discarded, but Context should be used. Go has long recommended this method. In the above article, we use Context to pass RequestId. In addition, we can also use it to pass values in a single request range, such as authenticated tokens, and we should be accustomed to using Context in the code.
[1] /context
This is the article about how to do link tracking in Go services. For more related link tracking content in Go services, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!