# Trace Log with traceId

In web applications, a single request may go through many business processes. To facilitate troubleshooting, we want all logs generated by different business processes to have a unified traceId. Having a traceId allows us to link all logs related to the entire business process, making it easier to trace and analyze where problems occur in the business flow.

In other open-source frameworks, it's generally recommended to pass a context.Context parameter to all functions, which is also recommended by the official Golang documentation. However, we believe that adding this extra parameter to every function is burdensome. We aim to avoid passing additional parameters to every function just to print a traceId in logs. Therefore, in Gone, we provide a built-in Goner to achieve this functionality.

tip: To understand the core concepts and terminology of Gone, please read: Core Concepts of Gone (opens new window)

Here, we use the BasePriest from the package github.com/gone-io/gone/tree/main/goner to bury related Goners. In the BasePriest, Goners related to tracer, config, and logrus are all buried into the Cemetery, as these three packages are commonly used together.

func MasterPriest(cemetery gone.Cemetery) error {
	_ = goner.BasePriest(cemetery)
	// Bury other Goners
	return nil
}

# Simple Usage

When the tracer is buried, when we print logs using the injected logrus.Logger interface, a traceId will automatically be added to the logs.

//...
type service struct {
    gone.Flag
    log logrus.Logger `gone:"gone-logger"` // Named injection into nested log attributes
}
func (svc *service) Business(input string) (string, error) {
    // Print log
	svc.log.Infof("input content is %s", input)
	return input, nil
}
//...

For example, the 061ad00f-8c0d-479c-bc4c-393e0cf2cca2 is the traceId:

2024-05-11 09:09:57.784|INFO|**/Users/jim/go/pkg/mod/github.com/gone-io/gone@v0.1.4/goner/gin/server.go:46**|061ad00f-8c0d-479c-bc4c-393e0cf2cca2|Server Listen At :8080

# Passing TraceId Across Goroutines

In the previous example, it's normal if no new goroutines are used. If a new goroutine is started using the go keyword, it will be noticed that the logs printed by the goroutine do not have a traceId. To solve this, inject the tracer.Tracer interface and use the Go method instead of the go keyword to start a new goroutine.

//...
type service struct {
    gone.Flag
    log logrus.Logger `gone:"gone-logger"` // Named injection into nested log attributes
    tracer.Tracer `gone:"gone-tracer"` // Inject tracer
}
func (svc *service) Business(input string) (string, error) {
	svc.Go(func() {
        // Print log in a new goroutine
		svc.log.Infof("log in new goroutine")
	})
	return input, nil
}
//...

# Passing TraceId Across Processes/Services

In microservices, a web request typically spans multiple microservices. Cross-service communication is generally done through:

  1. Message middleware To facilitate the passing of traceId in the message middleware and to facilitate the use of the message middleware to pass business events, we have open-sourced the https://github.com/gone-io/emitter (opens new window) repository. In this repository, we have implemented an adapter for Rocket MQ, and plan to adapt to other mainstream message middleware such as Kafka and RabbitMQ in the future.
  2. RPC calls/internal http calls Use the built-in Goner urllib (opens new window) to send http requests to Gone Web programs, and the traceId will be automatically passed between services. Additionally, using the built-in grpc (opens new window) to implement gRPC calls will also automatically pass the traceId. More RPC call support will be provided in the future.

# Multi-language Support

To pass the traceId in an HTTP request, a special header X-Trace-ID is added to carry the traceId. Therefore, if multiple programming languages are used, as long as the different services follow the rule of "attaching X-Trace-ID when making requests on the client side and parsing X-Trace-ID when processing requests on the server side," traceId can be seamlessly passed across services developed in different languages.
We plan to develop packages in other languages to seamlessly integrate with other languages in the future.