Skip to content

Repository files navigation

LoggerPlusPlus

ALIGNED, READABLE LOGGING

Enhanced logging for Python, built on Loguru — keep Loguru's simple, powerful API and add auto-aligned identifier columns, width-aware truncation, ready-made colorized formats, and convenience decorators. A small, dependency-light library meant to be shared across every service in a stack.

PyPI Python CI CodeQL License

Configure a sink with an auto-width format, bind an identifier, and log. Identifier columns align themselves to the longest value seen, overflow is truncated where you ask, and the whole API stays a transparent, drop-in proxy over Loguru.


Why LoggerPlusPlus

  • Columns that align themselves. Write {identifier:<auto} in a format and every identifier is padded to the widest one seen so far — no manual widths, no jitter between lines.
  • Truncation you control. Cap a field with [width] and choose the side: left, right, or middle, each with an ellipsis.
  • Ready-made formats, resolved by name. Five colorized layouts (Classic, Short, Ops, Debug, Minimal) that a service can select from configuration.
  • A transparent Loguru proxy. Everything Loguru exposes (sinks, levels, filters, backtraces, bind, contextualize, ...) remains available; a few names are enhanced, none are hidden.
  • Convenience decorators. catch, opt, log_timing, and log_io, each accepting an identifier or a pre-bound logger.
  • Small and safe to depend on. Runtime deps are just loguru and colorama; the package configures no sinks at import time and never prints.

Installation

pip install loggerplusplus
# or
poetry add loggerplusplus

Requires Python 3.9+. Big ASCII-art banners (Banner.figlet) need the optional extra:

pip install 'loggerplusplus[banners]'   # adds pyfiglet; box/rule banners work without it

Quickstart

import sys

from loggerplusplus import add, remove, logger

remove()  # drop Loguru's default handler first
add(
    sink=sys.stderr,
    level="DEBUG",
    format=(
        "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
        "<level>{level.name:<8}</level> | "
        "[<blue>{identifier:<auto[18~middle]}</blue>] | "
        "<level>{message}</level>"
    ),
)

logger.bind(identifier="MAIN").info("Hello from main")
2025-09-25 14:03:12.345 | INFO     | [MAIN] | Hello from main

