Trace Context in Logs
Adding trace context to application logs provides the ability to correlate the log messages from a traced transaction, and if sampled, the log messages to the transaction trace detail.
The AppOptics Go agent supports manual log context injection. The loggable trace ID can be accessed via the method LoggableTraceID()
of a trace. Below is a demo application showing how the loggable trace ID is used for logging purpose.
package main
import (
"fmt"
"github.com/appoptics/appoptics-apm-go/v1/ao"
"log"
"net/http"
"strconv"
)
const (
Port = 8090
)
func demoHandler(w http.ResponseWriter, r *http.Request) {
t := ao.TraceFromContext(r.Context())
log.Print(fmt.Sprintf("%s=%s\n", ao.LoggableTraceID, t.LoggableTraceID()))
_, _ = fmt.Fprintf(w, "demo\n")
}
func main() {
http.HandleFunc("/demo", ao.HTTPHandler(demoHandler))
fmt.Printf("%v", http.ListenAndServe(":"+strconv.Itoa(Port), nil))
}
It prints the key and value of the loggable trace ID, for example, ao.traceId=91A2FC1F4FDE5F2FFFF177E7C884BF06E44678C3-1
, if a client request accesses the endpoint /demo
.
Note that,
ao.traceId
: the recommended key, defined byao.LoggableTraceID
.91A2FC1F4FDE5F2FFFF177E7C884BF06E44678C3
: the trace ID.1
(after the dash): the sampling flag, which could be either0
(not sampled) or1
(sampled).
The method LoggableTraceID()
returns an empty string if there is no valid context for the trace, e.g., when the agent is disabled.
Zap
The loggable trace ID can be logged as a structured field in Zap:
var (
logger *zap.Logger
sugar *zap.SugaredLogger
)
func init() {
logger, _ = zap.NewProduction() // ignore error handling here for demo only
sugar = logger.Sugar()
}
func demoHandler(w http.ResponseWriter, r *http.Request) {
t := ao.TraceFromContext(r.Context())
// Logger
logger.Info("Failed to fetch URL.",
zap.String(ao.LoggableTraceID, t.LoggableTraceID()),
)
// Sugared logger
sugar.Infow("This is a demo log.",
ao.LoggableTraceID, t.LoggableTraceID())
_, _ = fmt.Fprintf(w, "demo\n")
}
An example output of it:
{"level":"info","ts":1557337865.186354,"caller":"go/test.go:29","msg":"Failed to fetch URL.","ao.traceId":"79CBEF7730D5E220FAFDEBEBD48EF5A38675031C-1"}
{"level":"info","ts":1557337865.1864462,"caller":"go/test.go:34","msg":"This is a demo log.","ao.traceId":"79CBEF7730D5E220FAFDEBEBD48EF5A38675031C-1"}
Logrus
Logrus also supports structured fields which is a good fit for the loggable trace ID:
func demoHandler(w http.ResponseWriter, r *http.Request) {
t := ao.TraceFromContext(r.Context())
log.WithFields(log.Fields{
ao.LoggableTraceID: t.LoggableTraceID(),
}).Info("This is a demo log.")
_, _ = fmt.Fprintf(w, "demo\n")
}
An example output of it:
INFO[0005] This is a demo log. ao.traceId=1BDEE50BD073C5A543919E3DC2F2C924ACCA7527-1
The scripts are not supported under any SolarWinds support program or service. The scripts are provided AS IS without warranty of any kind. SolarWinds further disclaims all warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The risk arising out of the use or performance of the scripts and documentation stays with you. In no event shall SolarWinds or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the scripts or documentation.