|
Option C oc_diagnostics
|
Classes | |
| class | _StreamInterceptor |
Functions | |
| str | _safe_env_path (str name) |
| _load_debug_config () | |
| str | _get_log_file () |
| str | _get_timestamp () |
| str | _get_color (str domain, int level) |
| str | _reset_color () |
| None | _prune_old_log_files (str log_dir, int max_count) |
| _ensure_log_dir_and_header () | |
| _write_to_file (str line) | |
| str | _apply_redaction (str text) |
| _emit (str line, int level, dict|None record=None) | |
| _dbg (str domain, str func, str msg, int|str|None level=None, bool override=False, int _depth=1) | |
| _dbg_ai_trace (str domain, str func, str msg, int _depth=1) | |
| None | _adbg (str domain, str func, str msg, int|None level=None, bool override=False) |
| bool | instrumentation_active () |
| bool | instrumentation_verbose () |
| bool | instrumentation_allow_custom_components () |
| bool | instrumentation_allow_all () |
| None | set_debug_level (str domain, int level) |
| None | set_global_setting (str key, value) |
| dbg_timed (str domain, str func, str label="", int level=3) | |
| dbg_scope (str domain, str func, str label="", int level=2) | |
| dbg_profile (str domain="GENERAL", str func="", str label="", int level=3) | |
| None | _dbg_assert (str domain, condition, str message, str|None func=None) |
| dbg_trace (str domain, int level=2) | |
| dbg_metrics (str domain, int level=3) | |
| _exception_hook (exc_type, exc_value, exc_traceback) | |
| None | _reload_config () |
| None | _watch_config_loop () |
| None | init (bool intercept_streams=True, bool intercept_exceptions=True, bool watch_config=False) |
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...
|
protected |
Async variant of _dbg(). Terminal output is written synchronously (fast, low-latency). File I/O is offloaded to a thread via asyncio.to_thread() so that async callers are not blocked by disk writes. The caller frame is captured *synchronously* before any await because frame references become invalid once the coroutine is suspended.
Definition at line 877 of file unified_debug.py.
|
protected |
Apply configured redaction patterns to log output (production mode only). OI-506 / FS-506 layer order (production mode): 1. Built-in secret/URL/token scrubbers (gated by ``REDACT_SECRETS``): git ``user:token@`` credentials, common token prefixes (``ghp_``, ``sk-``, ``xoxb-``, ``eyJ``...), and ``secret=value`` KV pairs. 2. User-defined ``REDACTION_PATTERNS`` from config. The built-ins run unconditionally when ``REDACT_SECRETS`` is True so production logs cannot leak credentials even if the operator forgot to configure a pattern list. The toggle exists purely as an escape hatch for environments that redact at the shipping layer instead.
Definition at line 674 of file unified_debug.py.
|
protected |
Unified debug logger.
Parameters
----------
domain : str
Debug domain (e.g., "CONTROLLER", "AHK", "MODE").
func : str
Logical context name provided by the caller.
msg : str
Message text.
level : int | str | None
Debug level (0-9), or ``"ai_trace"`` for AI-Trace shadow logging.
Defaults to 2.
override : bool
If True, bypasses domain-level filtering.
Behavior
--------
- Automatically captures filename, line number, and caller function.
- Formats output according to INCLUDE_FILENAME and ENABLE_TIMESTAMPS.
- Writes to terminal, file, or both.
- When ``level="ai_trace"``: writes to file/JSONL only (no console output).
Requires ``ai_trace_level: true`` in config; otherwise silently discarded.
Definition at line 727 of file unified_debug.py.
|
protected |
Write an AI-Trace message to file/JSONL only (never console). Called internally by ``_dbg()`` when ``level="ai_trace"``.
Definition at line 838 of file unified_debug.py.
|
protected |
Domain-aware runtime assertion, suppressed in production mode.
Parameters
----------
domain : str
Debug domain for the assertion message.
condition
Expression to evaluate. If falsy (and not in production mode),
emits an L1 override ``_dbg`` message and raises ``AssertionError``.
message : str
Descriptive failure message.
func : str | None
Logical context name. If ``None``, uses ``"<unknown>"``.
Notes
-----
In ``PROD_MODE`` this function returns immediately on entry; no
assertion check is performed, no ``_dbg`` is emitted, and no
``AssertionError`` is raised. Argument expressions (including
``condition`` and ``message``) are still evaluated by Python before
the call enters this function — do not pass expensive computations
as arguments. **Never** use for safety-critical invariants — see §6a
and AP-16 in the specification.
Definition at line 1088 of file unified_debug.py.
|
protected |
Definition at line 700 of file unified_debug.py.
|
protected |
Definition at line 599 of file unified_debug.py.
|
protected |
|
protected |
|
protected |
Return the LOG_FILE path, computing it on the first call. Always invoked from within a ``_log_lock``-protected block so the lazy one-time initialization is thread-safe. After the first call ``LOG_FILE`` is a non-None string and subsequent calls are a single None-check.
Definition at line 535 of file unified_debug.py.
|
protected |
|
protected |
Load debug_config.json from the project diagnostics/ directory if present, otherwise fall back to debug_config.json next to this module. Returns (domains_dict, color_codes_dict, global_settings_dict, config_source_str). Falls back to minimal defaults if the file is missing or invalid. config_source_str describes which file was loaded, or why defaults were used.
Definition at line 200 of file unified_debug.py.
|
protected |
Delete the oldest log files when the count exceeds max_count. (FS-06)
Definition at line 584 of file unified_debug.py.
|
protected |
Reload debug_config.json in-place, updating all module-level globals. Preserves the existing ``DEBUG_LEVELS`` dict *object* so that downstream modules holding a reference to the dict automatically see the updated levels. Uses an update-first, prune-stale-after pattern (OI-29) to avoid the brief empty-dict window that ``clear()`` + ``update()`` would create.
Definition at line 1386 of file unified_debug.py.
|
protected |
|
protected |
Return ``os.environ[name]`` if it passes OI-515 sanity checks. Returns an empty string when the env var is unset, empty, contains a NUL byte, or exceeds :data:`_MAX_ENV_PATH_LEN`. A warning is emitted in the length/NUL-byte case so operators notice the rejection.
Definition at line 167 of file unified_debug.py.
|
protected |
Daemon thread: poll the config file mtime every 2 s; reload on change. (FS-09)
Definition at line 1441 of file unified_debug.py.
|
protected |
Definition at line 638 of file unified_debug.py.
| oc_diagnostics.unified_debug.dbg_metrics | ( | str | domain, |
| int | level = 3 ) |
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).
Definition at line 1173 of file unified_debug.py.
| oc_diagnostics.unified_debug.dbg_profile | ( | str | domain = "GENERAL", |
| str | func = "", | ||
| str | label = "", | ||
| int | level = 3 ) |
Decorator that wraps a function with :func:`dbg_timed`.
Example::
@dbg_profile("CONTROLLER", label="fetch_data")
def fetch_data():
...
Definition at line 1064 of file unified_debug.py.
| oc_diagnostics.unified_debug.dbg_scope | ( | str | domain, |
| str | func, | ||
| str | label = "", | ||
| int | level = 2 ) |
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 :func:`dbg_timed` instead.
Example::
with dbg_scope("DATA", "load_records"):
result = expensive_operation()
Definition at line 1045 of file unified_debug.py.
| oc_diagnostics.unified_debug.dbg_timed | ( | str | domain, |
| str | func, | ||
| str | label = "", | ||
| int | level = 3 ) |
Context manager that logs ENTER and EXIT with elapsed time in milliseconds.
Example::
with dbg_timed("CONTROLLER", "my_func", label="heavy_op"):
do_work()
Definition at line 1026 of file unified_debug.py.
| oc_diagnostics.unified_debug.dbg_trace | ( | str | domain, |
| int | level = 2 ) |
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)
Definition at line 1130 of file unified_debug.py.
| None oc_diagnostics.unified_debug.init | ( | bool | intercept_streams = True, |
| bool | intercept_exceptions = True, | ||
| bool | watch_config = False ) |
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.
Definition at line 1466 of file unified_debug.py.
| bool oc_diagnostics.unified_debug.instrumentation_active | ( | ) |
Definition at line 954 of file unified_debug.py.
| bool oc_diagnostics.unified_debug.instrumentation_allow_all | ( | ) |
Return True when all components should be instrumented regardless of type. (FS-13)
Definition at line 968 of file unified_debug.py.
| bool oc_diagnostics.unified_debug.instrumentation_allow_custom_components | ( | ) |
Definition at line 964 of file unified_debug.py.
| bool oc_diagnostics.unified_debug.instrumentation_verbose | ( | ) |
Definition at line 960 of file unified_debug.py.
| None oc_diagnostics.unified_debug.set_debug_level | ( | str | domain, |
| int | level ) |
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.
Definition at line 977 of file unified_debug.py.
| None oc_diagnostics.unified_debug.set_global_setting | ( | str | key, |
| value ) |
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.
Definition at line 995 of file unified_debug.py.
|
protected |
Definition at line 556 of file unified_debug.py.
|
protected |
Definition at line 106 of file unified_debug.py.
|
protected |
Definition at line 395 of file unified_debug.py.
|
protected |
Definition at line 521 of file unified_debug.py.
|
protected |
Definition at line 153 of file unified_debug.py.
|
protected |
Definition at line 152 of file unified_debug.py.
|
protected |
Definition at line 140 of file unified_debug.py.
|
protected |
Definition at line 395 of file unified_debug.py.
|
protected |
Definition at line 434 of file unified_debug.py.
|
protected |
Definition at line 123 of file unified_debug.py.
|
protected |
Definition at line 465 of file unified_debug.py.
|
protected |
Definition at line 467 of file unified_debug.py.
|
protected |
Definition at line 520 of file unified_debug.py.
|
protected |
Definition at line 493 of file unified_debug.py.
|
protected |
Definition at line 531 of file unified_debug.py.
|
protected |
Definition at line 1462 of file unified_debug.py.
|
protected |
Definition at line 1461 of file unified_debug.py.
|
protected |
Definition at line 1273 of file unified_debug.py.
|
protected |
Definition at line 1463 of file unified_debug.py.
|
protected |
Definition at line 505 of file unified_debug.py.
|
protected |
Definition at line 395 of file unified_debug.py.
|
protected |
Definition at line 532 of file unified_debug.py.
|
protected |
Definition at line 164 of file unified_debug.py.
|
protected |
Definition at line 163 of file unified_debug.py.
|
protected |
Definition at line 1228 of file unified_debug.py.
|
protected |
Definition at line 457 of file unified_debug.py.
|
protected |
Definition at line 395 of file unified_debug.py.
|
protected |
Definition at line 499 of file unified_debug.py.
| frozenset oc_diagnostics.unified_debug.ADDITIONAL_STRUCTURAL_EXACT |
Definition at line 409 of file unified_debug.py.
| tuple oc_diagnostics.unified_debug.ADDITIONAL_STRUCTURAL_PREFIXES |
Definition at line 406 of file unified_debug.py.
| bool oc_diagnostics.unified_debug.AI_TRACE_ENABLED = bool(_settings.get("ai_trace_level", False)) |
Definition at line 462 of file unified_debug.py.
| oc_diagnostics.unified_debug.COLOR_CODES = _colors |
Definition at line 398 of file unified_debug.py.
| oc_diagnostics.unified_debug.CONFIG_SOURCE = _config_source |
Definition at line 400 of file unified_debug.py.
| oc_diagnostics.unified_debug.DEBUG_LEVELS = _levels |
Definition at line 397 of file unified_debug.py.
| oc_diagnostics.unified_debug.ENABLE_COLORS = _settings["enable_colors"] |
Definition at line 413 of file unified_debug.py.
| oc_diagnostics.unified_debug.ENABLE_TIMESTAMPS = _settings["enable_timestamps"] |
Definition at line 412 of file unified_debug.py.
| frozenset oc_diagnostics.unified_debug.HTTP_REDACT_HEADERS |
Definition at line 425 of file unified_debug.py.
| oc_diagnostics.unified_debug.INCLUDE_FILENAME = _settings["include_filename"] |
Definition at line 414 of file unified_debug.py.
| bool oc_diagnostics.unified_debug.INSTRUMENTATION_ALL = _settings.get("instrumentation_allow_all", False) |
Definition at line 431 of file unified_debug.py.
| oc_diagnostics.unified_debug.INSTRUMENTATION_ENABLED = _settings["instrumentation_enabled"] |
Definition at line 401 of file unified_debug.py.
| str oc_diagnostics.unified_debug.LOG_DIR = _safe_env_path("OC_DIAGNOSTICS_LOG_DIR") or os.path.join(os.getcwd(), "logs") |
Definition at line 515 of file unified_debug.py.
| str oc_diagnostics.unified_debug.LOG_FILE = _env_log_file or _config_log_file_path or None |
Definition at line 526 of file unified_debug.py.
| int oc_diagnostics.unified_debug.MAX_LOG_FILES = _settings["max_log_files"] |
Definition at line 418 of file unified_debug.py.
| tuple oc_diagnostics.unified_debug.MAX_LOG_SIZE_BYTES |
Definition at line 419 of file unified_debug.py.
| oc_diagnostics.unified_debug.ORIGINAL_STDERR = sys.stderr |
Definition at line 529 of file unified_debug.py.
| oc_diagnostics.unified_debug.ORIGINAL_STDOUT = sys.stdout |
Definition at line 528 of file unified_debug.py.
| oc_diagnostics.unified_debug.OUTPUT_MODE = _settings["output_mode"] |
Definition at line 415 of file unified_debug.py.
| int oc_diagnostics.unified_debug.PROD_MAX_LEVEL = int(_settings.get("prod_max_level", 2)) |
Definition at line 454 of file unified_debug.py.
| tuple oc_diagnostics.unified_debug.PROD_MODE |
Definition at line 443 of file unified_debug.py.
| bool oc_diagnostics.unified_debug.REDACT_SECRETS = bool(_settings.get("redact_secrets", True)) |
Definition at line 488 of file unified_debug.py.
| list oc_diagnostics.unified_debug.REDACTION_PATTERNS = [] |
Definition at line 474 of file unified_debug.py.
| oc_diagnostics.unified_debug.SILENT_MODE = _settings["silent_mode"] |
Definition at line 402 of file unified_debug.py.
| tuple oc_diagnostics.unified_debug.UI_EVENT_PROPERTIES |
Definition at line 437 of file unified_debug.py.