Correlate the browser and the service¶
HTTP Guardian and MIC can carry the same identifier from a user action to the
service logs. This does not recreate distributed tracing: the identifier makes
operational searches easier, while traceparent remains the standard for
linking browser, HTTP, and SQL spans.
Recommended contract¶
X-Request-Idcovers one logical request, including retries. Use it for log searches and support responses.X-Correlation-Idcovers a business journey that may span several requests. Use it for explicit functional grouping.traceparentcovers one distributed trace. Use it to connect OpenTelemetry spans through instrumented dependencies.
Use X-Request-Id when the identifier must immediately appear in every MIC
structured log. correlationIdGuard() keeps the value stable across retries,
and LogContextMiddleware validates it, binds it to the log context, and
returns it in the response.
Browser side¶
Install the current public HTTP Guardian prerelease:
Configure the guard with the header name expected by the MIC log context:
import {
correlationIdGuard,
createApiParserStructured,
createHttpClient,
noAuth,
} from "@cedvict/http-guardian";
export const api = createHttpClient({
baseUrl: "https://api.example.com",
auth: noAuth(),
parser: createApiParserStructured(),
guards: [
correlationIdGuard({ headerName: "X-Request-Id" }),
],
});
An identifier is generated once for each logical request. If a retry plugin
replays the call, every attempt therefore keeps the same identifier. The
header returned by the service is also copied to ctx.meta.correlationId for
HTTP Guardian logging, Sentry, or OpenTelemetry plugins.
MIC with FastAPI¶
from fastapi import FastAPI
from mic.observability import LogContextMiddleware
app = FastAPI()
app.add_middleware(LogContextMiddleware)
Every structured log emitted during the request receives request_id. The
response carries the same X-Request-Id. An incoming value that does not match
the [A-Za-z0-9_-]{1,128} allow-list is replaced instead of being reflected in
logs and headers.
MIC with Litestar¶
from litestar import Litestar
from litestar.middleware import DefineMiddleware
from mic.observability import LogContextMiddleware
app = Litestar(
route_handlers=[...],
middleware=[DefineMiddleware(LogContextMiddleware)],
)
The middleware is ASGI-based, so FastAPI and Litestar share the same contract and log fields.
Business journeys with X-Correlation-Id¶
Keep the default browser-side header name when the same identifier must span several requests:
Then enable the MIC correlation middleware:
MIC then exposes the identifier as request.state.correlation_id with
FastAPI, or as scope["state"]["correlation_id"] with Litestar. It also
returns X-Correlation-Id, X-Request-Id, and X-Response-Time-ms.
Prefer the X-Request-Id contract from the previous section when logs must be
correlated automatically. A business-journey identifier may be logged as an
additional field, but it does not replace the request identifier.
Frontend and API on different origins¶
The API CORS policy must:
- allow
X-Request-IdorX-Correlation-Idin request headers; - expose the same header through
Access-Control-Expose-Headersso HTTP Guardian can read the value returned by the service.
Without response-header exposure, propagation to the service still works, but the browser cannot read the value normalized by the API.
Operational check¶
The check is complete when:
- the response contains
X-Request-Id: checkout-smoke-42; - the MIC logs for this request contain
"request_id":"checkout-smoke-42"; - any retries carry the same value;
- distributed spans continue to use
traceparentindependently.