- Shared singleton logger with per-level colorized output.
once
helpers prevent duplicate log spam automatically.- Stackable progress bars that stay anchored while your logs flow freely.
- Built-in styling for progress bar fills, colors, gradients, and head glyphs.
- Animated progress titles with a subtle sweeping highlight.
- Column-aware table printer with spans, width hints, and
fit
sizing. - Zero dependencies; works anywhere Python runs.
pip install logbar
LogBar works out-of-the-box with CPython 3.8+ on Linux, macOS, and Windows terminals.
import time
from logbar import LogBar
log = LogBar.shared()
log.info("hello from logbar")
log.info.once("this line shows once")
log.info.once("this line shows once") # silently skipped
for _ in log.pb(range(5)):
time.sleep(0.2)
Sample output (colors omitted in plain-text view):
INFO hello from logbar
INFO this line shows once
INFO [###---------------] 20% (1/5)
The shared instance exposes the standard level helpers plus once
variants:
log.debug("details...")
log.warn("disk space is low")
log.error("cannot connect to database")
log.critical.once("fuse blown, shutting down")
Typical mixed-level output (Note: Markdown cannot display ANSI colors):
DEBUG model version=v2.9.1
WARN disk space is low (5%)
ERROR cannot connect to database
CRIT fuse blown, shutting down
Progress bars accept any iterable or integer total:
for item in log.pb(tasks):
process(item)
for _ in log.pb(500).title("Downloading"):
time.sleep(0.05)
Manual mode gives full control when you need to interleave logging and redraws:
pb = log.pb(jobs).title("Processing").manual()
for job in pb:
log.info(f"starting {job}")
pb.subtitle(f"in-flight: {job}").draw()
run(job)
log.info(f"finished {job}")
Progress bar snapshot (plain-text example):
INFO Processing [##########------------] 40% (8/20) in-flight: step-8
The bar always re-renders at the bottom, so log lines never overwrite your progress.
LogBar keeps each progress bar on its own line and restacks them whenever they redraw. Later bars always appear closest to the live log output.
pb_fetch = log.pb(range(80)).title("Fetch").manual()
pb_train = log.pb(range(120)).title("Train").manual()
for _ in pb_fetch:
pb_fetch.draw()
time.sleep(0.01)
for _ in pb_train:
pb_train.draw()
time.sleep(0.01)
pb_train.close()
pb_fetch.close()
Sample stacked output (plain-text view):
INFO Fetch [███████░░░░░░░░░░░░░░░░░░░░░░░░░░░░] | 58.8% 00:00:46 / 00:01:19
INFO Train [█████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] | 37.5% 00:01:15 / 00:03:20
Pick from bundled palettes or create your own blocks and colors:
pb = log.pb(250)
pb.style('sunset') # bundled gradients: emerald_glow, sunset, ocean, matrix, mono
pb.fill('▓', empty='·') # override glyphs
pb.colors(fill=['#ff9500', '#ff2d55'], head='mint') # custom palette, optional head accent
pb.colors(empty='slate') # tint the empty track
pb.head('>', color='82') # custom head glyph + color index
ProgressBar.available_styles()
lists builtin styles, and you can register additional ones with ProgressBar.register_style(...)
or switch defaults globally via ProgressBar.set_default_style(...)
. Custom colors accept ANSI escape codes, 256-color indexes (e.g. '82'
), or hex strings ('#4c1d95'
).
Styled output (plain-text view with ANSI removed):
INFO Upload [▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉···········] | 62.0% 00:01:48 / 00:02:52
Use log.columns(...)
to format aligned tables while logging data streams. Print the column header per context with cols.info.header()
(or cols.warn.header()
, etc.). Columns support spans and three width hints:
- character width:
"24"
- percentage of the available log width:
"30%"
- content-driven fit:
"fit"
cols = log.columns(
{"label": "tag", "width": "fit"},
{"label": "duration", "width": 8},
{"label": "message", "span": 2}
)
cols.info.header()
cols.info("startup", "1.2s", "ready", "subsystem online")
cols.info("alignment", "0.5s", "resizing", "fit width active")
Sample table output (plain-text):
INFO +----------+----------+-----------------------------+------------------------------+
INFO | tag | duration | message | message |
INFO +----------+----------+-----------------------------+------------------------------+
INFO | startup | 1.2s | ready | subsystem online |
INFO +----------+----------+-----------------------------+------------------------------+
INFO | alignment| 0.5s | resizing | fit width active |
INFO +----------+----------+-----------------------------+------------------------------+
Notice how the tag
column expands precisely to the longest value thanks to width="fit"
.
You can update column definitions at runtime:
cols.update({
"message": {"width": "40%"},
"duration": {"label": "time"}
})
The API mirrors common tqdm
patterns while staying more Pythonic:
# tqdm
for n in tqdm.tqdm(range(1000)):
consume(n)
# logbar
for n in log.pb(range(1000)):
consume(n)
Manual update comparison:
# tqdm manual mode
with tqdm.tqdm(total=len(items)) as pb:
for item in items:
handle(item)
pb.update()
# logbar manual redraw
with log.pb(items).manual() as pb:
for item in pb:
handle(item)
pb.render()
- Combine columns and progress bars by logging summaries at key checkpoints.
- Use
log.warn.once(...)
to keep noisy health checks readable. - For multi-line messages, pre-format text and pass it as a single string; LogBar keeps borders intact.