1from __future__ import annotations
2
3import logging
4import threading
5from typing import TYPE_CHECKING
6
7if TYPE_CHECKING:
8 from types import TracebackType
9
10
11class DebugMode:
12 """Context manager to temporarily set DEBUG level on a logger with reference counting."""
13
14 def __init__(self, logger: logging.Logger):
15 self.logger = logger
16 self.original_level = None
17 self._ref_count = 0
18 self._lock = threading.Lock()
19
20 def __enter__(self) -> DebugMode:
21 """Store original level and set to DEBUG."""
22 self.start()
23 return self
24
25 def __exit__(
26 self,
27 exc_type: type[BaseException] | None,
28 exc_val: BaseException | None,
29 exc_tb: TracebackType | None,
30 ) -> None:
31 """Restore original level."""
32 self.end()
33
34 def start(self) -> None:
35 """Enable DEBUG logging level."""
36 with self._lock:
37 if self._ref_count == 0:
38 self.original_level = self.logger.level
39 self.logger.setLevel(logging.DEBUG)
40 self._ref_count += 1
41
42 def end(self) -> None:
43 """Restore original logging level."""
44 with self._lock:
45 self._ref_count = max(0, self._ref_count - 1)
46 if self._ref_count == 0 and self.original_level is not None:
47 self.logger.setLevel(self.original_level)