crackerjack 0.31.9__py3-none-any.whl → 0.31.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of crackerjack might be problematic. Click here for more details.
- crackerjack/CLAUDE.md +288 -705
- crackerjack/__main__.py +22 -8
- crackerjack/agents/__init__.py +0 -3
- crackerjack/agents/architect_agent.py +0 -43
- crackerjack/agents/base.py +1 -9
- crackerjack/agents/coordinator.py +2 -148
- crackerjack/agents/documentation_agent.py +109 -81
- crackerjack/agents/dry_agent.py +122 -97
- crackerjack/agents/formatting_agent.py +3 -16
- crackerjack/agents/import_optimization_agent.py +1174 -130
- crackerjack/agents/performance_agent.py +956 -188
- crackerjack/agents/performance_helpers.py +229 -0
- crackerjack/agents/proactive_agent.py +1 -48
- crackerjack/agents/refactoring_agent.py +516 -246
- crackerjack/agents/refactoring_helpers.py +282 -0
- crackerjack/agents/security_agent.py +393 -90
- crackerjack/agents/test_creation_agent.py +1776 -120
- crackerjack/agents/test_specialist_agent.py +59 -15
- crackerjack/agents/tracker.py +0 -102
- crackerjack/api.py +145 -37
- crackerjack/cli/handlers.py +48 -30
- crackerjack/cli/interactive.py +11 -11
- crackerjack/cli/options.py +66 -4
- crackerjack/code_cleaner.py +808 -148
- crackerjack/config/global_lock_config.py +110 -0
- crackerjack/config/hooks.py +43 -64
- crackerjack/core/async_workflow_orchestrator.py +247 -97
- crackerjack/core/autofix_coordinator.py +192 -109
- crackerjack/core/enhanced_container.py +46 -63
- crackerjack/core/file_lifecycle.py +549 -0
- crackerjack/core/performance.py +9 -8
- crackerjack/core/performance_monitor.py +395 -0
- crackerjack/core/phase_coordinator.py +282 -95
- crackerjack/core/proactive_workflow.py +9 -58
- crackerjack/core/resource_manager.py +501 -0
- crackerjack/core/service_watchdog.py +490 -0
- crackerjack/core/session_coordinator.py +4 -8
- crackerjack/core/timeout_manager.py +504 -0
- crackerjack/core/websocket_lifecycle.py +475 -0
- crackerjack/core/workflow_orchestrator.py +355 -204
- crackerjack/dynamic_config.py +47 -6
- crackerjack/errors.py +3 -4
- crackerjack/executors/async_hook_executor.py +63 -13
- crackerjack/executors/cached_hook_executor.py +14 -14
- crackerjack/executors/hook_executor.py +100 -37
- crackerjack/executors/hook_lock_manager.py +856 -0
- crackerjack/executors/individual_hook_executor.py +120 -86
- crackerjack/intelligence/__init__.py +0 -7
- crackerjack/intelligence/adaptive_learning.py +13 -86
- crackerjack/intelligence/agent_orchestrator.py +15 -78
- crackerjack/intelligence/agent_registry.py +12 -59
- crackerjack/intelligence/agent_selector.py +31 -92
- crackerjack/intelligence/integration.py +1 -41
- crackerjack/interactive.py +9 -9
- crackerjack/managers/async_hook_manager.py +25 -8
- crackerjack/managers/hook_manager.py +9 -9
- crackerjack/managers/publish_manager.py +57 -59
- crackerjack/managers/test_command_builder.py +6 -36
- crackerjack/managers/test_executor.py +9 -61
- crackerjack/managers/test_manager.py +52 -62
- crackerjack/managers/test_manager_backup.py +77 -127
- crackerjack/managers/test_progress.py +4 -23
- crackerjack/mcp/cache.py +5 -12
- crackerjack/mcp/client_runner.py +10 -10
- crackerjack/mcp/context.py +64 -6
- crackerjack/mcp/dashboard.py +14 -11
- crackerjack/mcp/enhanced_progress_monitor.py +55 -55
- crackerjack/mcp/file_monitor.py +72 -42
- crackerjack/mcp/progress_components.py +103 -84
- crackerjack/mcp/progress_monitor.py +122 -49
- crackerjack/mcp/rate_limiter.py +12 -12
- crackerjack/mcp/server_core.py +16 -22
- crackerjack/mcp/service_watchdog.py +26 -26
- crackerjack/mcp/state.py +15 -0
- crackerjack/mcp/tools/core_tools.py +95 -39
- crackerjack/mcp/tools/error_analyzer.py +6 -32
- crackerjack/mcp/tools/execution_tools.py +1 -56
- crackerjack/mcp/tools/execution_tools_backup.py +35 -131
- crackerjack/mcp/tools/intelligence_tool_registry.py +0 -36
- crackerjack/mcp/tools/intelligence_tools.py +2 -55
- crackerjack/mcp/tools/monitoring_tools.py +308 -145
- crackerjack/mcp/tools/proactive_tools.py +12 -42
- crackerjack/mcp/tools/progress_tools.py +23 -15
- crackerjack/mcp/tools/utility_tools.py +3 -40
- crackerjack/mcp/tools/workflow_executor.py +40 -60
- crackerjack/mcp/websocket/app.py +0 -3
- crackerjack/mcp/websocket/endpoints.py +206 -268
- crackerjack/mcp/websocket/jobs.py +213 -66
- crackerjack/mcp/websocket/server.py +84 -6
- crackerjack/mcp/websocket/websocket_handler.py +137 -29
- crackerjack/models/config_adapter.py +3 -16
- crackerjack/models/protocols.py +162 -3
- crackerjack/models/resource_protocols.py +454 -0
- crackerjack/models/task.py +3 -3
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +25 -71
- crackerjack/monitoring/regression_prevention.py +28 -87
- crackerjack/orchestration/advanced_orchestrator.py +44 -78
- crackerjack/orchestration/coverage_improvement.py +10 -60
- crackerjack/orchestration/execution_strategies.py +16 -16
- crackerjack/orchestration/test_progress_streamer.py +61 -53
- crackerjack/plugins/base.py +1 -1
- crackerjack/plugins/managers.py +22 -20
- crackerjack/py313.py +65 -21
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +627 -0
- crackerjack/services/cache.py +7 -9
- crackerjack/services/config.py +35 -52
- crackerjack/services/config_integrity.py +5 -16
- crackerjack/services/config_merge.py +542 -0
- crackerjack/services/contextual_ai_assistant.py +17 -19
- crackerjack/services/coverage_ratchet.py +51 -76
- crackerjack/services/debug.py +25 -39
- crackerjack/services/dependency_monitor.py +52 -50
- crackerjack/services/enhanced_filesystem.py +14 -11
- crackerjack/services/file_hasher.py +1 -1
- crackerjack/services/filesystem.py +1 -12
- crackerjack/services/git.py +78 -44
- crackerjack/services/health_metrics.py +31 -27
- crackerjack/services/initialization.py +281 -433
- crackerjack/services/input_validator.py +760 -0
- crackerjack/services/log_manager.py +16 -16
- crackerjack/services/logging.py +7 -6
- crackerjack/services/metrics.py +43 -43
- crackerjack/services/pattern_cache.py +2 -31
- crackerjack/services/pattern_detector.py +26 -63
- crackerjack/services/performance_benchmarks.py +20 -45
- crackerjack/services/regex_patterns.py +2887 -0
- crackerjack/services/regex_utils.py +537 -0
- crackerjack/services/secure_path_utils.py +683 -0
- crackerjack/services/secure_status_formatter.py +534 -0
- crackerjack/services/secure_subprocess.py +605 -0
- crackerjack/services/security.py +47 -10
- crackerjack/services/security_logger.py +492 -0
- crackerjack/services/server_manager.py +109 -50
- crackerjack/services/smart_scheduling.py +8 -25
- crackerjack/services/status_authentication.py +603 -0
- crackerjack/services/status_security_manager.py +442 -0
- crackerjack/services/thread_safe_status_collector.py +546 -0
- crackerjack/services/tool_version_service.py +1 -23
- crackerjack/services/unified_config.py +36 -58
- crackerjack/services/validation_rate_limiter.py +269 -0
- crackerjack/services/version_checker.py +9 -40
- crackerjack/services/websocket_resource_limiter.py +572 -0
- crackerjack/slash_commands/__init__.py +52 -2
- crackerjack/tools/__init__.py +0 -0
- crackerjack/tools/validate_input_validator_patterns.py +262 -0
- crackerjack/tools/validate_regex_patterns.py +198 -0
- {crackerjack-0.31.9.dist-info → crackerjack-0.31.12.dist-info}/METADATA +197 -12
- crackerjack-0.31.12.dist-info/RECORD +178 -0
- crackerjack/cli/facade.py +0 -104
- crackerjack-0.31.9.dist-info/RECORD +0 -149
- {crackerjack-0.31.9.dist-info → crackerjack-0.31.12.dist-info}/WHEEL +0 -0
- {crackerjack-0.31.9.dist-info → crackerjack-0.31.12.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.31.9.dist-info → crackerjack-0.31.12.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Performance monitoring and metrics collection for async operations.
|
|
3
|
+
|
|
4
|
+
This module provides comprehensive monitoring of async operations,
|
|
5
|
+
timeout tracking, and performance analysis capabilities.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
import time
|
|
11
|
+
import typing as t
|
|
12
|
+
from collections import defaultdict, deque
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from threading import Lock
|
|
16
|
+
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
from rich.table import Table
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger("crackerjack.performance_monitor")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class OperationMetrics:
|
|
25
|
+
"""Metrics for a specific operation."""
|
|
26
|
+
|
|
27
|
+
operation_name: str
|
|
28
|
+
total_calls: int = 0
|
|
29
|
+
successful_calls: int = 0
|
|
30
|
+
failed_calls: int = 0
|
|
31
|
+
timeout_calls: int = 0
|
|
32
|
+
total_time: float = 0.0
|
|
33
|
+
min_time: float = float("inf")
|
|
34
|
+
max_time: float = 0.0
|
|
35
|
+
recent_times: deque[float] = field(default_factory=lambda: deque(maxlen=100))
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def success_rate(self) -> float:
|
|
39
|
+
"""Calculate success rate as percentage."""
|
|
40
|
+
if self.total_calls == 0:
|
|
41
|
+
return 0.0
|
|
42
|
+
return (self.successful_calls / self.total_calls) * 100
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def average_time(self) -> float:
|
|
46
|
+
"""Calculate average execution time."""
|
|
47
|
+
if self.successful_calls == 0:
|
|
48
|
+
return 0.0
|
|
49
|
+
return self.total_time / self.successful_calls
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def recent_average_time(self) -> float:
|
|
53
|
+
"""Calculate recent average execution time."""
|
|
54
|
+
if not self.recent_times:
|
|
55
|
+
return 0.0
|
|
56
|
+
return sum(self.recent_times) / len(self.recent_times)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass
|
|
60
|
+
class TimeoutEvent:
|
|
61
|
+
"""Record of a timeout event."""
|
|
62
|
+
|
|
63
|
+
operation: str
|
|
64
|
+
expected_timeout: float
|
|
65
|
+
actual_duration: float
|
|
66
|
+
timestamp: float
|
|
67
|
+
error_message: str = ""
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class AsyncPerformanceMonitor:
|
|
71
|
+
"""Monitor and track performance of async operations."""
|
|
72
|
+
|
|
73
|
+
def __init__(self, max_timeout_events: int = 1000) -> None:
|
|
74
|
+
self.metrics: dict[str, OperationMetrics] = {}
|
|
75
|
+
self.timeout_events: deque[TimeoutEvent] = deque(maxlen=max_timeout_events)
|
|
76
|
+
self._lock = Lock()
|
|
77
|
+
self.start_time = time.time()
|
|
78
|
+
|
|
79
|
+
# Circuit breaker tracking
|
|
80
|
+
self.circuit_breaker_events: dict[str, list[float]] = defaultdict(list)
|
|
81
|
+
|
|
82
|
+
# Performance thresholds
|
|
83
|
+
self.performance_thresholds: dict[str, dict[str, float]] = {
|
|
84
|
+
"default": {
|
|
85
|
+
"warning_time": 30.0,
|
|
86
|
+
"critical_time": 60.0,
|
|
87
|
+
"min_success_rate": 80.0,
|
|
88
|
+
},
|
|
89
|
+
"fast_hooks": {
|
|
90
|
+
"warning_time": 30.0,
|
|
91
|
+
"critical_time": 90.0,
|
|
92
|
+
"min_success_rate": 95.0,
|
|
93
|
+
},
|
|
94
|
+
"comprehensive_hooks": {
|
|
95
|
+
"warning_time": 120.0,
|
|
96
|
+
"critical_time": 300.0,
|
|
97
|
+
"min_success_rate": 90.0,
|
|
98
|
+
},
|
|
99
|
+
"test_execution": {
|
|
100
|
+
"warning_time": 300.0,
|
|
101
|
+
"critical_time": 600.0,
|
|
102
|
+
"min_success_rate": 85.0,
|
|
103
|
+
},
|
|
104
|
+
"ai_agent_processing": {
|
|
105
|
+
"warning_time": 60.0,
|
|
106
|
+
"critical_time": 180.0,
|
|
107
|
+
"min_success_rate": 75.0,
|
|
108
|
+
},
|
|
109
|
+
"network_operations": {
|
|
110
|
+
"warning_time": 5.0,
|
|
111
|
+
"critical_time": 15.0,
|
|
112
|
+
"min_success_rate": 95.0,
|
|
113
|
+
},
|
|
114
|
+
"file_operations": {
|
|
115
|
+
"warning_time": 3.0,
|
|
116
|
+
"critical_time": 10.0,
|
|
117
|
+
"min_success_rate": 98.0,
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
def record_operation_start(self, operation: str) -> float:
|
|
122
|
+
"""Record the start of an operation."""
|
|
123
|
+
return time.time()
|
|
124
|
+
|
|
125
|
+
def record_operation_success(self, operation: str, start_time: float) -> None:
|
|
126
|
+
"""Record successful completion of an operation."""
|
|
127
|
+
duration = time.time() - start_time
|
|
128
|
+
|
|
129
|
+
with self._lock:
|
|
130
|
+
if operation not in self.metrics:
|
|
131
|
+
self.metrics[operation] = OperationMetrics(operation)
|
|
132
|
+
|
|
133
|
+
metrics = self.metrics[operation]
|
|
134
|
+
metrics.total_calls += 1
|
|
135
|
+
metrics.successful_calls += 1
|
|
136
|
+
metrics.total_time += duration
|
|
137
|
+
metrics.min_time = min(metrics.min_time, duration)
|
|
138
|
+
metrics.max_time = max(metrics.max_time, duration)
|
|
139
|
+
metrics.recent_times.append(duration)
|
|
140
|
+
|
|
141
|
+
def record_operation_failure(self, operation: str, start_time: float) -> None:
|
|
142
|
+
"""Record failed completion of an operation."""
|
|
143
|
+
duration = time.time() - start_time
|
|
144
|
+
|
|
145
|
+
with self._lock:
|
|
146
|
+
if operation not in self.metrics:
|
|
147
|
+
self.metrics[operation] = OperationMetrics(operation)
|
|
148
|
+
|
|
149
|
+
metrics = self.metrics[operation]
|
|
150
|
+
metrics.total_calls += 1
|
|
151
|
+
metrics.failed_calls += 1
|
|
152
|
+
metrics.total_time += duration
|
|
153
|
+
metrics.min_time = min(metrics.min_time, duration)
|
|
154
|
+
metrics.max_time = max(metrics.max_time, duration)
|
|
155
|
+
metrics.recent_times.append(duration)
|
|
156
|
+
|
|
157
|
+
def record_operation_timeout(
|
|
158
|
+
self,
|
|
159
|
+
operation: str,
|
|
160
|
+
start_time: float,
|
|
161
|
+
expected_timeout: float,
|
|
162
|
+
error_message: str = "",
|
|
163
|
+
) -> None:
|
|
164
|
+
"""Record timeout of an operation."""
|
|
165
|
+
duration = time.time() - start_time
|
|
166
|
+
|
|
167
|
+
with self._lock:
|
|
168
|
+
if operation not in self.metrics:
|
|
169
|
+
self.metrics[operation] = OperationMetrics(operation)
|
|
170
|
+
|
|
171
|
+
metrics = self.metrics[operation]
|
|
172
|
+
metrics.total_calls += 1
|
|
173
|
+
metrics.timeout_calls += 1
|
|
174
|
+
|
|
175
|
+
# Record timeout event
|
|
176
|
+
timeout_event = TimeoutEvent(
|
|
177
|
+
operation=operation,
|
|
178
|
+
expected_timeout=expected_timeout,
|
|
179
|
+
actual_duration=duration,
|
|
180
|
+
timestamp=time.time(),
|
|
181
|
+
error_message=error_message,
|
|
182
|
+
)
|
|
183
|
+
self.timeout_events.append(timeout_event)
|
|
184
|
+
|
|
185
|
+
def record_circuit_breaker_event(self, operation: str, opened: bool) -> None:
|
|
186
|
+
"""Record circuit breaker state change."""
|
|
187
|
+
with self._lock:
|
|
188
|
+
if opened:
|
|
189
|
+
self.circuit_breaker_events[operation].append(time.time())
|
|
190
|
+
|
|
191
|
+
def get_operation_metrics(self, operation: str) -> OperationMetrics | None:
|
|
192
|
+
"""Get metrics for a specific operation."""
|
|
193
|
+
with self._lock:
|
|
194
|
+
return self.metrics.get(operation)
|
|
195
|
+
|
|
196
|
+
def get_all_metrics(self) -> dict[str, OperationMetrics]:
|
|
197
|
+
"""Get all operation metrics."""
|
|
198
|
+
with self._lock:
|
|
199
|
+
return self.metrics.copy()
|
|
200
|
+
|
|
201
|
+
def get_recent_timeout_events(self, limit: int = 10) -> list[TimeoutEvent]:
|
|
202
|
+
"""Get recent timeout events."""
|
|
203
|
+
with self._lock:
|
|
204
|
+
return list(self.timeout_events)[-limit:]
|
|
205
|
+
|
|
206
|
+
def get_performance_alerts(self) -> list[dict[str, t.Any]]:
|
|
207
|
+
"""Get current performance alerts."""
|
|
208
|
+
alerts = []
|
|
209
|
+
|
|
210
|
+
with self._lock:
|
|
211
|
+
for operation, metrics in self.metrics.items():
|
|
212
|
+
thresholds = self.performance_thresholds.get(
|
|
213
|
+
operation, self.performance_thresholds["default"]
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Check success rate
|
|
217
|
+
if metrics.success_rate < thresholds["min_success_rate"]:
|
|
218
|
+
alerts.append(
|
|
219
|
+
{
|
|
220
|
+
"type": "success_rate",
|
|
221
|
+
"operation": operation,
|
|
222
|
+
"current_value": metrics.success_rate,
|
|
223
|
+
"threshold": thresholds["min_success_rate"],
|
|
224
|
+
"severity": "critical"
|
|
225
|
+
if metrics.success_rate < 50
|
|
226
|
+
else "warning",
|
|
227
|
+
}
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
# Check average response time
|
|
231
|
+
avg_time = metrics.recent_average_time
|
|
232
|
+
if avg_time > thresholds["critical_time"]:
|
|
233
|
+
alerts.append(
|
|
234
|
+
{
|
|
235
|
+
"type": "response_time",
|
|
236
|
+
"operation": operation,
|
|
237
|
+
"current_value": avg_time,
|
|
238
|
+
"threshold": thresholds["critical_time"],
|
|
239
|
+
"severity": "critical",
|
|
240
|
+
}
|
|
241
|
+
)
|
|
242
|
+
elif avg_time > thresholds["warning_time"]:
|
|
243
|
+
alerts.append(
|
|
244
|
+
{
|
|
245
|
+
"type": "response_time",
|
|
246
|
+
"operation": operation,
|
|
247
|
+
"current_value": avg_time,
|
|
248
|
+
"threshold": thresholds["warning_time"],
|
|
249
|
+
"severity": "warning",
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
return alerts
|
|
254
|
+
|
|
255
|
+
def get_summary_stats(self) -> dict[str, t.Any]:
|
|
256
|
+
"""Get summary statistics."""
|
|
257
|
+
with self._lock:
|
|
258
|
+
total_calls = sum(m.total_calls for m in self.metrics.values())
|
|
259
|
+
total_successes = sum(m.successful_calls for m in self.metrics.values())
|
|
260
|
+
total_timeouts = sum(m.timeout_calls for m in self.metrics.values())
|
|
261
|
+
total_failures = sum(m.failed_calls for m in self.metrics.values())
|
|
262
|
+
|
|
263
|
+
uptime = time.time() - self.start_time
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
"uptime_seconds": uptime,
|
|
267
|
+
"total_operations": total_calls,
|
|
268
|
+
"total_successes": total_successes,
|
|
269
|
+
"total_timeouts": total_timeouts,
|
|
270
|
+
"total_failures": total_failures,
|
|
271
|
+
"overall_success_rate": (total_successes / total_calls * 100)
|
|
272
|
+
if total_calls > 0
|
|
273
|
+
else 0.0,
|
|
274
|
+
"timeout_rate": (total_timeouts / total_calls * 100)
|
|
275
|
+
if total_calls > 0
|
|
276
|
+
else 0.0,
|
|
277
|
+
"operations_per_minute": (total_calls / (uptime / 60))
|
|
278
|
+
if uptime > 0
|
|
279
|
+
else 0.0,
|
|
280
|
+
"unique_operations": len(self.metrics),
|
|
281
|
+
"circuit_breaker_trips": len(self.circuit_breaker_events),
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
def export_metrics_json(self, filepath: Path) -> None:
|
|
285
|
+
"""Export metrics to JSON file."""
|
|
286
|
+
with self._lock:
|
|
287
|
+
data = {
|
|
288
|
+
"summary": self.get_summary_stats(),
|
|
289
|
+
"operations": {
|
|
290
|
+
name: {
|
|
291
|
+
"total_calls": m.total_calls,
|
|
292
|
+
"successful_calls": m.successful_calls,
|
|
293
|
+
"failed_calls": m.failed_calls,
|
|
294
|
+
"timeout_calls": m.timeout_calls,
|
|
295
|
+
"success_rate": m.success_rate,
|
|
296
|
+
"average_time": m.average_time,
|
|
297
|
+
"recent_average_time": m.recent_average_time,
|
|
298
|
+
"min_time": m.min_time if m.min_time != float("inf") else 0,
|
|
299
|
+
"max_time": m.max_time,
|
|
300
|
+
}
|
|
301
|
+
for name, m in self.metrics.items()
|
|
302
|
+
},
|
|
303
|
+
"recent_timeout_events": [
|
|
304
|
+
{
|
|
305
|
+
"operation": event.operation,
|
|
306
|
+
"expected_timeout": event.expected_timeout,
|
|
307
|
+
"actual_duration": event.actual_duration,
|
|
308
|
+
"timestamp": event.timestamp,
|
|
309
|
+
"error_message": event.error_message,
|
|
310
|
+
}
|
|
311
|
+
for event in list(self.timeout_events)[-50:]
|
|
312
|
+
],
|
|
313
|
+
"performance_alerts": self.get_performance_alerts(),
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
filepath.write_text(json.dumps(data, indent=2))
|
|
317
|
+
|
|
318
|
+
def print_performance_report(self, console: Console | None = None) -> None:
|
|
319
|
+
"""Print a formatted performance report."""
|
|
320
|
+
if console is None:
|
|
321
|
+
console = Console()
|
|
322
|
+
|
|
323
|
+
console.print("\n[bold blue]🔍 Async Performance Monitor Report[/bold blue]")
|
|
324
|
+
console.print("=" * 60)
|
|
325
|
+
|
|
326
|
+
# Summary stats
|
|
327
|
+
summary = self.get_summary_stats()
|
|
328
|
+
console.print(f"⏱️ Uptime: {summary['uptime_seconds']:.1f}s")
|
|
329
|
+
console.print(f"📊 Total Operations: {summary['total_operations']}")
|
|
330
|
+
console.print(f"✅ Success Rate: {summary['overall_success_rate']:.1f}%")
|
|
331
|
+
console.print(f"⏰ Timeout Rate: {summary['timeout_rate']:.1f}%")
|
|
332
|
+
console.print(f"🚀 Operations/min: {summary['operations_per_minute']:.1f}")
|
|
333
|
+
|
|
334
|
+
# Operation metrics table
|
|
335
|
+
if self.metrics:
|
|
336
|
+
console.print("\n[bold]Operation Metrics:[/bold]")
|
|
337
|
+
table = Table()
|
|
338
|
+
table.add_column("Operation")
|
|
339
|
+
table.add_column("Calls")
|
|
340
|
+
table.add_column("Success %")
|
|
341
|
+
table.add_column("Avg Time")
|
|
342
|
+
table.add_column("Recent Avg")
|
|
343
|
+
table.add_column("Timeouts")
|
|
344
|
+
|
|
345
|
+
with self._lock:
|
|
346
|
+
for name, metrics in sorted(self.metrics.items()):
|
|
347
|
+
table.add_row(
|
|
348
|
+
name,
|
|
349
|
+
str(metrics.total_calls),
|
|
350
|
+
f"{metrics.success_rate:.1f}%",
|
|
351
|
+
f"{metrics.average_time:.2f}s",
|
|
352
|
+
f"{metrics.recent_average_time:.2f}s",
|
|
353
|
+
str(metrics.timeout_calls),
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
console.print(table)
|
|
357
|
+
|
|
358
|
+
# Performance alerts
|
|
359
|
+
alerts = self.get_performance_alerts()
|
|
360
|
+
if alerts:
|
|
361
|
+
console.print("\n[bold red]⚠️ Performance Alerts:[/bold red]")
|
|
362
|
+
for alert in alerts:
|
|
363
|
+
severity_emoji = "🔴" if alert["severity"] == "critical" else "🟡"
|
|
364
|
+
console.print(
|
|
365
|
+
f"{severity_emoji} {alert['operation']}: {alert['type']} "
|
|
366
|
+
f"{alert['current_value']:.1f} (threshold: {alert['threshold']:.1f})"
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
# Recent timeouts
|
|
370
|
+
recent_timeouts = self.get_recent_timeout_events(5)
|
|
371
|
+
if recent_timeouts:
|
|
372
|
+
console.print("\n[bold yellow]⏰ Recent Timeouts:[/bold yellow]")
|
|
373
|
+
for timeout in recent_timeouts:
|
|
374
|
+
console.print(
|
|
375
|
+
f" • {timeout.operation}: {timeout.actual_duration:.1f}s "
|
|
376
|
+
f"(expected: {timeout.expected_timeout:.1f}s)"
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
# Global performance monitor instance
|
|
381
|
+
_global_performance_monitor: AsyncPerformanceMonitor | None = None
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def get_performance_monitor() -> AsyncPerformanceMonitor:
|
|
385
|
+
"""Get the global performance monitor instance."""
|
|
386
|
+
global _global_performance_monitor
|
|
387
|
+
if _global_performance_monitor is None:
|
|
388
|
+
_global_performance_monitor = AsyncPerformanceMonitor()
|
|
389
|
+
return _global_performance_monitor
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def reset_performance_monitor() -> None:
|
|
393
|
+
"""Reset the global performance monitor."""
|
|
394
|
+
global _global_performance_monitor
|
|
395
|
+
_global_performance_monitor = AsyncPerformanceMonitor()
|