# snaplog

Zero-config structured logging for Python. One import, structured JSON output everywhere.

**Zero dependencies. Auto-context. Function tracing. JSON by default.**

## Install

```bash
pip install snaplog
```

## Quick Start

```python
from snaplog import get_logger

log = get_logger()

log.info("Order processed", order_id="abc", total=49.99)
# → {"ts":"2026-03-24T21:08:00.123Z","level":"info","msg":"Order processed","order_id":"abc","total":49.99,"logger":"root"}
```

That's it. No config files. No handlers. No formatters. Just log.

## Features

- 🚀 **Zero config** — `get_logger()` and go
- 📝 **Structured JSON** — every log is a JSON object
- 🎯 **Auto-context** — function name, file, line number captured automatically
- 🔍 **Function tracing** — `@log.trace` decorator adds timing
- 🏷️ **Request context** — `with log.context(user_id=...)` adds fields to all logs
- 👁️ **Human-readable mode** — for development
- 🔌 **Stdlib integration** — bridges to Python's `logging` module
- 🧵 **Thread-safe** — context propagates correctly
- 📦 **Zero dependencies** — pure Python

## API

### Basic Logging

```python
from snaplog import get_logger

log = get_logger()
log.debug("Verbose details", step=1)
log.info("Something happened", user_id="u1")
log.warning("Careful!", retries=3)
log.error("Failed to connect", host="db1", error_code=503)
log.critical("System down", component="database")
```

### Auto-Context

```python
# Automatically includes function name, file, and line
def process_order(order_id):
    log.info("Processing", order_id=order_id)
    # → {..., "func":"process_order", "file":"orders.py", "line":42}
```

### Function Tracing

```python
@log.trace
def fetch_user(user_id):
    return db.get(user_id)
# → {"level":"trace","msg":"enter: fetch_user","func":"fetch_user","args":{"user_id":"u1"}}
# → {"level":"trace","msg":"exit: fetch_user","func":"fetch_user","elapsed_ms":12.3,"result":"ok"}
```

### Request Context

```python
with log.context(request_id="r1", user_id="u42"):
    log.info("Processing request")  # includes request_id + user_id
    call_service()
    log.info("Done")  # still includes them
# Context automatically removed after `with` block
```

### Human-Readable Output

```python
from snaplog import get_logger, HumanFormatter

log = get_logger(formatter=HumanFormatter())
log.info("Hello", name="world")
# → 2026-03-24 21:08:00 [INFO] Hello  name=world
```

### Min Level

```python
log = get_logger(min_level="warning")  # debug, info suppressed
```

### Stdlib Integration

```python
import logging
from snaplog import SnaplogHandler

handler = SnaplogHandler()
logging.root.addHandler(handler)
logging.info("This goes through snaplog too")
```

### Custom Output

```python
# Write to a file
import sys
log = get_logger(output=open("app.log", "a"))

# Custom output function
def my_output(record_dict):
    requests.post("https://logs.example.com", json=record_dict)

log = get_logger(output=my_output)
```

## Output Format

```json
{
  "ts": "2026-03-24T21:08:00.123Z",
  "level": "info",
  "msg": "Order processed",
  "logger": "root",
  "func": "process_order",
  "file": "orders.py",
  "line": 42,
  "order_id": "abc",
  "total": 49.99
}
```

## License

MIT
