Envelope
The envelope is the structured log payload emitted by fapilog before serialization.
Shape (v1.1 Schema)
Every log entry follows the v1.1 schema with semantic field groupings:
{
"timestamp": "2024-01-15T10:30:00.123Z",
"level": "INFO",
"message": "User action",
"logger": "app",
"context": {
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"correlation_id": "req-123",
"request_id": "abc-456",
"user_id": "user-789"
},
"diagnostics": {
"service": "api",
"env": "production",
"host": "web-01",
"pid": 12345
},
"data": {
"action": "login",
"duration_ms": 42
}
}
Core Fields
timestamp: RFC3339 UTC string with millisecond precision (e.g.,"2024-01-15T10:30:00.123Z").level: one of DEBUG/INFO/WARNING/ERROR/CRITICAL.message: the message string passed to the logger method.logger: logger name (get_logger(name=...)).
Semantic Groupings
context: Request/trace identifiers. These identify WHO and WHAT request is being logged.message_id: Unique UUID for each log entry. Always present, auto-generated.correlation_id: Shared identifier across related log entries. Always present;nullwhen no correlation context is active, populated when set via context variable (e.g.,request_id_var). Use this for request-level tracing.request_id,user_id,tenant_id,trace_id,span_id: Optional trace context fields.
diagnostics: Runtime/operational data (service, env, host, pid, exception). These identify WHERE the log originated and system state.data: User-provided structured data from extra kwargs and bound context (excluding context fields).
message_id vs correlation_id
message_id: Uniquely identifies each individual log entry. A new UUID is generated for every log call. Use this when you need to reference a specific log line.
correlation_id: Groups related log entries together. Always present in the envelope for stable schema shape. Set to
nullwhen no correlation context is active; populated when set viarequest_id_var.set()or when using FastAPI integration (which sets it automatically per request). Use this for tracing all logs from a single HTTP request or operation.
from fapilog import get_logger
from fapilog.core.context import request_id_var
logger = get_logger()
# Without context - correlation_id is null
logger.info("standalone log") # {"context": {"message_id": "aaa-111", "correlation_id": null}}
# With context - both message_id and correlation_id present
token = request_id_var.set("req-123")
logger.info("first") # {"context": {"message_id": "bbb-222", "correlation_id": "req-123"}}
logger.info("second") # {"context": {"message_id": "ccc-333", "correlation_id": "req-123"}}
request_id_var.reset(token)
Exceptions
When exc_info=True or exc is provided, the envelope includes structured exception data in the diagnostics.exception field:
{
"diagnostics": {
"exception": {
"exception_type": "ValueError",
"exception_message": "bad input",
"stack": "... trimmed stack trace ...",
"frames": [
{"filename": "app.py", "lineno": 10, "function": "handle", "context_line": "..."}
]
}
}
}
Serialization respects exceptions_max_frames and exceptions_max_stack_chars from settings.
Redaction and serialization
Redactors (if enabled) run on the envelope after enrichment, before the sink.
When
serialize_in_flush=Trueand the sink supportswrite_serialized, the envelope is serialized once per entry in the flush path.
Where to see it
Default stdout sink emits JSON lines preserving the envelope structure. The
context,diagnostics, anddatafields remain nested.File/HTTP sinks receive the same envelope structure before serialization.