oc_diagnostics.unified_debug module#
Unified Debugging System#
Purpose#
Provide a single, centralized _dbg() debug logger for Option C projects,
together with per-domain level filtering, ANSI color output, timestamped log
files, stdout/stderr interception, and an uncaught-exception hook.
Responsibilities#
Load debug configuration from
diagnostics/debug_config.json(project root) or the bundled fallback, recordingCONFIG_SOURCEfor the log header.Provide
_dbg()with domain/level filtering, caller-site reporting, and optional override mode.Write formatted output to terminal (ANSI colors retained) and/or a log file (all ANSI escape sequences stripped) based on
OUTPUT_MODE.Provide
_StreamInterceptorwrappingsys.stdout/sys.stderrwith a fullio.TextIOBase-compatible interface and a thread-local re-entrant recursion guard.Provide
init()to activate stream interception and the exception hook explicitly; importing this module has zero side effects.Provide
instrumentation_active(),instrumentation_verbose(), andinstrumentation_allow_custom_components()helpers consumed by other modules.
Diagnostics#
Domain: GENERAL Levels:
L2 — lifecycle L3 — semantic details L4 — deep tracing
Contracts#
Importing this module must have zero side effects on
sys.stdout,sys.stderr, orsys.excepthook.init()must be idempotent; calling it more than once is a no-op._dbg()must never raise; all internal errors are silently swallowed.CONFIG_SOURCEmust be a non-empty string after module load.
Configuration#
Debug domains, levels, colors, and global settings are loaded from diagnostics/debug_config.json in the project root if present, or from debug_config.json in the same directory as this module as a fallback.
Each project supplies its own diagnostics/debug_config.json with project-specific domains. This keeps the library project-independent.
Environment Variables#
OC_DIAGNOSTICS_CONFIGAbsolute path to a
debug_config.jsonfile. When set, this path is used instead of<cwd>/diagnostics/debug_config.json. Useful for embedded usage, subprocess invocation, or CI environments where the working directory is not the project root.OC_DIAGNOSTICS_LOG_DIRAbsolute path to the directory where log files are written. When set, this replaces the default
<cwd>/logsdirectory.UNIFIED_DEBUG_DISABLE_EXCEPTION_HOOKSet to
1to preventinit()from installing the customsys.excepthook, even whenintercept_exceptions=Trueis passed. Useful in environments (pytest, debuggers, ASGI frameworks) that install their own exception handlers and must not be displaced.
Log Format#
When filename inclusion is enabled:
[DOMAIN][L2][file.py:123::caller=foo|logical context=bar] 14:03:12.345 Message…
When disabled:
[DOMAIN][L2][logical context=bar] 14:03:12.345 Message…
- oc_diagnostics.unified_debug.dbg_metrics(domain: str, level: int = 3)[source]#
Decorator that logs structured performance metrics (time + memory).
Emits a JSON-style string with
duration_msandmemory_mbfields suitable for observability tooling.- Parameters:
domain (str) – Debug domain for the metrics message.
level (int) – Debug level (default 3).
- oc_diagnostics.unified_debug.dbg_profile(domain: str = 'GENERAL', func: str = '', label: str = '', level: int = 3)[source]#
Decorator that wraps a function with
dbg_timed().Example:
@dbg_profile("CONTROLLER", label="fetch_data") def fetch_data(): ...
- oc_diagnostics.unified_debug.dbg_scope(domain: str, func: str, label: str = '', level: int = 2)[source]#
Context manager that logs ENTER and EXIT at the specified level without timing.
Use for pure lifecycle tracing where elapsed time is not needed. For timed tracing, use
dbg_timed()instead.Example:
with dbg_scope("DATA", "load_records"): result = expensive_operation()
- oc_diagnostics.unified_debug.dbg_timed(domain: str, func: str, label: str = '', level: int = 3)[source]#
Context manager that logs ENTER and EXIT with elapsed time in milliseconds.
Example:
with dbg_timed("CONTROLLER", "my_func", label="heavy_op"): do_work()
- oc_diagnostics.unified_debug.dbg_trace(domain: str, level: int = 2)[source]#
Decorator that emits START/END lifecycle messages and catches exceptions.
Injects a
funclocal variable set to the decorated function’s__name__so that inner_dbgcalls can reference it without hardcoding a string.- Parameters:
domain (str) – Debug domain for lifecycle messages.
level (int) – Debug level for START/END messages (default 2).
Example
@dbg_trace("DATA") def process_data(data): _dbg("DATA", func, "working...", 3) return transform(data)
- oc_diagnostics.unified_debug.init(intercept_streams: bool = True, intercept_exceptions: bool = True, watch_config: bool = False) None[source]#
Initialize oc_diagnostics side effects.
Must be called explicitly after importing oc_diagnostics. Importing the package does NOT activate stream interception or the custom exception hook automatically.
- Parameters:
intercept_streams (bool) – If True, replace sys.stdout and sys.stderr with _StreamInterceptor instances that route all print() calls through _dbg(). Can also be disabled by setting the environment variable UNIFIED_DEBUG_DISABLE_STDIO_INTERCEPT=1 before calling init(). Forced to False in production mode (PROD_MODE=True). Default True.
intercept_exceptions (bool) – If True, install a custom sys.excepthook that logs uncaught exceptions through _dbg() and then delegates to the previously active hook. Can also be disabled by setting the environment variable UNIFIED_DEBUG_DISABLE_EXCEPTION_HOOK=1 before calling init(). Forced to False in production mode (PROD_MODE=True). Default True.
watch_config (bool) – If True, start a daemon thread that polls the loaded config file every 2 seconds and reloads it when the mtime changes. Useful during development to adjust log levels without restarting the application. Default False. (FS-09)
Notes
Calling init() more than once is a no-op.
- oc_diagnostics.unified_debug.instrumentation_allow_all() bool[source]#
Return True when all components should be instrumented regardless of type. (FS-13)
- oc_diagnostics.unified_debug.set_debug_level(domain: str, level: int) None[source]#
Set the debug level for a domain at runtime (0–9).
Updates
DEBUG_LEVELSimmediately; takes effect on the next_dbg()call. Emits aUserWarningand returns without modifying anything iflevelis not an integer in 0–9.