# Exception logging in FastAPI with request_id + structured context

When your FastAPI app crashes, you need to know which request caused it. Without proper context, exception logs become a pile of stack traces with no way to trace them back to specific user actions.

## The Problem: Lost Context in Errors

Default FastAPI exception handling logs the stack trace but loses the request context:

```python
from fastapi import FastAPI

app = FastAPI()

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    # This exception loses all request context
    raise ValueError(f"User {user_id} not found")
```

When this crashes, you see:

```
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "...", line 42, in __call__
    ...
ValueError: User 123 not found
```

**What's missing:**

- Which request triggered this error?
- What was the URL path?
- Who made the request?
- How do I correlate this with other logs from the same request?

## The Solution: Automatic Context Preservation

fapilog's middleware automatically preserves request context in error logs:

```python
from fastapi import FastAPI
from fapilog.fastapi import FastAPIBuilder

app = FastAPI(
    lifespan=FastAPIBuilder()
        .with_preset("fastapi")
        .build()
)

@app.get("/users/{user_id}")
async def get_user(user_id: int):
    raise ValueError(f"User {user_id} not found")
```

Now when the same exception occurs, fapilog logs:

```json
{
  "timestamp": "2026-01-21T10:30:00.123Z",
  "level": "ERROR",
  "message": "request_failed",
  "request_id": "550e8400-e29b-41d4-a716-446655440000",
  "path": "/users/123",
  "method": "GET",
  "status_code": 500,
  "error_type": "ValueError",
  "error": "User 123 not found",
  "latency_ms": 12.5
}
```

Every field you need to debug the issue is right there.

## Custom Exception Handlers

For custom exception handling, use `get_request_logger` to maintain context:

```python
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from fapilog.fastapi import FastAPIBuilder, get_request_logger

app = FastAPI(
    lifespan=FastAPIBuilder()
        .with_preset("fastapi")
        .build()
)

class UserNotFoundError(Exception):
    def __init__(self, user_id: int):
        self.user_id = user_id
        super().__init__(f"User {user_id} not found")

@app.exception_handler(UserNotFoundError)
async def handle_user_not_found(request: Request, exc: UserNotFoundError):
    logger = await get_request_logger(request)
    await logger.warning(
        "user_not_found",
        user_id=exc.user_id,
        path=request.url.path,
    )
    return JSONResponse(
        status_code=404,
        content={"error": "User not found", "user_id": exc.user_id},
    )
```

The logger automatically includes the `request_id` from the current request context.

## Structured Error Fields

When fapilog captures an exception, it includes these structured fields:

| Field | Description |
|-------|-------------|
| `error_type` | Exception class name (e.g., `ValueError`) |
| `error` | Exception message (`str(exception)`) |
| `request_id` | Correlation ID for the request |
| `path` | Request URL path |
| `method` | HTTP method (GET, POST, etc.) |
| `status_code` | HTTP response status |
| `latency_ms` | Request duration in milliseconds |

For full stack trace capture, pass `exc_info=True`:

```python
try:
    process_payment(order)
except PaymentError as e:
    await logger.error(
        "payment_failed",
        order_id=order.id,
        exc_info=True,  # Captures full traceback
    )
    raise
```

This adds the `exception` field with detailed traceback info:

```json
{
  "level": "ERROR",
  "message": "payment_failed",
  "request_id": "abc-123",
  "order_id": "order-456",
  "exception": {
    "error.type": "PaymentError",
    "error.message": "Card declined",
    "error.stack": "Traceback (most recent call last):\n  ...",
    "error.frames": [
      {"file": "payment.py", "line": 42, "function": "charge", "code": "..."}
    ]
  }
}
```

## Going Deeper

- [FastAPI request_id Logging](fastapi-request-id-logging.md) - How request context propagates
- [Context Binding Reference](../api-reference/context-binding.md) - Manual context management
- [Context Enrichment](../user-guide/context-enrichment.md) - How enrichers work
- [Why Fapilog?](../why-fapilog.md) - How fapilog compares to other logging libraries
