Skip to content

Commit 095eef6

Browse files
authored
Support capture of exceptions from span event for live metrics (#39168)
1 parent 0cba175 commit 095eef6

File tree

4 files changed

+64
-2
lines changed

4 files changed

+64
-2
lines changed

sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
([#38451](https://github.com/Azure/azure-sdk-for-python/pull/38451))
1111
- Implement live metrics filtering for docs
1212
([#38925](https://github.com/Azure/azure-sdk-for-python/pull/38925))
13+
- Implement live metrics + filtering for span event exceptions
14+
([#39168](https://github.com/Azure/azure-sdk-for-python/pull/39168))
1315

1416
### Breaking Changes
1517

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_quickpulse/_live_metrics.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,16 @@ def _record_span(self, span: ReadableSpan) -> None:
204204
# Process docs for quickpulse filtering
205205
_apply_document_filters_from_telemetry_data(data)
206206

207-
# TODO: derive exception metrics from span events
207+
# Derive exception metrics from span events
208+
if span.events:
209+
for event in span.events:
210+
if event.name == "exception":
211+
self._exception_rate_counter.add(1)
212+
# Derive metrics for quickpulse filtering for exception
213+
exc_data = _ExceptionData._from_span_event(event)
214+
_derive_metrics_from_telemetry_data(exc_data)
215+
# Process docs for quickpulse filtering for exception
216+
_apply_document_filters_from_telemetry_data(exc_data)
208217
except Exception: # pylint: disable=broad-except
209218
_logger.exception("Exception occurred while recording span.")
210219

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_quickpulse/_types.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from typing import Dict, no_type_check
66

77
from opentelemetry.sdk._logs import LogRecord
8-
from opentelemetry.sdk.trace import ReadableSpan
8+
from opentelemetry.sdk.trace import Event, ReadableSpan
99
from opentelemetry.semconv.trace import SpanAttributes
1010
from opentelemetry.trace import SpanKind
1111

@@ -166,6 +166,15 @@ def _from_log_record(log_record: LogRecord):
166166
custom_dimensions=log_record.attributes,
167167
)
168168

169+
@staticmethod
170+
@no_type_check
171+
def _from_span_event(span_event: Event):
172+
return _ExceptionData(
173+
message=str(span_event.attributes.get(SpanAttributes.EXCEPTION_MESSAGE, "")),
174+
stack_trace=str(span_event.attributes.get(SpanAttributes.EXCEPTION_STACKTRACE, "")),
175+
custom_dimensions=span_event.attributes,
176+
)
177+
169178

170179
@dataclass
171180
class _TraceData(_TelemetryData):

sdk/monitor/azure-monitor-opentelemetry-exporter/tests/quickpulse/test_live_metrics.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,48 @@ def test_record_span_derive_filter_metrics(
352352
metric_derive_mock.assert_called_once_with(data)
353353
doc_mock.assert_called_once_with(data)
354354

355+
@mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics._ExceptionData")
356+
@mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics._apply_document_filters_from_telemetry_data")
357+
@mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics._derive_metrics_from_telemetry_data")
358+
@mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics._TelemetryData")
359+
@mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics._is_post_state")
360+
def test_record_span_span_event_exception(
361+
self, post_state_mock, data_mock, metric_derive_mock, doc_mock, exc_data_mock,
362+
):
363+
post_state_mock.return_value = True
364+
span_mock = mock.Mock()
365+
event_mock = mock.Mock()
366+
event_mock.name = "exception"
367+
span_mock.events = [event_mock]
368+
span_mock.end_time = 10
369+
span_mock.start_time = 5
370+
data = _RequestData(
371+
custom_dimensions={},
372+
duration=1000,
373+
success=True,
374+
name="test_req",
375+
response_code=400,
376+
url="test_url",
377+
)
378+
exc_data = _ExceptionData(
379+
custom_dimensions={},
380+
message="exception",
381+
stack_trace="",
382+
)
383+
data_mock._from_span.return_value = data
384+
exc_data_mock._from_span_event.return_value = exc_data
385+
qpm = _QuickpulseManager(
386+
connection_string="InstrumentationKey=4321abcd-5678-4efa-8abc-1234567890ac;LiveEndpoint=https://eastus.livediagnostics.monitor.azure.com/",
387+
resource=Resource.create(),
388+
)
389+
qpm._record_span(span_mock)
390+
data_mock._from_span.assert_called_once_with(span_mock)
391+
exc_data_mock._from_span_event.assert_called_once_with(event_mock)
392+
metric_derive_mock.assert_any_call(data)
393+
doc_mock.assert_any_call(data)
394+
metric_derive_mock.assert_any_call(exc_data)
395+
doc_mock.assert_any_call(exc_data)
396+
355397
@mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics._apply_document_filters_from_telemetry_data")
356398
@mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics._derive_metrics_from_telemetry_data")
357399
@mock.patch("azure.monitor.opentelemetry.exporter._quickpulse._live_metrics._TelemetryData")

0 commit comments

Comments
 (0)