Lightweight, zero-dependency logging wrapper with structured output, context injection, and file rotation
Project description
logtie
Lightweight, zero-dependency logging wrapper for Python. Structured output, context injection, colored terminal, JSON files, and file rotation — all configured in one call.
import logtie.log as log
log.configure(
name="myapp",
level=log.DEBUG,
stdout=log.Stdout(
fmt=log.LogFmt("[timestamp] [level] - [message]"),
datefmt=log.TimeFmt("[year]-[month]-[day] [hour]:[minute]:[second]"),
colored=True,
),
file=log.File(path="app.log", datefmt="iso", json=True),
)
log.bind(request_id="abc-123")
log.info("user logged in", user_id=42)
log.warning("slow query", duration_ms=340)
Installation
pip install logtie
Requires Python 3.14+. No external dependencies.
Quick start
1. Configure once at startup
import logtie.log as log
log.configure(
name="myapp", # logger name
level=log.DEBUG, # global minimum level
stdout=True, # terminal output with defaults
)
configure() accepts True / a plain path string as shorthand for either handler with default settings. Pass a Stdout or File instance to customise.
2. Log
log.debug("connecting to database")
log.info("server started", port=8080)
log.warning("high memory usage", used_mb=1800)
log.error("request failed", status=500, path="/checkout")
log.critical("service unreachable")
try:
...
except Exception:
log.exception("unexpected error") # includes full traceback
Keyword arguments are attached to the log record as extra fields — they appear in JSON output automatically.
3. Context injection
Inject fields once; they're included in every subsequent record until removed.
log.bind(request_id="req-001", user_id=42)
log.info("request started") # → includes request_id + user_id
log.debug("validating payload") # → same
log.unbind("user_id") # remove one key
log.clear_binds() # remove all
Context is backed by contextvars, so it's thread-safe and asyncio-safe — bind() inside an asyncio task only affects that task.
Handlers
Terminal (Stdout)
log.Stdout(
fmt=log.LogFmt("[timestamp] [level] - [message]"),
datefmt=log.TimeFmt("[year]-[month]-[day] [hour]:[minute]:[second].[ms]"),
colored=True,
colors=log.Colors(
debug=log.AnsiColor.BRIGHT_BLACK,
info="#00D4AA", # hex truecolor
warning="#FFA500",
),
level=log.DEBUG,
only=[log.DEBUG, log.INFO], # restrict to specific levels
)
File (File)
log.File(
path="app.log",
datefmt="iso", # ISO 8601 timestamps
json=True, # one JSON object per line
level=log.WARNING,
rotate_size=5 * 1024 * 1024, # rotate at 5 MB
backup_count=7,
)
File rotation
| Parameter | Behaviour |
|---|---|
rotate_size=N |
Rotate when file reaches N bytes |
rotate_lines=N |
Rotate when file reaches N lines |
backup_count=N |
Number of backup files to keep (default 5) |
Both use the same rename scheme: app.log → app.log.1 → app.log.2 …
JSON field mapping
By default JSON records use:
asctime → "timestamp", name → "logger", levelname → "level",
message → "message", funcName → "function", lineno → "line"
Override with json_fmt:
log.File(
path="app.log",
json=True,
json_fmt={"asctime": "ts", "levelname": "severity", "message": "msg"},
)
Any extra kwargs passed to log calls are added to the JSON object as-is.
Format tokens
LogFmt — log line layout:
| Token | Value |
|---|---|
[timestamp] |
formatted timestamp |
[level] |
level name (INFO, WARNING …) |
[message] |
log body |
[name] |
logger name |
[function] |
function name |
[line] |
line number |
[module] |
module name |
[path] |
full file path |
[process] |
process ID |
[thread] |
thread ID |
TimeFmt — timestamp format:
| Token | Value |
|---|---|
[year] |
4-digit year |
[month] |
2-digit month |
[day] |
2-digit day |
[hour] |
hour (24 h) |
[minute] |
minute |
[second] |
second |
[ms] |
milliseconds |
[tz] |
timezone offset |
Pass datefmt="iso" to either handler for ISO 8601 output instead.
Level routing
Send different levels to different destinations:
log.configure(
name="myapp",
level=log.DEBUG,
stdout=log.Stdout(only=[log.DEBUG, log.INFO]), # terminal: verbose
file=log.File(path="alerts.log", only=[log.WARNING, log.ERROR, log.CRITICAL]),
)
Asyncio
logtie is asyncio-safe out of the box. Use queue=True to offload handler I/O to a background thread and keep the event loop unblocked:
log.configure(name="myapp", level=log.DEBUG, stdout=True, queue=True)
bind() is task-local — context set inside one coroutine doesn't leak into sibling tasks.
Examples
The examples/ directory has runnable scripts covering every feature.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file logtie-0.1.0.tar.gz.
File metadata
- Download URL: logtie-0.1.0.tar.gz
- Upload date:
- Size: 10.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
35bf078779cc6bf2bd184ef3de3050ed153a20472389c097e919b177ee67375b
|
|
| MD5 |
b7c323e236891d7c80d0b5675e17509d
|
|
| BLAKE2b-256 |
7cda1ad824cb60c6b1af1ed5a837380b7d6c2aae5b4b377f141512e1c8089195
|
File details
Details for the file logtie-0.1.0-py3-none-any.whl.
File metadata
- Download URL: logtie-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
14a6bdf2bb4e17fd921e8989ac54fb56ffc1017c5ca18561789f8eb680fd3e28
|
|
| MD5 |
5c4ef9de872289cfd08d8812ffe5fa88
|
|
| BLAKE2b-256 |
61ee38a63a6c6418b83b4298734025dccac3a6eff2fe21c26b51ace7b3ca9dd8
|