Skip to main content

klogr

Batteries-included structured logger for Python data/ML projects — built on Rich.

PyPI Downloads Python Publish License: MIT uv Ruff mypy jaxtyping Pydantic v2 Beartype PRs Welcome

  get_logger()
       |
       v
  +---------------------------+
  |        LoggingRich        |
  +---------------------------+
  | .info  .success  .warning |  -->  Rich-formatted console
  | .error .debug    .rule    |       with auto file:line prefix
  | .track .print    .info_json|
  +---------------------------+

  plus, from the same package:

    @lru_cache  +  DisableableLRUCache  -->  cached calls,
                                             with a disable hook for tests
    path_join / path_exists / path_open      -->  local + s3:// transparent
    get_elapsed_time(seconds)                -->  "01d : 01h : 12m : 03s"
    sha256sum(path) / get_cache_dir()        -->  stable on-disk caching

klogr is what you reach for when stdlib logging.basicConfig doesn't cut it and print() feels gross. It wraps Python's logging module with rich.logging.RichHandler pre-configured, adds stacklevel awareness so every line shows where it came from, and ships a handful of helpers (caching, paths, timing) that show up in every ML/data project.

Why use it

  • Drop-in get_logger() — module-scoped, cached, zero setup. No logging.basicConfig, no handler wiring.
  • Stacklevel-aware — every log line auto-prefixes with the caller's file:line. Stop hunting for which module shouted what.
  • logger.track() — wraps rich.progress.track() and works inside ThreadPoolExecutor / ProcessPoolExecutor without going silent.
  • logger.print() / logger.info_json() — pretty-print Pydantic models, dicts, JSON without piping through print(json.dumps(..., indent=2)).
  • @lru_cache with a disable hook — flip an env var to bypass caching globally (handy for tests).
  • Path helpers that handle S3 — path_join, path_exists, path_mkdir, etc. work whether you pass /tmp/foo or s3://bucket/foo.
  • get_elapsed_time(seconds) — formats float seconds as 00d : 01h : 12m : 03s.
  • One-liner dual output — with logger.to_file("run.log"): mirrors every log line to BOTH the Rich-formatted console AND the log file. Perfect for batch jobs.
  • Timed blocks — with logger.timed("train epoch"): prints entry + exit + elapsed, no time.perf_counter() boilerplate.
  • logger.exception("msg") — like stdlib logging.exception but the traceback is rendered by Rich.
  • logger.kv(epoch=12, lr=3e-4) — one-line structured key=value logging for training loops.

Install

uv add klogr
# or
pip install klogr

Requires Python ≥ 3.10. Runtime deps: rich, pydantic, python-dotenv, beartype, jaxtyping, natsort, sha256sum.

Quickstart

from klogr import get_logger

logger = get_logger()

logger.info("loaded %d samples", 1024)         # auto file:line prefix
logger.success("training converged")            # green check
logger.warning("validation loss plateaued")
logger.error("OOM on batch 42")

# Pretty-print structured data
logger.print({"epoch": 12, "lr": 3e-4, "loss": 0.21})

# Progress bar that works in parallel
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=8) as pool:
    for result in logger.track(pool.map(process, items), total=len(items), description="batch"):
        ...

What's in the box

Logger

from klogr import get_logger, LoggingRich, DEFAULT_VERBOSITY

logger = get_logger()                            # default verbosity
quiet  = get_logger({"info": False, "debug": False})  # custom

logger.info(msg)            # cyan
logger.success(msg)         # green check
logger.warning(msg)         # yellow
logger.error(msg)           # red
logger.rule(title)          # horizontal divider
logger.print(any_object)    # rich.print, soft-wrap aware
logger.info_json(json_str)  # syntax-colored JSON
logger.track(iter, total, description)  # progress bar

Log to console AND a file at the same time

from klogr import get_logger

logger = get_logger()

