log_setup/README.md
2026-06-27 20:21:02 -04:00

106 lines
4.5 KiB
Markdown

# log_setup
Stdlib, sync, **zero-dependency** logging setup an application calls **once** at its
entry point: a live `run.log`, rotation (daily / size / on-start), gzip of rolled
files, retention, console output, and a consistent `time | module | level | message`
format.
It **configures** logging (handlers, rotation, format) — which reusable libraries here
must never do. That's fine because `log_setup` is the *application's* entry-point
setup, not library-internal config. Libraries still only `logging.getLogger(__name__)`
and emit; their records flow into the handlers `log_setup` wired.
## Install
```
log_setup @ git+ssh://git@git.rethinkstudios.io/rethink-public/log_setup.git@v0.1.0
```
No dependencies — stdlib only.
## Quick start
```python
import logging
from log_setup import setup_logging
setup_logging(name="run", level="INFO") # daily rotation, logs/ dir, gzip (file only)
log = logging.getLogger(__name__)
log.info("started") # -> ./run.log (add console=True for stdout too)
```
Call it once, at the app's entry point — before the rest of the app runs. Every module
(yours and the libraries you import) then just does `logging.getLogger(__name__)` and
emits; the records land in the configured root.
## What you get
- **Live file** at a stable path: `./run.log` — always `tail -f run.log`, no dated name
to chase. Rolled/compressed copies go into `log_dir` (default `logs/`).
- **Format:** `2026-06-27 19:55:05 | module.name | INFO | message`. `%(name)s` is the
`getLogger` name each module used, so you see which lib/module logged.
- **Rotation** (`rotate=`):
- `"daily"` (default) — rolls at midnight, dated name into `log_dir`, keeps
`backup_count` days.
- `"size"` — rolls at `max_bytes`, numbered backups in `log_dir`.
- `"on_start"` — on startup, moves an existing `run.log` into `log_dir`
(`run.<timestamp>.log[.gz]`) and starts fresh; prunes to `backup_count`.
- `None` — single file, no rotation.
- **compress=True** (default) gzips each rolled file (`run.log.2026-06-27.gz`).
- **Retention** = `backup_count` (default 14) for every mode.
- **console=True** (off by default) also logs to stdout in the same format — opt in when
you want live terminal output alongside the file.
## Signature
```python
setup_logging(
name="run", # base -> run.log (the live file at cwd)
log_dir="logs", # rotated/compressed copies live here (created if absent)
level="INFO", # root level (str name or logging constant)
rotate="daily", # "daily" | "size" | "on_start" | None
backup_count=14, # rotated files to keep (older auto-deleted)
max_bytes=10_000_000, # only for rotate="size"
compress=True, # gzip rolled files
console=False, # also log to stdout (off by default; opt in)
queue=False, # route through a background QueueListener (async-friendly)
fmt=None, # override the format string
datefmt=None, # override the date format
) -> logging.Logger # returns the configured root logger
```
## Async-friendly (`queue=True`)
For async-heavy apps, `queue=True` routes records through a stdlib `QueueHandler` to a
background `QueueListener` that owns the file/console handlers, so the event loop never
blocks on file I/O. The API stays sync (`log.info()` as usual); the queue is internal.
The listener is stopped (and flushed) cleanly at process exit, so no records are lost.
```python
setup_logging(name="run", queue=True)
```
## Safety
- **Idempotent:** calling `setup_logging` again clears only the handlers it added (no
duplicate lines) and leaves handlers your app added itself alone.
- **Never crashes the app over logging:** if `log_dir` isn't writable, it falls back to
console-only with a warning instead of raising.
## Scope — what this is NOT
`log_setup` produces clean, rotating, compressed, retention-managed, consistently
formatted **files**. It does **not** ship logs anywhere — no Loki/ELK/syslog/network
handlers. Getting files to a backend is a separate concern (e.g. Promtail tails
`run.log` → Loki → Grafana panels + alerting). Keeping shipping out means the log
backend can change without touching any app, and the consistent format here is what
makes downstream parsing and alerting easy.
Also out of v0.1.0 (possible later additions): structured/JSON logging, color
formatting, per-logger filters, remote handlers.
## Versioning
Tagged `vX.Y.Z`. Pin the tag.