# 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:

```json
{
  "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; `null` when 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 `null` when no correlation context is active; populated when set via `request_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.

```python
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:

```json
{
  "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=True` and the sink supports `write_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`, and `data` fields remain nested.
- File/HTTP sinks receive the same envelope structure before serialization.
