Skip to content

requestLog

const requestLog: (config?) => Policy

Defined in: packages/gateway/src/policies/observability/request-log.ts:142

Emit structured JSON logs for every request/response pair.

Captures method, path, status, duration, client IP, user agent, and gateway context (request ID, gateway name, route path). Runs at priority 0 so it wraps the entire pipeline and measures end-to-end latency.

By default, logs are written to console.log as JSON lines. Provide a custom sink to route logs to an external service (e.g., Logflare, Datadog, or a Durable Object buffer).

Request logs and analytics (@vivero/stoma-analytics) serve different purposes and deliberately carry different fields.

Request logs (this policy) are for debugging and operational triage. Fields are high-cardinality — grep-friendly, not GROUP BY-friendly:

FieldWhy it’s here
requestIdUnique per request — grep to find a single transaction
pathActual URL e.g. /users/42 (high cardinality)
clientIpPII, high cardinality — abuse investigation only
userAgentHigh cardinality — debug specific client issues
spanIdDistributed tracing span correlation
requestBodyDeep debugging (opt-in, redactable)
responseBodyDeep debugging (opt-in, redactable)

Overlapping fields (appear in both logs and analytics):

FieldWhy both need it
timestampTime-series bucketing (analytics) / grep by time (logs)
gatewayNameGROUP BY gateway (analytics) / filter logs by gateway
routePathGROUP BY route pattern (analytics) / filter by route
methodGROUP BY method (analytics) / filter logs by method
statusCodeError rate dashboards (analytics) / grep errors (logs)
durationMsAVG/P99 latency (analytics) / slow request triage
traceIdDashboard anomaly drill-down → find matching log lines

Analytics-only fields (NOT in request logs):

FieldWhy only analytics
responseSizeSUM bandwidth, detect payload bloat — aggregate only
dimensionsExtensible low-cardinality facets for GROUP BY

RequestLogConfig

Custom field extraction, body logging, and sink. All fields optional.

Policy

A Policy at priority 0 (runs first, wraps everything).

import { createGateway } from "@vivero/stoma";
import { requestLog } from "@vivero/stoma/policies";
// Default structured JSON logging to console
createGateway({
policies: [requestLog()],
routes: [...],
});
// With body logging and redaction
requestLog({
logRequestBody: true,
logResponseBody: true,
redactPaths: ["password", "*.secret", "auth.token"],
sink: async (entry) => {
await fetch("https://logs.example.com/ingest", {
method: "POST",
body: JSON.stringify(entry),
});
},
});