pkg/gateway/server/request_metadata.go
Source
- Package:
server - File:
pkg/gateway/server/request_metadata.go - GitHub: https://github.com/theroutercompany/api_router/blob/main/pkg/gateway/server/request_metadata.go
Overview
What: Generates and propagates request and trace identifiers through headers and request context.
Why: Stable IDs are required for correlating logs, metrics, and upstream calls across a distributed system.
How: Ensures X-Request-Id and X-Trace-Id are present on the request and stores them in context.Context for middleware/handlers to read.
Contents
Imports
import block 1
import (
"context"
"net/http"
"github.com/google/uuid"
)
Constants
const block 1
const (
requestIDKey contextKey = "requestID"
traceIDKey contextKey = "traceID"
)
requestIDKey
What: Context key used to store the request ID string.
Why: Avoids collisions by using a private typed key.
How: Written by ensureRequestIDs and read by requestIDFromContext.
traceIDKey
What: Context key used to store the trace ID string.
Why: Avoids collisions by using a private typed key.
How: Written by ensureRequestIDs and read by traceIDFromContext.
Types
type block 1
type contextKey string
contextKey
What: Private typed context key for request metadata.
Why: Prevents collisions with other context values by not using raw strings.
How: Used to define requestIDKey and traceIDKey.
Functions and Methods
ensureRequestIDs
What: Ensures a request has both request and trace identifiers and returns a request with an updated context.
Why: Callers and middleware should be able to assume IDs exist for logging and error responses.
How: Reads X-Request-Id and X-Trace-Id, generates a UUID when missing, defaults trace ID to request ID, sets headers, and stores both values in context.
func ensureRequestIDs(r *http.Request) (*http.Request, string, string) {
requestID := r.Header.Get("X-Request-Id")
if requestID == "" {
requestID = uuid.NewString()
r.Header.Set("X-Request-Id", requestID)
}
traceID := r.Header.Get("X-Trace-Id")
if traceID == "" {
traceID = requestID
r.Header.Set("X-Trace-Id", traceID)
}
ctx := context.WithValue(r.Context(), requestIDKey, requestID)
ctx = context.WithValue(ctx, traceIDKey, traceID)
return r.WithContext(ctx), requestID, traceID
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L18:
requestID := r.Header.Get("X-Request-Id")- What: Defines requestID.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L19:
if requestID == "" { requestID = uuid.NewString() r.Header.Set("X-Request-Id", requestID) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L20:
requestID = uuid.NewString()- What: Assigns requestID.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L21:
r.Header.Set("X-Request-Id", requestID)- What: Calls r.Header.Set.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L20:
- L24:
traceID := r.Header.Get("X-Trace-Id")- What: Defines traceID.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L25:
if traceID == "" { traceID = requestID r.Header.Set("X-Trace-Id", traceID) }- What: Branches conditionally.
- Why: Handles different execution paths based on runtime state.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L26:
traceID = requestID- What: Assigns traceID.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L27:
r.Header.Set("X-Trace-Id", traceID)- What: Calls r.Header.Set.
- Why: Performs side effects or delegates work to a helper.
- How: Executes the expression statement.
- L26:
- L30:
ctx := context.WithValue(r.Context(), requestIDKey, requestID)- What: Defines ctx.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L31:
ctx = context.WithValue(ctx, traceIDKey, traceID)- What: Assigns ctx.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L33:
return r.WithContext(ctx), requestID, traceID- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
requestIDFromContext
What: Extracts the request ID from context.
Why: Allows middleware to log and emit IDs without re-parsing headers.
How: Looks up requestIDKey and type-asserts to string.
func requestIDFromContext(ctx context.Context) string {
if ctx == nil {
return ""
}
if value, ok := ctx.Value(requestIDKey).(string); ok {
return value
}
return ""
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L37:
if ctx == nil { return "" }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L38:
return ""- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L38:
- L40:
if value, ok := ctx.Value(requestIDKey).(string); ok { return value }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L40:
value, ok := ctx.Value(requestIDKey).(string)- What: Defines value, ok.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L41:
return value- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L40:
- L43:
return ""- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
traceIDFromContext
What: Extracts the trace ID from context.
Why: Allows middleware and error writers to correlate failures across systems.
How: Looks up traceIDKey and type-asserts to string.
func traceIDFromContext(ctx context.Context) string {
if ctx == nil {
return ""
}
if value, ok := ctx.Value(traceIDKey).(string); ok {
return value
}
return ""
}
Walkthrough
The list below documents the statements inside the function body, including nested blocks and inline closures.
- L47:
if ctx == nil { return "" }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L48:
return ""- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L48:
- L50:
if value, ok := ctx.Value(traceIDKey).(string); ok { return value }- What: Branches conditionally.
- Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
- How: Evaluates the condition and executes the matching branch.
- Nested steps:
- L50:
value, ok := ctx.Value(traceIDKey).(string)- What: Defines value, ok.
- Why: Keeps intermediate state available for later steps in the function.
- How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
- L51:
return value- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).
- L50:
- L53:
return ""- What: Returns from the current function.
- Why: Ends the current execution path and hands control back to the caller.
- How: Executes a
returnstatement (possibly returning values).