debugbundle-python 0.1.2__tar.gz → 0.1.3__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.
- {debugbundle_python-0.1.2/src/debugbundle_python.egg-info → debugbundle_python-0.1.3}/PKG-INFO +1 -1
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/pyproject.toml +1 -1
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/core.py +29 -2
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3/src/debugbundle_python.egg-info}/PKG-INFO +1 -1
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/tests/test_remote_config.py +85 -1
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/LICENSE +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/README.md +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/setup.cfg +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/__init__.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/config.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/__init__.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/common.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/django.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/fastapi.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/flask.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/relay_django.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/relay_fastapi.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/relay_flask.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/logger_integrations.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/py.typed +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/redaction.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/relay.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/suppression.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/transport.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/trigger_token.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle_python.egg-info/SOURCES.txt +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle_python.egg-info/dependency_links.txt +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle_python.egg-info/requires.txt +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle_python.egg-info/top_level.txt +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/tests/test_contracts.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/tests/test_framework_integrations.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/tests/test_hooks.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/tests/test_http_integration.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/tests/test_relay.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/tests/test_repository_metadata.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/tests/test_schema_validation.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/tests/test_sdk.py +0 -0
- {debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/tests/test_trigger_token.py +0 -0
|
@@ -53,6 +53,11 @@ LEVEL_RANKS = {
|
|
|
53
53
|
"error": 40,
|
|
54
54
|
"critical": 50,
|
|
55
55
|
}
|
|
56
|
+
BALANCED_IMMEDIATE_REQUEST_STATUSES = {408, 423, 424, 425, 429}
|
|
57
|
+
INVESTIGATIVE_IMMEDIATE_REQUEST_STATUSES = BALANCED_IMMEDIATE_REQUEST_STATUSES | {409}
|
|
58
|
+
BALANCED_STANDARD_ANOMALY_STATUSES = {401, 403, 404, 409, 422}
|
|
59
|
+
BALANCED_HIGH_VOLUME_ANOMALY_STATUSES = {400, 410}
|
|
60
|
+
INVESTIGATIVE_ANOMALY_STATUSES = BALANCED_STANDARD_ANOMALY_STATUSES | BALANCED_HIGH_VOLUME_ANOMALY_STATUSES
|
|
56
61
|
|
|
57
62
|
|
|
58
63
|
@dataclass
|
|
@@ -604,7 +609,7 @@ class DebugBundleSdk:
|
|
|
604
609
|
candidate = response.get("status_code") or response.get("response_status")
|
|
605
610
|
if isinstance(candidate, int):
|
|
606
611
|
status_code = candidate
|
|
607
|
-
if status_code
|
|
612
|
+
if _is_immediate_request_incident_status(status_code, self._capture_policy.preset):
|
|
608
613
|
return True
|
|
609
614
|
if policy == "off":
|
|
610
615
|
return False
|
|
@@ -615,7 +620,7 @@ class DebugBundleSdk:
|
|
|
615
620
|
if status_code is None:
|
|
616
621
|
return policy == "filtered"
|
|
617
622
|
if policy == "failures_only":
|
|
618
|
-
return
|
|
623
|
+
return _is_request_anomaly_candidate_status(status_code, self._capture_policy.preset)
|
|
619
624
|
if policy == "filtered":
|
|
620
625
|
return False
|
|
621
626
|
return True
|
|
@@ -846,6 +851,28 @@ def _iso_now(time_provider: Callable[[], float]) -> str:
|
|
|
846
851
|
return datetime.fromtimestamp(time_provider(), tz=timezone.utc).isoformat().replace("+00:00", "Z")
|
|
847
852
|
|
|
848
853
|
|
|
854
|
+
def _is_immediate_request_incident_status(status_code: int | None, preset: str) -> bool:
|
|
855
|
+
if status_code is None:
|
|
856
|
+
return False
|
|
857
|
+
if status_code >= 500:
|
|
858
|
+
return True
|
|
859
|
+
if preset == "investigative":
|
|
860
|
+
return status_code in INVESTIGATIVE_IMMEDIATE_REQUEST_STATUSES
|
|
861
|
+
if preset == "balanced":
|
|
862
|
+
return status_code in BALANCED_IMMEDIATE_REQUEST_STATUSES
|
|
863
|
+
return False
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
def _is_request_anomaly_candidate_status(status_code: int | None, preset: str) -> bool:
|
|
867
|
+
if status_code is None or status_code < 400 or status_code >= 500:
|
|
868
|
+
return False
|
|
869
|
+
if preset == "investigative":
|
|
870
|
+
return status_code in INVESTIGATIVE_ANOMALY_STATUSES
|
|
871
|
+
if preset == "balanced":
|
|
872
|
+
return status_code in BALANCED_STANDARD_ANOMALY_STATUSES or status_code in BALANCED_HIGH_VOLUME_ANOMALY_STATUSES
|
|
873
|
+
return False
|
|
874
|
+
|
|
875
|
+
|
|
849
876
|
def _time_now() -> float:
|
|
850
877
|
return datetime.now(tz=timezone.utc).timestamp()
|
|
851
878
|
|
|
@@ -226,4 +226,88 @@ def test_capture_policy_filters_logs_and_request_events_from_remote_config() ->
|
|
|
226
226
|
events = _events(transport)
|
|
227
227
|
assert [event["event_type"] for event in events] == ["log_event", "request_event"]
|
|
228
228
|
assert events[0]["payload"]["message"] == "error kept"
|
|
229
|
-
assert events[1]["payload"]["response_status"] == 503
|
|
229
|
+
assert events[1]["payload"]["response_status"] == 503
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def test_balanced_capture_policy_keeps_request_failure_anomaly_candidates() -> None:
|
|
233
|
+
clock = ManualClock()
|
|
234
|
+
fetch = FakeFetch(
|
|
235
|
+
responses=[
|
|
236
|
+
FakeConfigResponse(
|
|
237
|
+
200,
|
|
238
|
+
{
|
|
239
|
+
"probes_enabled": True,
|
|
240
|
+
"remote_probes_enabled": True,
|
|
241
|
+
"active_probes": [],
|
|
242
|
+
"poll_interval_ms": 15000,
|
|
243
|
+
"capture_policy": {
|
|
244
|
+
"preset": "balanced",
|
|
245
|
+
"capture_logs": "warning",
|
|
246
|
+
"capture_request_events": "failures_only",
|
|
247
|
+
"capture_breadcrumbs": "exception_only",
|
|
248
|
+
"capture_probe_events": "buffer_only",
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
)
|
|
252
|
+
]
|
|
253
|
+
)
|
|
254
|
+
transport = FakeTransport()
|
|
255
|
+
sdk = DebugBundleSdk(transport=transport, time_provider=clock.time)
|
|
256
|
+
sdk.init(
|
|
257
|
+
project_token="dbundle_proj_test",
|
|
258
|
+
service="checkout-api",
|
|
259
|
+
environment="production",
|
|
260
|
+
fetch_impl=fetch,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
sdk.capture_request({"method": "POST", "path": "/checkout", "headers": {}}, {"status_code": 429})
|
|
264
|
+
sdk.capture_request({"method": "POST", "path": "/checkout", "headers": {}}, {"status_code": 404})
|
|
265
|
+
sdk.capture_request({"method": "POST", "path": "/checkout", "headers": {}}, {"status_code": 409})
|
|
266
|
+
sdk.flush()
|
|
267
|
+
|
|
268
|
+
events = _events(transport)
|
|
269
|
+
request_statuses = [
|
|
270
|
+
event["payload"]["response_status"]
|
|
271
|
+
for event in events
|
|
272
|
+
if event["event_type"] == "request_event"
|
|
273
|
+
]
|
|
274
|
+
assert request_statuses == [429, 404, 409]
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def test_investigative_capture_policy_promotes_409_even_when_request_capture_is_off() -> None:
|
|
278
|
+
clock = ManualClock()
|
|
279
|
+
fetch = FakeFetch(
|
|
280
|
+
responses=[
|
|
281
|
+
FakeConfigResponse(
|
|
282
|
+
200,
|
|
283
|
+
{
|
|
284
|
+
"probes_enabled": True,
|
|
285
|
+
"remote_probes_enabled": True,
|
|
286
|
+
"active_probes": [],
|
|
287
|
+
"poll_interval_ms": 15000,
|
|
288
|
+
"capture_policy": {
|
|
289
|
+
"preset": "investigative",
|
|
290
|
+
"capture_logs": "info",
|
|
291
|
+
"capture_request_events": "off",
|
|
292
|
+
"capture_breadcrumbs": "standalone",
|
|
293
|
+
"capture_probe_events": "standalone_when_activated",
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
)
|
|
297
|
+
]
|
|
298
|
+
)
|
|
299
|
+
transport = FakeTransport()
|
|
300
|
+
sdk = DebugBundleSdk(transport=transport, time_provider=clock.time)
|
|
301
|
+
sdk.init(
|
|
302
|
+
project_token="dbundle_proj_test",
|
|
303
|
+
service="checkout-api",
|
|
304
|
+
environment="production",
|
|
305
|
+
fetch_impl=fetch,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
sdk.capture_request({"method": "POST", "path": "/checkout", "headers": {}}, {"status_code": 409})
|
|
309
|
+
sdk.capture_request({"method": "POST", "path": "/checkout", "headers": {}}, {"status_code": 404})
|
|
310
|
+
sdk.flush()
|
|
311
|
+
|
|
312
|
+
events = _events(transport)
|
|
313
|
+
assert [event["payload"]["response_status"] for event in events if event["event_type"] == "request_event"] == [409]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/__init__.py
RENAMED
|
File without changes
|
{debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/common.py
RENAMED
|
File without changes
|
{debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/django.py
RENAMED
|
File without changes
|
{debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/fastapi.py
RENAMED
|
File without changes
|
|
File without changes
|
{debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/relay_django.py
RENAMED
|
File without changes
|
{debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/relay_fastapi.py
RENAMED
|
File without changes
|
{debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/integrations/relay_flask.py
RENAMED
|
File without changes
|
{debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle/logger_integrations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle_python.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle_python.egg-info/requires.txt
RENAMED
|
File without changes
|
{debugbundle_python-0.1.2 → debugbundle_python-0.1.3}/src/debugbundle_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|