Skip to content

Commit 4730f88

Browse files
committed
bugsnag: emit slice_name from service_name in custom metadata tab
Observe error analytics filters on custom.slice_name to assign errors to a slice. Store the service_name passed to setup() and write it as custom.slice_name on every event so errors from orchestrator, api, and metrics_poller each land in their own slice. Consolidates all custom tab writes into a single add_tab call.
1 parent 563c451 commit 4730f88

2 files changed

Lines changed: 37 additions & 2 deletions

File tree

cloud_pipelines_backend/instrumentation/bugsnag_instrumentation.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,19 @@
3535

3636
IS_BUGSNAG_ENABLED: bool = bool(_BUGSNAG_API_KEY)
3737
_setup_called: bool = False
38+
_service_name: str | None = None
3839

3940

4041
def _before_notify(event: bugsnag_event.Event) -> None:
4142
"""Attach contextual logging metadata to every Bugsnag event."""
4243
context = contextual_logging.get_all_context_metadata()
4344
if context:
4445
event.add_tab("tangle_context", context)
46+
47+
custom: dict[str, str] = {}
48+
if _service_name:
49+
custom["slice_name"] = _service_name
50+
4551
if _CUSTOM_GROUPING_KEY and event.original_error:
4652
# Use the full chain for grouping so that "LauncherError <- TimeoutError"
4753
# and "LauncherError <- ApiException" land in separate, stable groups.
@@ -50,7 +56,7 @@ def _before_notify(event: bugsnag_event.Event) -> None:
5056
)
5157
prefix = (event.metadata.get("extra") or {}).get("grouping_prefix")
5258
key_value = f"{prefix}: {chain}" if prefix else chain
53-
event.add_tab("custom", {_CUSTOM_GROUPING_KEY: key_value})
59+
custom[_CUSTOM_GROUPING_KEY] = key_value
5460
if prefix and event.errors:
5561
try:
5662
for error in event.errors:
@@ -72,6 +78,9 @@ def _before_notify(event: bugsnag_event.Event) -> None:
7278
except Exception:
7379
_logger.debug("Could not set chain title on errorClass", exc_info=True)
7480

81+
if custom:
82+
event.add_tab("custom", custom)
83+
7584

7685
def setup(*, service_name: str | None = None) -> None:
7786
"""Configure the Bugsnag client.
@@ -102,8 +111,9 @@ def setup(*, service_name: str | None = None) -> None:
102111
project_root=service_name,
103112
)
104113
bugsnag_sdk.before_notify(_before_notify)
105-
global _setup_called
114+
global _setup_called, _service_name
106115
_setup_called = True
116+
_service_name = service_name
107117
except Exception:
108118
_logger.exception("Failed to initialize Bugsnag")
109119

tests/instrumentation/test_bugsnag.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,31 @@ def test_before_notify_skips_error_class_prefix_gracefully_on_bad_errors_structu
271271
bugsnag_module._before_notify(mock_event)
272272

273273

274+
def test_before_notify_sets_slice_name_when_service_name_configured(monkeypatch):
275+
monkeypatch.setenv("TANGLE_BUGSNAG_API_KEY", "test-api-key")
276+
monkeypatch.setenv("TANGLE_ENV", "staging")
277+
278+
import importlib
279+
import cloud_pipelines_backend.instrumentation.bugsnag_instrumentation as bugsnag_module
280+
281+
importlib.reload(bugsnag_module)
282+
283+
from cloud_pipelines_backend.instrumentation import contextual_logging
284+
285+
contextual_logging.clear_context_metadata()
286+
287+
with mock.patch("bugsnag.configure"), mock.patch("bugsnag.before_notify"):
288+
bugsnag_module.setup(service_name="orchestrator")
289+
290+
mock_event = mock.MagicMock()
291+
mock_event.original_error = None
292+
mock_event.metadata = {}
293+
294+
bugsnag_module._before_notify(mock_event)
295+
296+
mock_event.add_tab.assert_called_once_with("custom", {"slice_name": "orchestrator"})
297+
298+
274299
def test_before_notify_skips_empty_context(monkeypatch):
275300
monkeypatch.setenv("TANGLE_BUGSNAG_API_KEY", "test-api-key")
276301
monkeypatch.setenv("TANGLE_ENV", "staging")

0 commit comments

Comments
 (0)