_tracing.py 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. """
  2. Tracing utils
  3. """
  4. from __future__ import annotations
  5. from typing import Any
  6. from typing import Callable
  7. from typing import Sequence
  8. from typing import Tuple
  9. _Writer = Callable[[str], object]
  10. _Processor = Callable[[Tuple[str, ...], Tuple[Any, ...]], object]
  11. class TagTracer:
  12. def __init__(self) -> None:
  13. self._tags2proc: dict[tuple[str, ...], _Processor] = {}
  14. self._writer: _Writer | None = None
  15. self.indent = 0
  16. def get(self, name: str) -> TagTracerSub:
  17. return TagTracerSub(self, (name,))
  18. def _format_message(self, tags: Sequence[str], args: Sequence[object]) -> str:
  19. if isinstance(args[-1], dict):
  20. extra = args[-1]
  21. args = args[:-1]
  22. else:
  23. extra = {}
  24. content = " ".join(map(str, args))
  25. indent = " " * self.indent
  26. lines = ["{}{} [{}]\n".format(indent, content, ":".join(tags))]
  27. for name, value in extra.items():
  28. lines.append(f"{indent} {name}: {value}\n")
  29. return "".join(lines)
  30. def _processmessage(self, tags: tuple[str, ...], args: tuple[object, ...]) -> None:
  31. if self._writer is not None and args:
  32. self._writer(self._format_message(tags, args))
  33. try:
  34. processor = self._tags2proc[tags]
  35. except KeyError:
  36. pass
  37. else:
  38. processor(tags, args)
  39. def setwriter(self, writer: _Writer | None) -> None:
  40. self._writer = writer
  41. def setprocessor(self, tags: str | tuple[str, ...], processor: _Processor) -> None:
  42. if isinstance(tags, str):
  43. tags = tuple(tags.split(":"))
  44. else:
  45. assert isinstance(tags, tuple)
  46. self._tags2proc[tags] = processor
  47. class TagTracerSub:
  48. def __init__(self, root: TagTracer, tags: tuple[str, ...]) -> None:
  49. self.root = root
  50. self.tags = tags
  51. def __call__(self, *args: object) -> None:
  52. self.root._processmessage(self.tags, args)
  53. def get(self, name: str) -> TagTracerSub:
  54. return self.__class__(self.root, self.tags + (name,))