fastapi-loopguard 0.3.1__tar.gz → 0.4.1__tar.gz
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.
- fastapi_loopguard-0.4.1/PKG-INFO +131 -0
- fastapi_loopguard-0.4.1/README.md +86 -0
- fastapi_loopguard-0.4.1/assets/error-page-screenshot-console.png +0 -0
- fastapi_loopguard-0.4.1/assets/error-page-screenshot-endpoint.png +0 -0
- fastapi_loopguard-0.4.1/assets/error-page-screenshot.png +0 -0
- fastapi_loopguard-0.4.1/docs/CONFIGURATION.md +129 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/pyproject.toml +1 -1
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/config.py +12 -1
- fastapi_loopguard-0.4.1/src/fastapi_loopguard/middleware.py +634 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/tests/test_cumulative_blocking.py +15 -3
- fastapi_loopguard-0.4.1/tests/test_enforcement_mode.py +511 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/tests/test_middleware.py +1 -0
- fastapi_loopguard-0.3.1/PKG-INFO +0 -154
- fastapi_loopguard-0.3.1/README.md +0 -109
- fastapi_loopguard-0.3.1/src/fastapi_loopguard/middleware.py +0 -236
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/.github/dependabot.yml +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/.github/workflows/ci.yml +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/.github/workflows/publish.yml +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/.gitignore +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/CLAUDE.md +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/LICENSE +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/assets/loopguard-logo.webp +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/examples/demo_app.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/examples/locustfile.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/examples/run_stress_test.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/examples/stress_app.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/__init__.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/context.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/logging.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/metrics.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/monitor.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/py.typed +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/pytest_plugin.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/tests/__init__.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/tests/test_config.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/tests/test_context.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/tests/test_logging.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/tests/test_metrics.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/tests/test_monitor.py +0 -0
- {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.1}/tests/test_pytest_plugin.py +0 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-loopguard
|
|
3
|
+
Version: 0.4.1
|
|
4
|
+
Summary: Detect event-loop blocking in FastAPI/Starlette with per-request attribution
|
|
5
|
+
Project-URL: Homepage, https://github.com/parhamdavari/fastapi-loopguard
|
|
6
|
+
Project-URL: Documentation, https://github.com/parhamdavari/fastapi-loopguard#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/parhamdavari/fastapi-loopguard
|
|
8
|
+
Project-URL: Issues, https://github.com/parhamdavari/fastapi-loopguard/issues
|
|
9
|
+
Author: Parham
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: asyncio,blocking,event-loop,fastapi,middleware,monitoring,performance,starlette
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Framework :: AsyncIO
|
|
15
|
+
Classifier: Framework :: FastAPI
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: System :: Monitoring
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.12
|
|
25
|
+
Requires-Dist: starlette<1.0,>=0.37.0
|
|
26
|
+
Provides-Extra: all
|
|
27
|
+
Requires-Dist: prometheus-client>=0.19.0; extra == 'all'
|
|
28
|
+
Requires-Dist: structlog>=24.1.0; extra == 'all'
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: coverage>=7.4.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: fastapi>=0.110.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: httpx>=0.27.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: mypy>=1.8.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: ruff>=0.3.0; extra == 'dev'
|
|
37
|
+
Provides-Extra: prometheus
|
|
38
|
+
Requires-Dist: prometheus-client>=0.19.0; extra == 'prometheus'
|
|
39
|
+
Provides-Extra: stress
|
|
40
|
+
Requires-Dist: locust>=2.20.0; extra == 'stress'
|
|
41
|
+
Requires-Dist: uvicorn>=0.27.0; extra == 'stress'
|
|
42
|
+
Provides-Extra: structlog
|
|
43
|
+
Requires-Dist: structlog>=24.1.0; extra == 'structlog'
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
<p align="center">
|
|
47
|
+
<img src="assets/loopguard-logo.webp" alt="LoopGuard" width="280" />
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
<p align="center">
|
|
51
|
+
<strong>Catch event-loop blocking in FastAPI with per-request attribution.</strong>
|
|
52
|
+
</p>
|
|
53
|
+
|
|
54
|
+
<p align="center">
|
|
55
|
+
<a href="https://badge.fury.io/py/fastapi-loopguard"><img src="https://badge.fury.io/py/fastapi-loopguard.svg" alt="PyPI version"></a>
|
|
56
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.12+-blue.svg" alt="Python 3.12+"></a>
|
|
57
|
+
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
|
58
|
+
</p>
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
When a request blocks your event loop (via `time.sleep()`, blocking I/O, or CPU work), LoopGuard detects it **and tells you which endpoint caused it**.
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install fastapi-loopguard
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Quick Start
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from fastapi import FastAPI
|
|
74
|
+
from fastapi_loopguard import LoopGuardMiddleware
|
|
75
|
+
|
|
76
|
+
app = FastAPI()
|
|
77
|
+
app.add_middleware(LoopGuardMiddleware)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Enforcement Modes
|
|
81
|
+
|
|
82
|
+
| Mode | Behavior | Use Case |
|
|
83
|
+
|------|----------|----------|
|
|
84
|
+
| `"warn"` | Console warnings + headers | **Default** |
|
|
85
|
+
| `"strict"` | HTTP 503 + error page | Development / CI |
|
|
86
|
+
| `"log"` | Silent logging | Production |
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from fastapi_loopguard import LoopGuardConfig
|
|
90
|
+
|
|
91
|
+
# Development: strict enforcement (503 on blocking)
|
|
92
|
+
config = LoopGuardConfig(dev_mode=True)
|
|
93
|
+
|
|
94
|
+
# Production: silent logging
|
|
95
|
+
config = LoopGuardConfig(enforcement_mode="log")
|
|
96
|
+
|
|
97
|
+
app.add_middleware(LoopGuardMiddleware, config=config)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## What You Get
|
|
101
|
+
|
|
102
|
+
### Strict Mode
|
|
103
|
+
Returns an educational 503 page that explains what went wrong and how to fix it:
|
|
104
|
+
|
|
105
|
+
<p align="center">
|
|
106
|
+
<img src="assets/error-page-screenshot.png" alt="Strict mode error page" width="600" />
|
|
107
|
+
</p>
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### Warn Mode
|
|
112
|
+
Adds diagnostic headers to every response for debugging:
|
|
113
|
+
|
|
114
|
+
<p align="center">
|
|
115
|
+
<img src="assets/error-page-screenshot-endpoint.png" alt="Warn mode headers" width="600" />
|
|
116
|
+
</p>
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### Log Mode
|
|
121
|
+
Writes structured logs with full request attribution:
|
|
122
|
+
|
|
123
|
+
<p align="center">
|
|
124
|
+
<img src="assets/error-page-screenshot-console.png" alt="Console output" width="600" />
|
|
125
|
+
</p>
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
<p align="center">
|
|
130
|
+
<a href="docs/CONFIGURATION.md"><strong>Full Configuration Reference</strong></a>
|
|
131
|
+
</p>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/loopguard-logo.webp" alt="LoopGuard" width="280" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<strong>Catch event-loop blocking in FastAPI with per-request attribution.</strong>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://badge.fury.io/py/fastapi-loopguard"><img src="https://badge.fury.io/py/fastapi-loopguard.svg" alt="PyPI version"></a>
|
|
11
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.12+-blue.svg" alt="Python 3.12+"></a>
|
|
12
|
+
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
When a request blocks your event loop (via `time.sleep()`, blocking I/O, or CPU work), LoopGuard detects it **and tells you which endpoint caused it**.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install fastapi-loopguard
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from fastapi import FastAPI
|
|
29
|
+
from fastapi_loopguard import LoopGuardMiddleware
|
|
30
|
+
|
|
31
|
+
app = FastAPI()
|
|
32
|
+
app.add_middleware(LoopGuardMiddleware)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Enforcement Modes
|
|
36
|
+
|
|
37
|
+
| Mode | Behavior | Use Case |
|
|
38
|
+
|------|----------|----------|
|
|
39
|
+
| `"warn"` | Console warnings + headers | **Default** |
|
|
40
|
+
| `"strict"` | HTTP 503 + error page | Development / CI |
|
|
41
|
+
| `"log"` | Silent logging | Production |
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from fastapi_loopguard import LoopGuardConfig
|
|
45
|
+
|
|
46
|
+
# Development: strict enforcement (503 on blocking)
|
|
47
|
+
config = LoopGuardConfig(dev_mode=True)
|
|
48
|
+
|
|
49
|
+
# Production: silent logging
|
|
50
|
+
config = LoopGuardConfig(enforcement_mode="log")
|
|
51
|
+
|
|
52
|
+
app.add_middleware(LoopGuardMiddleware, config=config)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## What You Get
|
|
56
|
+
|
|
57
|
+
### Strict Mode
|
|
58
|
+
Returns an educational 503 page that explains what went wrong and how to fix it:
|
|
59
|
+
|
|
60
|
+
<p align="center">
|
|
61
|
+
<img src="assets/error-page-screenshot.png" alt="Strict mode error page" width="600" />
|
|
62
|
+
</p>
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
### Warn Mode
|
|
67
|
+
Adds diagnostic headers to every response for debugging:
|
|
68
|
+
|
|
69
|
+
<p align="center">
|
|
70
|
+
<img src="assets/error-page-screenshot-endpoint.png" alt="Warn mode headers" width="600" />
|
|
71
|
+
</p>
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
### Log Mode
|
|
76
|
+
Writes structured logs with full request attribution:
|
|
77
|
+
|
|
78
|
+
<p align="center">
|
|
79
|
+
<img src="assets/error-page-screenshot-console.png" alt="Console output" width="600" />
|
|
80
|
+
</p>
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
<p align="center">
|
|
85
|
+
<a href="docs/CONFIGURATION.md"><strong>Full Configuration Reference</strong></a>
|
|
86
|
+
</p>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Configuration Reference
|
|
2
|
+
|
|
3
|
+
All configuration options for `LoopGuardConfig`.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from fastapi_loopguard import LoopGuardConfig
|
|
9
|
+
|
|
10
|
+
config = LoopGuardConfig(
|
|
11
|
+
# Enforcement
|
|
12
|
+
enforcement_mode="warn", # "log" | "warn" | "strict"
|
|
13
|
+
dev_mode=False, # Auto-escalates to strict when True
|
|
14
|
+
|
|
15
|
+
# Detection tuning
|
|
16
|
+
monitor_interval_ms=10.0, # How often to check (ms)
|
|
17
|
+
threshold_multiplier=5.0, # Blocking = lag > baseline × multiplier
|
|
18
|
+
fallback_threshold_ms=50.0, # Threshold if calibration fails
|
|
19
|
+
|
|
20
|
+
# Cumulative detection (enabled by default)
|
|
21
|
+
cumulative_blocking_enabled=True,
|
|
22
|
+
cumulative_blocking_threshold_ms=200.0,
|
|
23
|
+
cumulative_window_ms=1000.0,
|
|
24
|
+
|
|
25
|
+
# Adaptive threshold (disabled by default)
|
|
26
|
+
adaptive_threshold=False,
|
|
27
|
+
|
|
28
|
+
# Integrations
|
|
29
|
+
prometheus_enabled=False,
|
|
30
|
+
log_blocking_events=True,
|
|
31
|
+
)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Core Settings
|
|
37
|
+
|
|
38
|
+
| Option | Type | Default | Description |
|
|
39
|
+
|--------|------|---------|-------------|
|
|
40
|
+
| `enabled` | bool | `True` | Master switch. Set `False` to disable entirely. |
|
|
41
|
+
| `enforcement_mode` | str | `"warn"` | How to respond: `"log"`, `"warn"`, or `"strict"` |
|
|
42
|
+
| `dev_mode` | bool | `False` | Enables response headers. Auto-escalates to strict mode. |
|
|
43
|
+
| `log_blocking_events` | bool | `True` | Log blocking events to console |
|
|
44
|
+
| `exclude_paths` | frozenset | `{"/health", ...}` | Paths to skip monitoring |
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Detection Tuning
|
|
49
|
+
|
|
50
|
+
| Option | Type | Default | Description |
|
|
51
|
+
|--------|------|---------|-------------|
|
|
52
|
+
| `monitor_interval_ms` | float | `10.0` | Sentinel check frequency (ms) |
|
|
53
|
+
| `threshold_multiplier` | float | `5.0` | Blocking detected when lag > baseline × this |
|
|
54
|
+
| `calibration_iterations` | int | `100` | Samples during startup calibration |
|
|
55
|
+
| `fallback_threshold_ms` | float | `50.0` | Used if calibration is unreliable |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Cumulative Blocking Detection
|
|
60
|
+
|
|
61
|
+
Catches "death by a thousand cuts" - many small blocks that add up.
|
|
62
|
+
|
|
63
|
+
| Option | Type | Default | Description |
|
|
64
|
+
|--------|------|---------|-------------|
|
|
65
|
+
| `cumulative_blocking_enabled` | bool | `True` | Enable cumulative detection |
|
|
66
|
+
| `cumulative_blocking_threshold_ms` | float | `200.0` | Alert if total blocking exceeds this... |
|
|
67
|
+
| `cumulative_window_ms` | float | `1000.0` | ...within this time window (ms) |
|
|
68
|
+
|
|
69
|
+
**Example:** With defaults, alerts if blocking totals >200ms within any 1-second window.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Adaptive Threshold
|
|
74
|
+
|
|
75
|
+
Dynamically adjusts threshold based on observed latency. Useful for high-concurrency environments.
|
|
76
|
+
|
|
77
|
+
| Option | Type | Default | Description |
|
|
78
|
+
|--------|------|---------|-------------|
|
|
79
|
+
| `adaptive_threshold` | bool | `False` | Enable adaptive mode |
|
|
80
|
+
| `adaptive_window_size` | int | `1000` | Samples in sliding window |
|
|
81
|
+
| `adaptive_percentile` | float | `0.95` | Percentile for baseline (0.5-0.99) |
|
|
82
|
+
| `adaptive_min_samples` | int | `100` | Min samples before activation |
|
|
83
|
+
| `adaptive_update_interval_ms` | float | `1000.0` | Recalculation frequency (ms) |
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Integrations
|
|
88
|
+
|
|
89
|
+
| Option | Type | Default | Description |
|
|
90
|
+
|--------|------|---------|-------------|
|
|
91
|
+
| `prometheus_enabled` | bool | `False` | Expose Prometheus metrics |
|
|
92
|
+
|
|
93
|
+
When enabled, exposes:
|
|
94
|
+
- `loopguard_blocking_events_total` - Counter of blocking events
|
|
95
|
+
- `loopguard_blocking_duration_ms` - Histogram of blocking durations
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Common Configurations
|
|
100
|
+
|
|
101
|
+
### Development (strict enforcement)
|
|
102
|
+
```python
|
|
103
|
+
config = LoopGuardConfig(dev_mode=True)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Production (silent monitoring)
|
|
107
|
+
```python
|
|
108
|
+
config = LoopGuardConfig(
|
|
109
|
+
enforcement_mode="log",
|
|
110
|
+
prometheus_enabled=True,
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### High-concurrency (adaptive threshold)
|
|
115
|
+
```python
|
|
116
|
+
config = LoopGuardConfig(
|
|
117
|
+
adaptive_threshold=True,
|
|
118
|
+
adaptive_percentile=0.99,
|
|
119
|
+
)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Sensitive detection (lower threshold)
|
|
123
|
+
```python
|
|
124
|
+
config = LoopGuardConfig(
|
|
125
|
+
monitor_interval_ms=5.0,
|
|
126
|
+
threshold_multiplier=3.0,
|
|
127
|
+
fallback_threshold_ms=30.0,
|
|
128
|
+
)
|
|
129
|
+
```
|
|
@@ -34,6 +34,10 @@ class LoopGuardConfig:
|
|
|
34
34
|
log_blocking_events: bool = True
|
|
35
35
|
prometheus_enabled: bool = False
|
|
36
36
|
|
|
37
|
+
# Enforcement mode: how aggressively to respond to blocking
|
|
38
|
+
# "log" = just log (production), "warn" = loud warnings, "strict" = 503 errors
|
|
39
|
+
enforcement_mode: str = "warn"
|
|
40
|
+
|
|
37
41
|
# Adaptive threshold settings
|
|
38
42
|
adaptive_threshold: bool = False
|
|
39
43
|
adaptive_window_size: int = 1000
|
|
@@ -42,7 +46,7 @@ class LoopGuardConfig:
|
|
|
42
46
|
adaptive_update_interval_ms: float = 1000.0
|
|
43
47
|
|
|
44
48
|
# Cumulative blocking detection
|
|
45
|
-
cumulative_blocking_enabled: bool =
|
|
49
|
+
cumulative_blocking_enabled: bool = True
|
|
46
50
|
cumulative_blocking_threshold_ms: float = 200.0
|
|
47
51
|
cumulative_window_ms: float = 1000.0
|
|
48
52
|
|
|
@@ -86,3 +90,10 @@ class LoopGuardConfig:
|
|
|
86
90
|
raise ValueError(
|
|
87
91
|
"cumulative_window_ms cannot be less than monitor_interval_ms"
|
|
88
92
|
)
|
|
93
|
+
# Enforcement mode validation
|
|
94
|
+
valid_enforcement_modes = {"log", "warn", "strict"}
|
|
95
|
+
if self.enforcement_mode not in valid_enforcement_modes:
|
|
96
|
+
raise ValueError(
|
|
97
|
+
f"enforcement_mode must be one of {valid_enforcement_modes}, "
|
|
98
|
+
f"got '{self.enforcement_mode}'"
|
|
99
|
+
)
|