# Example: JSON Output

Use structured JSON logs when logs are parsed by machines, indexed by platforms, or queried for metrics and alerting.

## Goal

Configure JSON output with `setup_logging(format="json")`, emit structured fields, and parse each line safely.

## Baseline Setup

```python
import logging
from azure_functions_logging import get_logger, setup_logging

setup_logging(level=logging.INFO, format="json")
logger = get_logger("orders")

logger.info("service started", service="orders", environment="prod")
```

## Sample JSON Line

A single log event is emitted as one JSON object per line:

```json
{"timestamp":"2026-03-14T10:20:30.123456+00:00","level":"INFO","logger":"orders","message":"service started","invocation_id":null,"function_name":null,"trace_id":null,"cold_start":null,"exception":null,"extra":{"service":"orders","environment":"prod"}}
```

## Field Breakdown

Top-level fields generated by formatter:

- `timestamp`
- `level`
- `logger`
- `message`
- `invocation_id`
- `function_name`
- `trace_id`
- `cold_start`
- `exception`
- `extra`

Use `extra` for application-specific dimensions such as tenant, order ID, and operation type.

## Logging Structured Data

```python
logger.info(
    "order accepted",
    order_id="ord-1001",
    tenant_id="tenant-a",
    amount=149.5,
    currency="USD",
)

logger.warning(
    "payment retry",
    order_id="ord-1001",
    attempt=2,
    gateway="stripe",
)
```

## Parsing JSON Logs in Python

```python
import json

line = '{"timestamp":"...","level":"INFO","logger":"orders","message":"order accepted","invocation_id":null,"function_name":null,"trace_id":null,"cold_start":null,"exception":null,"extra":{"order_id":"ord-1001"}}'

record = json.loads(line)

print(record["level"])
print(record["message"])
print(record["extra"].get("order_id"))
```

## Azure Function Usage Pattern

```python
import azure.functions as func
from azure_functions_logging import get_logger, inject_context, setup_logging

setup_logging(format="json")
logger = get_logger(__name__)

app = func.FunctionApp()


@app.route(route="orders")
def create_order(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    inject_context(context)
    logger.info("request received", method=req.method, route="/orders")
    return func.HttpResponse("ok", status_code=200)
```

With context injection, invocation fields are populated for every event in this request context.

## Production Query Patterns

Typical query dimensions from JSON output:

- `level` for error rates.
- `function_name` for function-specific dashboards.
- `cold_start` for startup impact monitoring.
- `extra.tenant_id` for multi-tenant analysis.
- `extra.order_id` for incident correlation.

## Reliability Guidelines

- Keep one JSON object per line.
- Keep message concise; push dimensions to structured keys.
- Use stable field names over time.
- Avoid deeply nested extra payloads unless required.

## Error Logging in JSON

```python
try:
    1 / 0
except ZeroDivisionError:
    logger.exception("math failure", operation="division", request_id="r-1")
```

This produces `exception` text and preserves `extra` context.

## Migration Strategy from Color to JSON

1. Start locally with `format="color"`.
2. Add structured keys to log calls.
3. Switch non-local tiers to `format="json"`.
4. Validate parser compatibility in staging.
5. Roll out dashboards and alerts using structured fields.

## Quick Validation Script

```python
import json
import logging
from azure_functions_logging import get_logger, setup_logging

setup_logging(level=logging.INFO, format="json")
logger = get_logger("validation")
logger.info("json check", run_id="run-20260314")

# Validate line shape in your sink by loading one event as JSON.
```

## Related Docs

- [Configuration](../configuration.md)
- [Usage Guide](../usage.md)
- [Troubleshooting](../troubleshooting.md)
- [API Reference](../api.md)
