Done channel that closes when the cancellation occursDone() channel is often used in select blockscontext.Background top level context) for any timeout contexts
ctx := context.Background()
ctx = context.WithValue(ctx, "traceId", "abc123")
ctx, cancel := context.WithTimeout(ctx, 3 * time.Second)
defer cancel() // it is common to defer cancel
req, _ := http.NewRequest(http.MethodGet, url, nil)
req = req.WithContext(ctx)
resp, err := http.DefaultClient.Do(req)
Done())
select, then you can see the error in ctx.Err()type contextKey int
// Make sure the keys are exported (but not the type itself), then clients have a single source of truth for requesting context values without the risk of collision
const (
TraceIdContextKey contextKey = iota
StartTimeContextKey contextKey
AuthContextKey contextKey
)
func AddTrace(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if traceID := r.Header.Get("X-Trace-Context"); traceID != "" {
ctx = context.WithValue(ctx, TraceIdContextKey, traceID)
}
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func LogWithContext(ctx context.Context, f string, args ...any) {
// reflection is required because the context values "map" can contain any. We need
// to downcast the any to a string (this two argument cast will return ok=true if
// the conversion was a success). More on reflection later ;)
traceID, ok := ctx.Value(TraceIdContextKey).(string)
// adding the trace ID to the log message if it is in the context
if ok && traceID != "" {
f = traceID + ": " + f
}
log.Printf(f, args...)
}