exlab_wizard.logging#
Canonical logger package. Backend Spec §16.
This package owns the runtime logger architecture for ExLab-Wizard:
get_logger()is the single entry point for logger creation (§16.2.1; the only allowed call site forlogging.getLogger).configure_logging()installs the §16.2.5 queue-based handler chain on FastAPI lifespan startup.set_run_context()is a context manager that pushes structured tags (host / equipment / project / run-kind / run-id) onto per-taskcontextvarsso subsequent log calls within the block carry them.
Component authors only need get_logger for normal logging; the
launcher and the creation controller use configure_logging and
set_run_context respectively. See §16.2 for the full architecture.
- exlab_wizard.logging.clear_run_context()[source]#
Reset every context var to
None.Provided for test fixtures that want to ensure a clean slate between cases. Production code should rely on
set_run_context()’s exit semantics; callingclear_run_contextmid-run would mask a nestedwithblock’s prior values, which is almost always wrong.- Return type:
- exlab_wizard.logging.configure_logging(config=None)[source]#
Install the §16.2.5 queue-based handler chain.
Idempotent: calling this a second time tears down the existing listener and rebuilds it with the new
config. In-flight log records that have already been enqueued are drained into the old handlers before they’re replaced (seeQueueListener.stop), so aPUT /api/v1/configreconfigure does not lose log output.On first call:
Sets the root logger’s level threshold from
config.level(defaultINFOifconfigisNone).Creates a fresh unbounded
queue.Queue.Wires a
QueueHandleronto the root logger so everylogger.info(...)returns immediately.Builds the real handlers (per-equipment file, central rotating file, stderr stream) and starts a
QueueListenerthread that drains the queue into them.
The per-equipment file handler is only installed when a
local_rootis configured (Phase 3A’sconfigure_loggingaccepts aLoggingConfigonly; future phases may pass an explicitlocal_rootargument). Whenlocal_rootis unset, the chain falls back to central + stderr only – this keeps unit tests that don’t model an equipment root from crashing.- Parameters:
config (
LoggingConfig|None)- Return type:
- exlab_wizard.logging.get_logger(name)[source]#
Return a logger for
name.The ONLY place in the codebase that may call
logging.getLogger(). Component authors must use this entry point; a pre-commit lint rule rejects directlogging.getLoggercalls in any module underexlab_wizard/other than this one (§16.2.1).The returned logger inherits the root level set by
configure_logging(). Ifconfigure_logginghas not been called yet (e.g. during early module import or in unit tests that don’t exercise the handler chain), the logger still works – it just falls through to the stdlib root logger’s defaults until the firstconfigure_loggingcall.
- exlab_wizard.logging.get_run_context()[source]#
Return a snapshot of the active context as a plain
dict.Used by the formatter to render structured tags. Keys match the kwarg names of
set_run_context(); values areNonewhen the var is unset.
- exlab_wizard.logging.set_run_context(*, host=None, equipment_id=None, project_short_id=None, run_kind=None, run_id=None)[source]#
Push the supplied context vars on entry and reset them on exit.
Vars whose argument is
None(the default) are left unchanged. This lets a caller incrementally widen the context (e.g. sethostandequipment_idin an outerwithblock, then addrun_idin a nested inner block) without destroying the outer values.The context manager is implemented via
contextvars.Tokenso nested entries are restored to their prior value on exit (not toNone). Each token corresponds to exactly oneContextVar.setcall.The caller is responsible for ensuring values are non-empty strings; this helper does no validation. Passing an empty string
""is treated as a real value (it suppresses the default-None suppression in the formatter), which is almost certainly a caller bug – so callers should passNoneexplicitly when they mean “don’t touch this var”.
Modules