# Logging — Flight Recorder & Logging Conventions

## Overview

This module provides the FlightRecorder — a context manager that captures all log output during a handler's `process()` call and attaches it to `AdapterResponse.execution_logs`. Handlers are **completely unaware** of the flight recorder — they use standard Python logging and the framework handles capture automatically.

## Concepts

### Flight Recorder

The flight recorder attaches to the Python logging system as a custom handler during each `process()` call. Every log emitted by the handler (and any code it calls) is captured as a structured dict and stored in memory. When `process()` completes — whether successfully or via exception — the captured logs are attached to the `AdapterResponse`.

This happens transparently at the dispatch level (see `app.md`). Handler code never creates, starts, or stops a flight recorder.

### Log Destination

Captured logs travel this path:

1. Handler emits log → Python logging system
2. Flight recorder captures log entry as structured dict
3. Logs attached to `AdapterResponse.execution_logs`
4. Core receives response, stores logs in `FileLog` table with `from_adapter=True`
5. Logs visible in Core's file processing history for diagnostics

### Entry Limit

The flight recorder retains a maximum of **5,000 log entries** per request (configurable). When the limit is exceeded:

1. The oldest entries are dropped (most recent are preserved)
2. A truncation marker is appended:
   ```json
   {"truncated": true, "dropped_count": 5, "message": "Log buffer exceeded limit, oldest entries dropped", "level": "WARNING"}
   ```

### Exception Handling

When a handler raises an unhandled exception:

- The full traceback is logged **inside** the flight recorder (visible in `execution_logs`)
- The framework returns `AdapterResponse(status="failed")` with a safe error message in `failure_reason`
- No stack traces leak in the response — only the exception type and message
- Logs emitted **before** the crash are still captured and attached

## Patterns

### Handler Logging Conventions

| Convention | Example |
|-----------|---------|
| Logger creation | `logger = logging.getLogger(__name__)` |
| Structured context | `logger.info("Sent to ERP", extra={"erp_response_code": 200})` |
| No print statements | `print()` bypasses the flight recorder entirely |
| No credential logging | Logs are sent to Core — never log secrets |

### Log Levels

| Level | Use For |
|-------|---------|
| `DEBUG` | Detailed diagnostics (field values, intermediate calculations) |
| `INFO` | Key processing steps (started, validated, sent, completed) |
| `WARNING` | Recoverable issues (missing optional field, fallback used) |
| `ERROR` | Failures that result in `StatusEnum.FAILED` response |

### Log Entry Format

Each captured entry is a dict:

| Key | Type | Description |
|-----|------|-------------|
| `timestamp` | `str` | ISO 8601 UTC timestamp |
| `level` | `str` | Log level name (DEBUG, INFO, WARNING, ERROR, CRITICAL) |
| `logger_name` | `str` | Logger name (typically module path via `__name__`) |
| `message` | `str` | Formatted log message |
| `context` | `dict` or `None` | Extra data from `extra={}` parameter |

## Guidance

### Do

- Use `logging.getLogger(__name__)` — the flight recorder captures from all loggers
- Use `extra={}` for structured data that aids debugging
- Log key decision points: what was validated, what was sent, what was received
- Let unexpected exceptions propagate — the framework handles them safely

### Don't

- Don't use `print()` — it bypasses the flight recorder
- Don't log credentials, tokens, or secrets — logs are transmitted to Core
- Don't wrap `process()` in a catch-all `try/except` — it hides errors from the flight recorder
- Don't create your own `FlightRecorder` instance — the framework manages this
- Don't use string interpolation for complex objects — use `extra={}` for structured data

## Cross-References

- **API reference**: See docstrings in `flight_recorder.py` for `FlightRecorder`, `FlightRecorderHandler`, and `execute_with_flight_recorder()` parameters and behavior
- **Dispatch integration**: See `app.md` for how the dispatcher wraps handler calls in the flight recorder
- **Handler conventions**: See `handlers.md` for the error handling contract and recommended flow
- **Core-side storage**: Core's `FileAdapterService._store_adapter_execution_logs()` reads `execution_logs` and creates `FileLog` entries with `from_adapter=True`
