A lightweight, fully-typed Python logging toolkit — a thin wrapper on the standard logging library with extra wheels.
- Wildcard logger configuration — define a
"*"logger and have it applied to every registered logger. - Keyword-friendly logging — pass keyword arguments directly; they become
extrafields. - Advanced filters — whitelist, blocklist, and conditional attribute assignment via TOML config.
- Structured JSON output — rich exception context powered by structlog.
- Remote logging — send logs to an HTTP endpoint or Telegram chat out of the box.
- Fully typed — strict MyPy compliance with a
py.typedPEP 561 marker.
pip install thinlogconfig.toml:
[logging]
root = {level = "DEBUG", handlers = ["queue"]}
[logging.formatters]
json = {"()" = "thinlog.formatters.json.JsonFormatter", show_locals = false}
msg = {"()" = "thinlog.formatters.msg.MsgFormatter"}
[logging.handlers]
stream = {class = "logging.StreamHandler", level = "DEBUG", stream = "ext://sys.stderr", formatter = "msg"}
queue = {class = "logging.handlers.QueueHandler", handlers = ["stream"], formatter = "json", respect_handler_level = true}app.py:
import tomllib
from pathlib import Path
from thinlog import configure_logging
config = tomllib.loads(Path("config.toml").read_text())
logger = configure_logging("myapp", config["logging"])
logger.info("Hello from Thinlog!")
logger.warning("Something happened", user_id=42, ip="10.0.0.1")From any other location/library or file if you need a specific logger you can simply do:
from thinlog import get_logger
logger = get_logger("my_other_specific_logger", dict(more="data", we="can pass"))
# Rest of the file...A recommendation: Context(ctx) is good, use context everywhere, your app can have its own context, every request can have its own ctx so you can easily access your resources(e.g myapp logger) from ctx.
configure_logging is a plain function that returns a ready-to-use logger. It registers an atexit handler that automatically stops QueueHandler listeners and flushes handlers on interpreter exit.
Define a "*" logger in your config and it will be applied to all registered loggers. Use "merge": true on a specific logger to extend rather than replace the wildcard config.
[logging.loggers]
"*" = {level = "INFO", handlers = ["queue"]}
[logging.loggers.trace_log]
merge = true
handlers = ["trace_handler"]- WhitelistFilter — allow records matching name, message, or attribute patterns.
- BlocklistFilter — block matching records (inverse of whitelist).
- AssignerFilter — conditionally assign attributes to matching records without blocking any.
[logging.filters]
skip_noisy = {"()" = "thinlog.filters.blocklist.BlocklistFilter", by_name = ["urllib3", "httpx"]}- JsonHTTPHandler — send JSON logs via HTTP POST with per-record URL/header overrides.
- TelegramHandler — send logs to Telegram; auto-splits long messages into document attachments.
- CtxPrintHandler — print record context as JSON to stdout for development.
- JsonFormatter — full record as JSON with structured exception tracebacks.
- MsgFormatter — plain message string only.
- TelegramFormatter — HTML-formatted output with length-aware splitting.
Full documentation is available at minfrastructure.github.io/thinlog.
This module is fully typed and compatible with MyPy out of the box. Please open an issue for any suggestions or bugs.