_debug.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. """Env-flagged wire-payload dump for VP MQTT debug (gated; off by default).
  2. Set ``BAMBUDDY_VP_DUMP_WIRE=1`` to write the most recent inbound (bridge
  3. cache input) and outbound (slicer-facing 1Hz push) MQTT payloads to disk,
  4. one file per VP per direction, overwritten each tick.
  5. Used to triage shape-of-payload bugs (e.g. #1622) where the question is
  6. "is the bridge missing fields in the cache, or is something else stripping
  7. them on the way out to the slicer?" Compare ``*_in.json`` and ``*_out.json``
  8. for the failing VP against a known-good VP (e.g. H2D vs P1S).
  9. Layout: ``<log_dir>/vp_wire/<sanitized_vp_name>_<direction>.json``
  10. Failure modes are swallowed at debug level — debug instrumentation must
  11. never break the bridge or slicer-facing 1Hz loop. Disable by unsetting the
  12. env var; the in-progress files stay on disk and can be deleted manually.
  13. """
  14. from __future__ import annotations
  15. import json
  16. import logging
  17. import os
  18. import re
  19. from backend.app.core.config import settings as app_settings
  20. logger = logging.getLogger(__name__)
  21. _ENV_FLAG = "BAMBUDDY_VP_DUMP_WIRE"
  22. _NAME_SAFE = re.compile(r"[^A-Za-z0-9._-]+")
  23. def _enabled() -> bool:
  24. return os.environ.get(_ENV_FLAG, "").strip().lower() in ("1", "true", "yes", "on")
  25. def _sanitize(name: str) -> str:
  26. safe = _NAME_SAFE.sub("_", name or "vp").strip("_")
  27. return safe or "vp"
  28. def dump_wire(vp_name: str, direction: str, payload: dict | bytes | str) -> None:
  29. """Write ``payload`` to ``<log_dir>/vp_wire/<vp_name>_<direction>.json``.
  30. No-op when the env flag is unset. Accepts dict (json-encoded with
  31. ``indent=2``), bytes (decoded as utf-8 with errors='replace'), or
  32. str (written verbatim).
  33. """
  34. if not _enabled():
  35. return
  36. try:
  37. target_dir = app_settings.log_dir / "vp_wire"
  38. target_dir.mkdir(parents=True, exist_ok=True)
  39. path = target_dir / f"{_sanitize(vp_name)}_{_sanitize(direction)}.json"
  40. if isinstance(payload, dict):
  41. text = json.dumps(payload, indent=2, default=str)
  42. elif isinstance(payload, bytes):
  43. text = payload.decode("utf-8", errors="replace")
  44. else:
  45. text = str(payload)
  46. tmp = path.with_suffix(path.suffix + ".tmp")
  47. tmp.write_text(text, encoding="utf-8")
  48. tmp.replace(path)
  49. except OSError as e:
  50. logger.debug("[%s] vp_wire dump (%s) failed: %s", vp_name, direction, e)