logger is the enhanced, ready-to-use singleton (a drop-in for Loguru's logger). The same API is also available on the loggerplusplus singleton and as the top-level functions add / remove / catch / opt / log_timing / log_io.


Auto-width alignment

{identifier:<auto} is not valid Loguru syntax on its own — LoggerPlusPlus rewrites it. The auto width tracks the longest value observed for that field over the process lifetime and pads every line to it, so a column never shrinks and never jitters. LoggerClass (below) pre-registers its identifier so alignment is correct from the very first line.

[MAIN]           | starting
[WORKER]         | working
[LONG-SERVICE-A] | columns grew, and stay aligned

The token grammar is {field:<align><width>[cap~trunc]}:

Part Values Meaning
align < > ^ left / right / center (default <)
width auto or an integer grow-to-fit, or a fixed width
cap integer, in [...] maximum width
trunc left right middle which side to cut when overflowing, with

field may be a record attribute (level.name), a dotted path, or extra[key].


Truncation

"{identifier:<auto[18~middle]}"   # grow to fit, but never wider than 18, cut in the middle
"{name:<20~right}"                 # fixed width 20, cut the tail
"{extra[service]:>auto[12~left]}"  # right-aligned, capped at 12, cut the head

VeryLongServiceName capped at 12 with ~middle renders as VeryL…Name.


Ready-made formats

Each format is a subclass of str, so an instance is a format string and can be passed straight to add(format=...). Formats are resolved by name, which is convenient for configuration:

import sys

from loggerplusplus import loggerplusplus, formats

loggerplusplus.remove()
loggerplusplus.add(sink=sys.stdout, level="DEBUG", format=formats.ShortFormat())

# Select one by name (e.g. from an env var), with a safe fallback:
chosen = "OpsFormat"
loggerplusplus.add(sink=sys.stdout, format=getattr(formats, chosen, formats.DebugFormat)())
Format Contents
ClassicFormat time, level, identifier, source name:line, message
ShortFormat time, level, identifier, message
OpsFormat time, level, identifier, process/thread, message
DebugFormat time, level, identifier, process/thread, source name:line, message
MinimalFormat identifier, message
PlainFormat Short layout, uncolored by default — for file sinks
FileFormat Classic layout with name:line, uncolored by default — for file sinks

Every format accepts overrides such as colorized=False (plain output for file sinks) and per-field widths (level_width=, identifier_width=, ...). See docs/FORMATS.md.


LoggerClass

Any class can get a bound self.logger whose identifier defaults to the class name:

from loggerplusplus import LoggerClass

class Service(LoggerClass):
    def run(self):
        self.logger.info("Service is running")

Service().run()
Service(identifier="Custom").run()   # explicit identifier

Decorators

from loggerplusplus import catch, log_timing, log_io

@catch(identifier="WORKER", level="ERROR")
def risky():
    raise RuntimeError("Boom!")

@log_timing(identifier="TASK", exit_message="Finished {func} in {duration:.2f}s")
@log_io(identifier="CALC", log_args=True, log_return=True)
def compute(a, b):
    return a + b

catch also works as a context manager; opt accepts either an identifier or a pre-bound logger. See docs/USAGE.md.


Banners & message transforms

Every level method takes an optional transform (any str -> str) applied to the message before it is emitted, and an optional raw (emit without the timestamp/level prefix). Banner bundles ready-made transforms — big ASCII art, Unicode boxes, rules — and transforms exposes the general concept.

from loggerplusplus import logger, Banner, transforms

logger.info("Config loaded", transform=Banner.box())          # zero-dependency Unicode box
logger.info("SECTION", transform=Banner.rule())               # ──────── SECTION ────────
logger.info("DEPLOY", transform=Banner.figlet(font="slant"), raw=True)   # big art (extra)
logger.info("{user} in", user="alice", transform=str.upper)   # ALICE IN — any str->str works
logger.warning("done", transform=transforms.chain(str.upper, Banner.box(double=True)))

Banner.figlet needs the banners extra; Banner.box/Banner.rule and the transform mechanism itself are dependency-free. The keywords work on the singleton, on bind() loggers and on LoggerClass.logger alike. See docs/USAGE.md.


Architecture

add(format=str)
   ├─ parser.prepare_auto_format()   rewrite {identifier:<auto} into {extra[__lp_auto_N__]}
   ├─ runtime.compose_filter()       loguru calls this once per record, before formatting —
   │                                 the injection point for the padded/truncated value
   ├─ registry (_AUTO)               thread-safe max-observed width per field (monotonic)
   └─ loguru formatting

A fuller explanation is in docs/REFERENCE.md.


Public API

from loggerplusplus import (
    loggerplusplus,   # enhanced singleton (drop-in for loguru's logger)
    logger,           # alias of the singleton
    LoggerPlusPlus,   # the proxy class
    LoggerClass,      # mixin providing self.logger
    formats,          # ClassicFormat, ShortFormat, OpsFormat, DebugFormat, MinimalFormat, PlainFormat, FileFormat
    add, remove,      # sink management
    catch, opt,       # loguru helpers with identifier binding
    log_timing, log_io,  # timing / I/O decorators
    Banner,           # banner transform factories: figlet / box / rule / preset
    transforms,       # message-transform toolkit (Transform, chain, indent, upper, lower)
    __version__,
)

Level methods also accept transform= (a str -> str) and raw= keywords — see Banners & message transforms.

The public surface is a stability contract: names are added, never renamed or removed without a major version bump. Full signatures in docs/REFERENCE.md.


Documentation

Document Contents
docs/INSTALL.md Installation, requirements, and version support
docs/USAGE.md Task-oriented guide: sinks, formats, decorators
docs/FORMATS.md The five formats and the auto-width token grammar
docs/REFERENCE.md API reference and the auto-width pipeline internals
CHANGELOG.md Release history (maintained by release-please)
CONTRIBUTING.md Development workflow, the CI gate, and releasing

Project layout

src/loggerplusplus/
├── __init__.py        public API
├── proxy.py           LoggerPlusPlus — transparent proxy over loguru.logger
├── api.py             add() — wires the format parser into loguru.add
├── parser.py          auto-width token grammar
├── runtime.py         per-record width / truncation computation
├── registry.py        thread-safe max-observed width
├── logger_class.py    LoggerClass mixin
├── decorators.py      catch · opt · log_timing · log_io
├── transform_proxy.py transform=/raw= keyword on level methods · TransformProxy
├── transforms.py      Transform concept · chain/indent/upper/lower · re-exports Banner
├── banners.py         Banner — figlet / box / rule / preset transform factories
├── context.py         bind_context · new_id · otel_context (correlation, optional OTel)
├── structured.py      add_json — structured/JSON sink
├── intercept.py       intercept_std_logging · InterceptHandler (stdlib bridge)
├── bootstrap.py       setup · configure_from_env
├── width.py           registry controls · testing.py — capture helper
└── formats/           BaseFormat + Classic · Short · Ops · Debug · Minimal · Plain · File; Theme

Development

poetry install
poetry run black src tests test_module
poetry run ruff check src tests test_module
poetry run mypy src
poetry run pytest            # 100% coverage; the CI gate is 95%

The CI matrix runs the suite on Python 3.9–3.13 across Linux, macOS, and Windows. See CONTRIBUTING.md.


License

LoggerPlusPlus is licensed under the GNU General Public License v3.0 — see LICENSE. It builds on top of Loguru (MIT).

Author

Created and maintained by Florian BARRE. Website · LinkedIn · GitHub

About

No description, website, or topics provided.

Resources

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages