Logging
duho.logging
TRACE
module-attribute
TRACEBACK_ENV = 'DUHO_TRACEBACK'
module-attribute
VERBOSE_HELP = ''
module-attribute
VERBOSE_LEVELS = {}
module-attribute
__all__ = ['add_logging_level', 'DefaultFormatter', 'VERBOSE_LEVELS', 'VERBOSE_HELP', 'parse_loglevels', 'init_stderr_logging', 'initverbose', 'TRACEBACK_ENV', 'traceback_enabled', 'log_exception']
module-attribute
DefaultFormatter(fmt='%(asctime)s | %(levelname)8s | %(name)s: %(message)s', datefmt=None, style='%', validate=True, *, color=True)
Bases: Formatter
Log formatter with colored output.
Source code in src/duho/logging.py
223 224 225 226 227 228 229 230 231 232 233 234 235 236 | |
COLORS = {_logging.DEBUG: _asicode(34), _logging.INFO: _asicode(32), _logging.WARNING: _asicode(33), _logging.ERROR: _asicode(31), _logging.CRITICAL: _asicode(31, 47)}
class-attribute
instance-attribute
RESET_ALL = _asicode(0)
class-attribute
instance-attribute
format(record)
Source code in src/duho/logging.py
238 239 240 241 242 243 244 245 | |
__getattr__(name)
Source code in src/duho/logging.py
44 45 46 47 48 49 50 | |
add_logging_level(name, level, force=False, color=None)
Register a custom log level.
Installs NAME on the logging module and the method name on the
module, on logging.Logger, on the current logging.getLoggerClass() and
on logging.LoggerAdapter (so logging.name(...), logger.name(...)
and adapter.name(...) all work; the adapter's applies its process()),
registers the level name, refreshes the -v/-q verbosity table
(:func:initverbose) so the new level immediately participates in it
regardless of whether anything has configured logging yet, and optionally
gives it a color for :class:DefaultFormatter.
Unless force is set, refuses (ValueError) to clobber an existing
logging/Logger/LoggerAdapter attribute that duho itself did not
install -- guarding both the given NAME (as before) and its lower-cased method
name: a level named e.g. LOG or EXCEPTION would otherwise
silently replace logging.log/Logger.exception. A repeat call for
a name duho already installed itself (tracked in
:data:_installed_level_names) at the same number is a harmless no-op
either way, but at a different number it raises (renumber with
force=True); a name that collides with something duho did NOT install --
an unrelated stdlib constant like logging.BASIC_FORMAT, not just a level/method name --
raises instead of silently no-oping, the same guard the lower-cased
check below already gave method names.
Source code in src/duho/logging.py
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | |
init_stderr_logging(name=None, level=None)
Initialize logging to stderr with color support.
Idempotent: a repeat call on the same logger (directly, or via
duho.main/duho.app each time they run) finds the handler this
function installed last time (tagged, never matched by identity/count)
and does not add a second one -- calling it twice does not duplicate
log lines. level, when given, is still (re)applied.
Color is gated the same way duho's own --help formatters are:
ANSI only when the stream is a TTY, off when NO_COLOR is set, forced
on with FORCE_COLOR -- so redirecting/piping output, or a CI log,
never receives raw escape bytes. When color is enabled and colorama is
importable, colorama.just_fix_windows_console() is called so a legacy
Windows console renders the codes instead of showing them literally.
Source code in src/duho/logging.py
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | |
initverbose()
Initialize verbose level mappings.
Source code in src/duho/logging.py
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | |
log_exception(logger, msg, *args, level=_logging.ERROR)
Log a caught exception, with a traceback iff DUHO_TRACEBACK is set.
The framework's resilient paths (discovery skipping a bad command, a runpath
step failing, a fan-out target raising) deliberately do NOT propagate the
exception, which means the stack -- the only thing that says where it broke
-- is lost unless it is logged. Logging it unconditionally would bury an
ordinary "optional dependency missing" warning under 30 frames, so this
helper makes it opt-in via :func:traceback_enabled.
Call it from inside an except block, where exc_info has an exception
to render. When disabled the exc_info kwarg is omitted entirely rather
than passed as False -- both suppress the traceback, but False is
recorded verbatim on LogRecord.exc_info, so a handler or test inspecting
that attribute would see False where every other un-decorated record in
the process carries None.
stacklevel=2 skips this helper's own frame, so the record attributes
the caller's except-block, not duho/logging.py:log_exception, as the
log site.
Source code in src/duho/logging.py
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 | |
parse_loglevels(text, itemdivider=',', valkey_separator=':')
Parse a [NAME:]LEVEL[,NAME:LEVEL...] log level specification.
LEVEL is matched against the registered level names -- first by its
EXACT text (so a custom level registered under a lowercase/mixed-case
name, e.g. logging.addLevelName(25, "notice"), matches directly),
then by its upper-cased form (so the standard DEBUG, debug and
Debug spellings are all still accepted) -- or may be a plain integer.
Surrounding whitespace around a name or level is stripped. An entry that
resolves to neither raises argparse.ArgumentTypeError naming the bad
token -- argparse reports this as a normal "invalid value" usage error
instead of the entry silently disappearing from the returned mapping.
Source code in src/duho/logging.py
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | |
traceback_enabled()
Return whether framework error logs should carry a full traceback.
Reads :data:TRACEBACK_ENV (DUHO_TRACEBACK) from the process
environment on EVERY call, or the variable a running program's root names
with _traceback_env_ instead rather than caching it, so a test (or an app that
sets it mid-run) can flip the switch without re-importing duho. The read is
a dict lookup -- cheap enough to sit on an error path.
Off by default: a CLI user seeing a framework warning wants the message, not
a stack. A developer debugging where a step/command/target actually failed
exports DUHO_TRACEBACK=1 and gets the traceback for free at every site.
Source code in src/duho/logging.py
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 | |