Predinex uses structured JSON logs across the API and settlement bot. Every log line is a single JSON object written to stdout/stderr so log aggregators can parse and index it directly. A single correlation ID links a client request through API handlers, queued/background work, and each settlement step.
| Field | Required | Description |
|---|---|---|
ts |
yes | ISO-8601 timestamp (e.g. 2026-10-22T...Z). |
level |
yes | debug, info, warn, or error. |
service |
yes | predinex-api or predinex-settlement-bot. |
msg |
yes | Stable event name (e.g. http_request, unhandled_api_error, worker.job.settlement_cycle, pool_settled). |
correlationId |
when in scope | Canonical ID for this request/job chain. Grep for one ID to follow the full flow. |
requestId |
when in scope | Client-supplied or echoed request identifier. |
traceId |
when known | W3C trace ID (propagated via traceparent). |
spanId |
when known | W3C span ID. |
error |
on error | Error message when logging failures via serializeError. |
errorName |
on error | Error class name. |
stack |
optional | Stack trace when available. |
| ... | varies | Event-specific fields (route, status, durationMs, poolId, marketId, etc.). |
- Inbound API:
api/src/middleware/request-context.tsresolves the ID fromX-Correlation-ID,X-Request-ID, thetraceIdin a W3Ctraceparentheader, or a newly generated UUID. The ID is stored inAsyncLocalStorage, echoed asX-Correlation-IDandX-Request-IDon every response (including 404 and 500), and logged by the access middleware ashttp_request. - Tracing integration:
api/src/middleware/tracing.tsattachespredinex.correlation_idto spans and copies the correlation ID intox-correlation-id/x-request-idviainjectTraceHeadersandinjectJobTraceContext. Job carriers include bothtraceContextandcorrelationIdso workers can continue the chain. - Settlement bot:
bot/src/tracing.tsextractsx-correlation-id/x-request-idfrom job carriers andtraceparentfrom headers, stores it onSpanContext(falling back to the trace ID if none is provided), and injects it on outgoing headers.bot/src/logger.tsattachescorrelationId,traceId, andspanIdto every log line.bot/src/poller.tswraps each cycle inwithWorkerSpan("worker.job.settlement_cycle", ...), so every log emitted during that cycle inherits the correlation ID. - Downstream calls: Use
injectTraceHeaders()/injectWorkerTraceHeaders()when calling other services so correlation propagates end-to-end.
All API error responses include a correlation ID when available:
{
"success": false,
"error": {
"code": "INTERNAL_ERROR",
"message": "Internal server error",
"correlationId": "..."
},
"timestamp": "..."
}Route 404s use the same shape with "code": "NOT_FOUND" and include
correlationId.
- API:
LOG_LEVEL(debug|info|warn|error, defaultinfo) viaapi/src/lib/logger.ts. - Bot:
LOG_LEVEL(debug|info|warn|error, defaultinfo) viabot/src/logger.ts(configured throughbot/src/config.ts). - Tracing remains opt-in via
OTEL_TRACING_ENABLEDfor both services.
- API tests can read the correlation ID from response headers
(
X-Correlation-ID) or error bodies and assert logs when necessary. - Bot tests can pass a carrier with
x-correlation-idtowithWorkerSpan/extractWorkerTraceContextand assertgetActiveCorrelationId()or logged fields. - In tests, prefer injecting a known correlation ID to avoid non-determinism.