# Observability with Hermes

Kurier includes a built-in observability tool called **Hermes**. It is designed to provide clear, actionable logging and distributed tracing without adding complexity to your codebase.

## Table of Contents

- [1. Tracing with Correlation ID](#1-tracing-with-correlation-id)
- [2. Using Hermes for Logging](#2-using-hermes-for-logging)
- [3. Integration with Standard Logging](#3-integration-with-standard-logging)
- [Monitoring Best Practices](#monitoring-best-practices)

---

## 1. Tracing with Correlation ID

Every "Passenger" (message) in Kurier is automatically tagged with a **UUIDv7** `correlation_id` when it is published.

*   **Batching**: When you use `publish_batch_async()`, all messages in that batch share the same `correlation_id`, allowing you to trace the entire bulk operation as a single unit.
*   **Propagation**: The `correlation_id` is automatically extracted during consumption and made available to your handlers via the `context` dictionary.

---

## 2. Using Hermes for Logging

Hermes provides a simple, level-based logging interface. You can use it in your handlers to ensure your logs are consistent with the Kurier system.

```python
from kurier.hermes import Hermes

@Station.add_handler()
async def process_order(message: OrderCreated, *context):
    envelope = context[0] if context else {}
    cid = envelope['correlation_id']
    Hermes.info(f"Processing order {message.order_id} (CID: {cid})")
    
    if message.amount > 1000:
        Hermes.warn(f"High-value order detected! (CID: {envelope['correlation_id']})")
```

### Available Levels:
*   `Hermes.trace()`: Detailed debug information for development.
*   `Hermes.debug()`: General debugging info.
*   `Hermes.info()`: Key milestones in message flow.
*   `Hermes.warn()`: Non-critical issues or unexpected events.
*   `Hermes.error()`: Recoverable errors (e.g., broker disconnect).
*   `Hermes.fatal()`: Critical failures that prevent operation.

---

## 3. Integration with Standard Logging

Hermes is built to be a thin wrapper. By default, it does not emit anything for `TRACE`, `DEBUG`, `INFO`, or `WARN` unless you register a consumer. `ERROR` and `FATAL` fall back to `sys.stderr` when no consumer is configured.

In a production environment, you can route Hermes output into your application's central logger by registering consumers with `Hermes.set_consumer(...)`:

```python
from kurier.hermes import Hermes
from kurier.hermes.letter_levels import LetterLevel

Hermes.set_consumer(LetterLevel.INFO, lambda msg: print(msg, end=""))
Hermes.set_consumer(LetterLevel.ERROR, lambda msg: print(msg, end=""))
```

---

## Monitoring Best Practices

1.  **Always Log the Correlation ID**: When logging outside of Kurier's built-in hooks, always include the `correlation_id` to ensure you can trace the message across services.
2.  **Use Trace for Development**: Kurier's transports generate detailed `trace` logs. Enable these in development to see the raw wire-format payloads.
3.  **Monitor Fatal Logs**: Errors logged at the `fatal` level typically indicate that a "Line" has crashed or a handler has an unhandled exception that stops the consumer.
