Coverage for / home / runner / work / bijux-cli / bijux-cli / src / bijux_cli / infra / telemetry.py: 100%
77 statements
« prev ^ index » next coverage.py v7.13.2, created at 2026-01-26 17:59 +0000
« prev ^ index » next coverage.py v7.13.2, created at 2026-01-26 17:59 +0000
1# SPDX-License-Identifier: Apache-2.0
2# Copyright © 2025 Bijan Mousavi
4"""Telemetry adapter interfaces and default implementations."""
6from __future__ import annotations
8from enum import Enum
9from typing import Any
12class TelemetryEvent(str, Enum):
13 """Standardized telemetry event names."""
15 CLI_STARTED = "cli_started"
16 CLI_ERROR = "cli_error"
17 CLI_INTERRUPTED = "cli_interrupted"
18 CLI_SYSTEM_EXIT = "cli_system_exit"
19 CLI_UNEXPECTED_ERROR = "cli_unexpected_error"
20 CLI_SHUTDOWN_FAILED = "cli_shutdown_failed"
21 ENGINE_INITIALIZED = "engine_initialized"
22 ENGINE_SHUTDOWN = "engine_shutdown"
23 PLUGINS_LIST_COMMAND = "cmd/plugins/list"
24 PLUGINS_LIST_COMMAND_FAILED = "cmd/err/plugins/list"
25 PLUGINS_INFO_COMMAND = "cmd/plugins/info"
26 PLUGINS_INFO_COMMAND_FAILED = "cmd/err/plugins/info"
27 PLUGINS_INFO_NOT_FOUND = "cmd/err/plugins/info/not_found"
28 PLUGINS_INSTALL_COMMAND = "cmd/plugins/install"
29 PLUGINS_INSTALL_COMMAND_FAILED = "cmd/err/plugins/install"
30 PLUGINS_UNINSTALL_COMMAND = "cmd/plugins/uninstall"
31 PLUGINS_UNINSTALL_COMMAND_FAILED = "cmd/err/plugins/uninstall"
32 PLUGINS_UNINSTALL_NOT_FOUND = "cmd/err/plugins/uninstall/not_found"
33 PLUGINS_CHECK_COMMAND = "cmd/plugins/check"
34 PLUGINS_CHECK_COMMAND_FAILED = "cmd/err/plugins/check"
35 PLUGINS_CHECK_NOT_FOUND = "cmd/err/plugins/check/not_found"
36 PLUGINS_SCAFFOLD_COMMAND = "cmd/plugins/scaffold"
37 PLUGINS_SCAFFOLD_COMMAND_FAILED = "cmd/err/plugins/scaffold"
38 PLUGINS_SCAFFOLD_DIR_EXISTS = "cmd/err/plugins/scaffold/dir_exists"
39 CONFIG_COMMAND = "cmd/config"
40 CONFIG_COMMAND_FAILED = "cmd/err/config"
41 AUDIT_COMMAND = "cmd/audit"
42 AUDIT_COMMAND_FAILED = "cmd/err/audit"
43 DOCTOR_COMMAND = "cmd/doctor"
44 DOCTOR_COMMAND_FAILED = "cmd/err/doctor"
45 VERSION_COMMAND = "cmd/version"
46 VERSION_COMMAND_FAILED = "cmd/err/version"
47 STATUS_COMMAND = "cmd/status"
48 STATUS_COMMAND_FAILED = "cmd/err/status"
49 SLEEP_COMMAND = "cmd/test/sleep"
50 SLEEP_COMMAND_FAILED = "cmd/err/test/sleep"
51 HISTORY_COMMAND = "cmd/history"
52 HISTORY_COMMAND_FAILED = "cmd/err/history"
53 REPL_COMMAND = "cmd/repl"
54 REPL_EXIT = "cmd/repl/exit"
55 REPL_COMMAND_NOT_FOUND = "cmd/err/repl/not_found"
56 DEV_COMMAND_EXECUTED = "cmd/dev"
57 DEV_COMMAND_FAILED = "cmd/err/dev"
58 MEMORY_COMMAND_EXECUTED = "cmd/memory"
59 MEMORY_COMMAND_FAILED = "cmd/err/memory"
60 HELP_COMMAND = "cmd/help"
61 HELP_COMMAND_FAILED = "cmd/err/help"
62 PLUGIN_STARTED = "plugin_started"
63 PLUGIN_SHUTDOWN = "plugin_shutdown"
64 PLUGIN_INSTALLED = "plugin_installed"
65 PLUGIN_LOADED = "plugin_loaded"
66 PLUGIN_CLI_REGISTERED = "plugin_cli_registered"
67 PLUGIN_LOAD_FAILED = "plugin_load_failed"
70class NoopTelemetry:
71 """No-op telemetry adapter."""
73 def event(self, name: str | TelemetryEvent, payload: dict[str, Any]) -> None:
74 """Record a telemetry event and do nothing."""
75 return None
77 def flush(self) -> None:
78 """Flush buffered events (no-op)."""
79 return None
81 def enable(self) -> None:
82 """Enable telemetry (no-op)."""
83 return None
86class LoggingTelemetry:
87 """Telemetry adapter that logs events via an observability sink."""
89 def __init__(self, observability: Any) -> None:
90 """Initialize with an observability sink."""
91 self._observability = observability
92 self._buffer: list[tuple[str, dict[str, Any]]] = []
94 def event(self, name: str | TelemetryEvent, payload: dict[str, Any]) -> None:
95 """Record and log a telemetry event."""
96 event = name.value if isinstance(name, TelemetryEvent) else str(name)
97 self._buffer.append((event, payload))
98 self._observability.log(
99 "debug", f"telemetry:{event}", extra={"event": event, **payload}
100 )
102 def flush(self) -> None:
103 """Clear buffered telemetry events."""
104 self._buffer.clear()
106 def enable(self) -> None:
107 """Enable telemetry (no-op for logging adapter)."""
108 return None
111__all__ = [
112 "TelemetryEvent",
113 "NoopTelemetry",
114 "LoggingTelemetry",
115]