# Scoped to a block — auto-restores console-only on exit:
with logger.to_file("step.log"):
    logger.info("written to console AND step.log")
    logger.success("step done")
# back to console-only here

# Or set/unset manually if you need broader control:
logger.enable_dual_output("training.log")
logger.info("epoch=12")
logger.disable_dual_output()

The file gets the Rich-rendered output verbatim (colors stripped on the file side, preserved in the terminal). Check logger.is_file_enabled() to confirm dual output is on.

Time a block

with logger.timed("train epoch"):
    train_one_epoch()
# logs:  ▶ train epoch
#        ✓ train epoch — 00d : 00h : 12m : 03s

Structured key=value lines

logger.kv(epoch=12, lr=3e-4, loss=0.214)
# logs:  epoch=12 lr=0.0003 loss=0.214  (with rich coloring)

Log an exception with a Rich traceback

try:
    do_work()
except Exception:
    logger.exception("do_work failed")
# logs the message at ERROR, then a syntax-highlighted traceback

Caching

from klogr import lru_cache, DisableableLRUCache, get_cache_dir, sha256sum

@lru_cache(maxsize=128)
def expensive(key: str) -> bytes:
    ...

# Stable on-disk paths
cache_root = get_cache_dir()                          # XDG cache root
digest     = sha256sum("/path/to/file.bin")           # hex string

Path helpers (local + S3 transparent)

from klogr.path import (
    path_join, path_exists, path_mkdir, path_dirname,
    path_basename, path_glob, path_is_s3, path_open,
)

path_exists("/tmp/local.bin")       # True/False
path_exists("s3://bucket/key.bin")  # True/False (same call)
path_mkdir("/tmp/nested/dir", parents=True, exist_ok=True)
for p in path_glob("/data/*.jpg"):
    with path_open(p, "rb") as f:
        ...

Timing

from klogr import get_elapsed_time

print(get_elapsed_time(86400 + 3661))   # '01d : 01h : 01m : 01s'

Examples

Runnable scripts live in examples/:

uv run examples/01_basic_logger.py

Layout

klogr/
├── __init__.py     # public re-exports
├── logger.py       # LoggingRich, LoggingTable, get_logger
├── cache.py        # lru_cache, DisableableLRUCache, sha256sum, get_cache_dir
├── path/           # local + S3 path helpers
│   ├── __init__.py
│   ├── ops.py      # pure ops (join, dirname, basename, suffix, …)
│   ├── env.py      # path_dotenv, path_home, path_expanduser
│   ├── io.py       # mkdir, remove, copy, move, read/write
│   └── query.py    # exists, is_dir, glob, listdir, stat
└── time.py         # get_elapsed_time

Build & test

uv sync
.venv/bin/pre-commit run --all-files
.venv/bin/pytest
.venv/bin/mypy klogr/

License

MIT — see LICENSE.md.

Metadata

Release files for klogr 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for klogr 0.2.1
File Size Uploaded Provenance
klogr-0.2.1.tar.gz 33.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for klogr 0.2.1
File Interpreter ABI Platform
klogr-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 61.7 kB

Release files / klogr-0.2.1.tar.gz

Download URL klogr-0.2.1.tar.gz
Size 33.6 kB
Tags Source
SHA-256 checksum
How to use checksums
2054a53741bad30bd59940c3dece53757767e557c17a8340b9b7657ced801663
BLAKE2b-256 checksum
How to use checksums
cf980e1e3d15dcc9933d8291bac31206f32c1d58691380a82b47a76185e2020a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release files / klogr-0.2.1-py3-none-any.whl

Download URL klogr-0.2.1-py3-none-any.whl
Size 28.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
40f7ea752f7bb8f3a03478ec3ed7e0430af08706229d92a4e83b1cbe5dd97b2c
BLAKE2b-256 checksum
How to use checksums
6eea0cc039ad3b3a224b35795a80ae65aae82833dc6511f69fa944abef49481c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Security for klogr 0.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page