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, recording CONFIG_SOURCE for 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 _StreamInterceptor wrapping sys.stdout/sys.stderr with a full io.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(), and instrumentation_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, or sys.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_SOURCE must 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_CONFIG

Absolute path to a debug_config.json file. 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_DIR

Absolute path to the directory where log files are written. When set, this replaces the default <cwd>/logs directory.

UNIFIED_DEBUG_DISABLE_EXCEPTION_HOOK

Set to 1 to prevent init() from installing the custom sys.excepthook, even when intercept_exceptions=True is 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_ms and memory_mb fields 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 func local variable set to the decorated function’s __name__ so that inner _dbg calls 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_active() bool[source]#
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.instrumentation_allow_custom_components() bool[source]#
oc_diagnostics.unified_debug.instrumentation_verbose() bool[source]#
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_LEVELS immediately; takes effect on the next _dbg() call. Emits a UserWarning and returns without modifying anything if level is not an integer in 0–9.

oc_diagnostics.unified_debug.set_global_setting(key: str, value) None[source]#

Update a global diagnostic setting at runtime.

Valid keys: silent_mode, enable_timestamps, enable_colors, include_filename, output_mode. Emits a UserWarning for unknown keys and returns without modifying anything.