dazzle-loglib
Channel/verbosity-aware CLI output management -- the diagnostic-output member of the perpendicular tier of the DazzleLib stack.
One signed verbosity axis crossed with consumer-defined named channels, so a program can answer how loud is each channel of my self-narration, and where does each message go? — per subsystem, independently, from one -v-stacking CLI surface.
pip install dazzle-loglib
What this owns (and what it doesn't)
| Owns | Does not own |
|---|---|
| The verbosity gate — one integer comparison, and no message is formatted unless it passes | Color and rich formatting (supply a renderer callable; the hook is built in) |
| The channel registry each program declares for itself | Your channel vocabulary — there is no built-in "correct" channel set |
| Where a message goes (per-manager, per-channel, or per-message destinations) | Durable structured logs; this is on-demand interactive detail, not the audit record |
| Env/CLI resolution with defined precedence | Reading the environment implicitly — you name your own variables |
| A hint registry (runtime, context-filtered, session-deduplicated) | Help-surface content — that's dazzle-helplib's TIPs, a deliberately separate mechanism |
The model: verbosity x channel
One signed verbosity axis (a dazzle_lib.Continuum with an invariant zero):
<-- quieter -------------------- default --------------------------- louder -->
-4 -3 -2 -1 0 1 2 3 4 5
wall errors warnings minimal default extra diagnostics config lite-debug debug
A message at level shows when level <= threshold; at -4 (the hard wall) nothing shows at all. -v steps warmer, -q steps colder, and they compose (-vv -q = 1).
Channels are the orthogonal dimension, and each program declares its own:
from dazzle_loglib import init_output, get_output, ChannelDef
init_output(
verbosity=args.verbose - args.quiet,
strict_channels=True,
channel_defs=[
ChannelDef("liveness", "Session liveness verification"),
ChannelDef("git", "Git operations"),
ChannelDef("scan", "Discovery and scanning"),
ChannelDef("vals", "Value annotations on results", opt_in=True),
],
channels=args.show, # e.g. ["liveness:diagnostics", "scan:2"]
)
out = get_output()
out.emit(1, "scanned {n} sessions", channel="scan", n=count)
out.emit(2, "entry={id} pid={pid} in_by_pid={hit} -> reject({rung})",
channel="liveness", id=entry_id, pid=pid, hit=hit, rung=rung)
Each channel can be pinned independently of the global level, so --show liveness:debug floods one subsystem without drowning the rest. Named rungs work anywhere integers do, opt-in channels stay cold until raised, and the channels x verbosity crossing is a real ContinuumSpace (out.verbosity_space()) — so further axes compose rather than bolt on.
Status
0.3.x, alpha — and deliberately still flexible. This library ships mid-development of the wider DazzleLib stack: its first real consumer has not landed yet, and adoption is what usually reshapes an API. So the promise here is no silent drift rather than no change — docs/api-stability.md enumerates the tracked surface (pinned by an import-stability canary), the parts explicitly excluded from any promise, and, honestly, where movement is still expected. Changes land in CHANGELOG.md with a version bump, never quietly.
Usage
Zero cost when gated
emit() never formats a message that will not show — keyword arguments are interpolated only after the gate passes. For expensive collection, ask first:
if out.is_level_active(2, "liveness"):
rows = expensive_enumeration() # skipped entirely at default verbosity
out.emit(2, "rows={n}", channel="liveness", n=len(rows))
Resolution with defined precedence
from dazzle_loglib import resolve_verbosity, resolve_channel_specs
verbosity = resolve_verbosity(args.verbose, args.quiet,
explicit=args.verbosity, # --verbosity N wins outright
env_var="MYAPP_VERBOSITY") # consulted only when the CLI is silent
specs = resolve_channel_specs(args.show, env_var="MYAPP_SHOW")
Precedence is explicit > CLI counts > environment > default, and CLI counts count as expressed whenever either is nonzero — -v -q nets to zero but still beats the environment. Hooks, schedulers, and other non-interactive contexts turn detail up by setting the variables; nothing edits scripts.
Injecting an emitter (the std-swappable seam)
Libraries that should never depend on a logging package can still speak:
from dazzle_loglib.protocols import EmitterProtocol, NullEmitter
def verify_tree(root, emitter: EmitterProtocol = None):
emitter = emitter or NullEmitter() # silent by default
if emitter.is_level_active(2, "verify"):
emitter.emit(2, "checking {p}", channel="verify", p=root)
EmitterProtocol is structural: a real OutputManager satisfies it, and so does a four-line shim over print (or CallableEmitter(logging.getLogger(__name__).info)). The contract travels down the stack; the implementation stays out of your dependency tree.
Renderers, hints, and tracing
emit() resolves a renderer in layers — per-call render=, per-channel renderer, global default_renderer, then plain print() to the resolved destination (stderr by default; 'stdout'/'stderr' sentinels resolve at emit time, so rebound streams are honored). Color belongs in a renderer callable (init_output(renderer=console.print)), never in the core. Also included: a Hint registry (context-filtered, session-deduplicated, routed through the same gate) and a @trace decorator (function entry/exit at full debug on the trace channel).
Installation
pip install dazzle-loglib
From source
git clone https://github.com/DazzleLib/dazzle-loglib.git
cd dazzle-loglib
pip install -e ".[dev]"
Documentation
- docs/api-stability.md — the tracked surface, what is excluded, where movement is still expected, and the protocol homing promise
- docs/cli-integration.md — the canonical CLI wiring: flags, resolution, help tables, keeping
--jsonoutput pure - CHANGELOG.md — release history
- ROADMAP.md — where this is going (tracked live in #1)
- docs/platform-support.md — platform and Python support
Migrating from a vendored log_lib
Projects carrying the ancestral copy: 0.2.0 re-runged the verbosity scale (config 2→3, debug 3→5, timing renamed extra) and replaced module-set channel registration with channel_defs=. The legacy module sets still import but warn on mutation and are excluded from the API-stability guarantee. See DazzleTools/dazzlecmd#118 for the cutover playbook.
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
python -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install -e ".[dev]"
# Run tests
python -m pytest tests/ -v
# Install git hooks
bash scripts/repokit-common/install-hooks.sh
Two house rules this library lives by:
- Dependencies point down only. The perpendicular tier consumes the bedrock and nothing else — never a consumer, never a sibling.
dazzle-loglibanddazzle-helplibdo not import each other. - The public surface changes loudly or not at all. The symbols and behaviors listed in docs/api-stability.md are pinned by
tests/test_import_stability.py, so drift fails a test rather than reaching a consumer. While the stack is mid-development the surface is still expected to move; the discipline is that it moves deliberately, versioned, and documented.
Like the project?
Part of DazzleLib
dazzle-loglib sits in the perpendicular tier: usable from any layer, depending only on the dazzle-lib bedrock.
Related Projects
- dazzle-lib — the bedrock: protocols, payload schemas, and the
Continuumprimitive this builds on - dazzle-helplib — the sibling: help content, detail continuum, and TIPs
- dazzle-filekit — cross-platform file operations
- dazzle-linklib — content-addressable link records
- The stack map — how the pieces fit
License
dazzle-loglib, Copyright (C) 2026 Dustin Darcy
Licensed under the MIT License -- see LICENSE. The whole DazzleLib stack is MIT-licensed.
Metadata
Release files for dazzle-loglib 0.3.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dazzle_loglib-0.3.3.tar.gz | 41.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dazzle_loglib-0.3.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 72.7 kB
Release files / dazzle_loglib-0.3.3.tar.gz
| Download URL | dazzle_loglib-0.3.3.tar.gz |
|---|---|
| Size | 41.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
098bd03d1106516bdbe31b6621b5cef0b151bf51aea117317a27896d3c77f7bd
|
|
BLAKE2b-256 checksum How to use checksums |
ed3cec13498bdf6b99377cc7af8c0af382ec5083edad394e8283c6407edec765
|
| 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 Aug 16, 2026.
Transparency logRelease files / dazzle_loglib-0.3.3-py3-none-any.whl
| Download URL | dazzle_loglib-0.3.3-py3-none-any.whl |
|---|---|
| Size | 31.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c0b5f6222edc6b020ff9ca8e4d71a7b40fe6865ae2d557133eb046893baa6ee9
|
|
BLAKE2b-256 checksum How to use checksums |
f6ce2476b51e49457c5a93db0344a3c8f2806aeaacc8389d30fce8626e7cb5fd
|
| 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 Aug 16, 2026.
Transparency log