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.
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, ormiddle, 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, andlog_io, each accepting anidentifieror a pre-bound logger. - Small and safe to depend on. Runtime deps are just
loguruandcolorama; 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
Metadata
Release files for loggerplusplus 1.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| loggerplusplus-1.2.0.tar.gz | 58.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| loggerplusplus-1.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 126.0 kB
Release files / loggerplusplus-1.2.0.tar.gz
| Download URL | loggerplusplus-1.2.0.tar.gz |
|---|---|
| Size | 58.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1ff7f65eba6cb09645dcac796b8f8e786b14aecc297f2b55ddb01429495c7ce2
|
|
BLAKE2b-256 checksum How to use checksums |
ce275c19d41dfa74c738faf9c9218831feb7b478584a9b254f3dac81a2042582
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / loggerplusplus-1.2.0-py3-none-any.whl
| Download URL | loggerplusplus-1.2.0-py3-none-any.whl |
|---|---|
| Size | 67.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
592dfffa4afeae0a52fcf6e8c5b9a51481e2d1114ea4384a570a7b5007e78cd3
|
|
BLAKE2b-256 checksum How to use checksums |
e3e06c538dc6540d78a848b34f1ad6c6dd4d25f98dda0b382fe6aafae855e116
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency log