Option C oc_diagnostics
Loading...
Searching...
No Matches
logging_handler.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# oc_diagnostics/logging_handler.py
4
5"""
6Python logging Handler
7======================
8
9Purpose
10-------
11Bridge the Python standard-library ``logging`` module into ``oc_diagnostics``
12so that libraries and frameworks that emit log records via ``logging.getLogger``
13are automatically captured and displayed through the unified diagnostics system.
14
15Responsibilities
16----------------
17- Provide ``OcDiagnosticsHandler``, a ``logging.Handler`` subclass that
18 converts each ``LogRecord`` to an ``oc_diagnostics._dbg()`` call.
19- Map Python logging levels to oc_diagnostics diagnostic levels via a
20 configurable ``level_map`` dict.
21- Use a sensible default mapping:
22 DEBUG → L4 (deep tracing)
23 INFO → L2 (lifecycle)
24 WARNING → L1 (errors only)
25 ERROR → L1
26 CRITICAL → L1
27
28Diagnostics
29-----------
30Domain: configurable (default: "GENERAL")
31Levels: determined by level_map at runtime
32
33Contracts
34---------
35- Must not raise; errors are delegated to ``logging.Handler.handleError()``.
36- Requires no optional dependencies (pure Python, always available).
37- The ``domain`` and ``level_map`` are settable at construction time.
38"""
39
40import logging
41
42from oc_diagnostics.unified_debug import _dbg
43
44
45# Default mapping from Python logging level ints to oc_diagnostics levels
46_DEFAULT_LEVEL_MAP: dict[int, int] = {
47 logging.DEBUG: 4,
48 logging.INFO: 2,
49 logging.WARNING: 1,
50 logging.ERROR: 1,
51 logging.CRITICAL: 1,
52}
53
54
55class OcDiagnosticsHandler(logging.Handler):
56 """A ``logging.Handler`` that routes records through ``oc_diagnostics._dbg()``.
57
58 Parameters
59 ----------
60 domain : str
61 The oc_diagnostics domain used for all records routed through this
62 handler. Default ``"GENERAL"``.
63 level_map : dict[int, int] | None
64 Mapping from Python ``logging`` level integers to oc_diagnostics
65 diagnostic level integers. When ``None``, the built-in default is
66 used: DEBUG→4, INFO→2, WARNING/ERROR/CRITICAL→1.
67
68 Example
69 -------
70 ::
71
72 import logging
73 from oc_diagnostics import OcDiagnosticsHandler
74
75 handler = OcDiagnosticsHandler(domain="DATA")
76 logging.getLogger("sqlalchemy").addHandler(handler)
77 """
78
80 self,
81 domain: str = "GENERAL",
82 level_map: dict[int, int] | None = None,
83 ) -> None:
84 super().__init__()
85 self.domain = domain
86 self._level_map: dict[int, int] = (
87 level_map if level_map is not None else _DEFAULT_LEVEL_MAP
88 )
89
90 def emit(self, record: logging.LogRecord) -> None:
91 try:
92 diag_level = self._level_map.get(record.levelno, 2)
93 msg = self.format(record)
94 _dbg(
95 self.domain,
96 record.funcName or "",
97 msg,
98 level=diag_level,
99 _depth=2,
100 )
101 except Exception:
102 self.handleError(record)
None emit(self, logging.LogRecord record)
None __init__(self, str domain="GENERAL", dict[int, int]|None level_map=None)