Alex

Context (Video 25)

The Context Tree

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)

Context With Values

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...)
}