fix(k1): stabilize repeated acquisition and live viewer recovery
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from datetime import UTC, datetime
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from pathlib import Path
|
||||
from typing import Final
|
||||
|
||||
SCANNER_LOGGER_NAME: Final = "k1link.device_plugins.xgrids_k1"
|
||||
SCANNER_DIAGNOSTIC_FILE: Final = "scanner-diagnostics.jsonl"
|
||||
_HANDLER_MARKER: Final = "_mission_core_scanner_diagnostics_path"
|
||||
_EXTRA_FIELDS: Final = (
|
||||
"event_code",
|
||||
"reason_code",
|
||||
"failed_phase",
|
||||
"dialogue_stage",
|
||||
"transport_state",
|
||||
"publish_attempts",
|
||||
"modeling_command_attempted",
|
||||
"outcome_unknown",
|
||||
"safe_to_retry",
|
||||
"status_reconciliation",
|
||||
"network_change_admissible",
|
||||
"network_change_reconciliation",
|
||||
"mqtt_loop_result_code",
|
||||
"mqtt_loop_result_name",
|
||||
"mqtt_loop_phase",
|
||||
"automatic_retry",
|
||||
"camera_source_id",
|
||||
"evidence_session_id",
|
||||
"activation_trigger",
|
||||
"lease_generation",
|
||||
"lease_state",
|
||||
"recovery_strategy",
|
||||
"endpoint_reachable",
|
||||
"host_route_class",
|
||||
"address_changed",
|
||||
"device_write_performed",
|
||||
"failure_stage",
|
||||
"start_checkpoint_released",
|
||||
"stale_session_retired",
|
||||
"stream_id",
|
||||
"backend_activity_sequence",
|
||||
"viewer_range_max_ns",
|
||||
"stalled_for_ms",
|
||||
"recovery_attempt",
|
||||
"preferred_port",
|
||||
"selected_port",
|
||||
)
|
||||
|
||||
|
||||
class ScannerDiagnosticJsonFormatter(logging.Formatter):
|
||||
"""Serialize a bounded, secret-free scanner diagnostic record."""
|
||||
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
document: dict[str, object] = {
|
||||
"timestamp_utc": datetime.fromtimestamp(record.created, UTC)
|
||||
.isoformat()
|
||||
.replace("+00:00", "Z"),
|
||||
"level": record.levelname,
|
||||
"logger": record.name,
|
||||
"message": record.getMessage()[:1000],
|
||||
}
|
||||
for field in _EXTRA_FIELDS:
|
||||
value = getattr(record, field, None)
|
||||
if value is not None and isinstance(value, (str, int, bool)):
|
||||
document[field] = value
|
||||
return json.dumps(document, ensure_ascii=False, separators=(",", ":"))
|
||||
|
||||
|
||||
def configure_scanner_diagnostics(logs_root: Path) -> Path:
|
||||
"""Install one private rotating JSONL handler for K1 field diagnostics."""
|
||||
|
||||
logs_root.mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||
os.chmod(logs_root, 0o700)
|
||||
target = (logs_root / SCANNER_DIAGNOSTIC_FILE).resolve()
|
||||
logger = logging.getLogger(SCANNER_LOGGER_NAME)
|
||||
for handler in logger.handlers:
|
||||
if getattr(handler, _HANDLER_MARKER, None) == str(target):
|
||||
return target
|
||||
|
||||
handler = RotatingFileHandler(
|
||||
target,
|
||||
maxBytes=5 * 1024 * 1024,
|
||||
backupCount=5,
|
||||
encoding="utf-8",
|
||||
delay=False,
|
||||
)
|
||||
os.chmod(target, 0o600)
|
||||
handler.setLevel(logging.INFO)
|
||||
handler.setFormatter(ScannerDiagnosticJsonFormatter())
|
||||
setattr(handler, _HANDLER_MARKER, str(target))
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(logging.INFO)
|
||||
return target
|
||||
Reference in New Issue
Block a user