diff --git a/CHANGELOG.md b/CHANGELOG.md index 57976ae4ba..ebbac23718 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#415](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/415)) - `opentelemetry-instrumentation-tornado` Add request/response hooks. ([#426](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/426)) +- `opentelemetry-exporter-datadog` Add parsing exception events for error tags. + ([#459](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/459)) - `opentelemetry-instrumenation-django` now supports trace response headers. ([#436](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/436)) - `opentelemetry-instrumenation-tornado` now supports trace response headers. diff --git a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/constants.py b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/constants.py index 90f15a7ffc..6f86c12cce 100644 --- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/constants.py +++ b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/constants.py @@ -7,3 +7,10 @@ ENV_KEY = "env" VERSION_KEY = "version" SERVICE_NAME_TAG = "service.name" +EVENT_NAME_EXCEPTION = "exception" +EXCEPTION_TYPE_ATTR_KEY = "exception.type" +EXCEPTION_MSG_ATTR_KEY = "exception.message" +EXCEPTION_STACK_ATTR_KEY = "exception.stacktrace" +DD_ERROR_TYPE_TAG_KEY = "error.type" +DD_ERROR_MSG_TAG_KEY = "error.msg" +DD_ERROR_STACK_TAG_KEY = "error.stack" diff --git a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py index 10693d0a8c..d6aa7d478b 100644 --- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py +++ b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py @@ -22,8 +22,15 @@ import opentelemetry.trace as trace_api from opentelemetry.exporter.datadog.constants import ( + DD_ERROR_MSG_TAG_KEY, + DD_ERROR_STACK_TAG_KEY, + DD_ERROR_TYPE_TAG_KEY, DD_ORIGIN, ENV_KEY, + EVENT_NAME_EXCEPTION, + EXCEPTION_MSG_ATTR_KEY, + EXCEPTION_STACK_ATTR_KEY, + EXCEPTION_TYPE_ATTR_KEY, SAMPLE_RATE_METRIC_KEY, SERVICE_NAME_TAG, VERSION_KEY, @@ -145,11 +152,12 @@ def _translate_to_datadog(self, spans): if not span.status.is_ok: datadog_span.error = 1 - if span.status.description: - exc_type, exc_val = _get_exc_info(span) - # no mapping for error.stack since traceback not recorded - datadog_span.set_tag("error.msg", exc_val) - datadog_span.set_tag("error.type", exc_type) + # loop over events and look for exception events, extract info. + # https://github.com/open-telemetry/opentelemetry-python/blob/71e3a7a192c0fc8a7503fac967ada36a74b79e58/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py#L810-L819 + if span.events: + _extract_tags_from_exception_events( + span.events, datadog_span + ) # combine resource attributes and span attributes, don't modify existing span attributes combined_span_tags = {} @@ -178,7 +186,7 @@ def _translate_to_datadog(self, spans): if sampling_rate is not None: datadog_span.set_metric(SAMPLE_RATE_METRIC_KEY, sampling_rate) - # span events and span links are not supported + # span events and span links are not supported except for extracting exception event context datadog_spans.append(datadog_span) @@ -318,3 +326,17 @@ def _extract_tags_from_resource(resource): else: tags[attribute_key] = attribute_value return [tags, service_name] + + +def _extract_tags_from_exception_events(events, datadog_span): + """Parse error tags from exception events, error.msg error.type + and error.stack have special significance within datadog""" + for event in events: + if event.name is not None and event.name == EVENT_NAME_EXCEPTION: + for key, value in event.attributes.items(): + if key == EXCEPTION_TYPE_ATTR_KEY: + datadog_span.set_tag(DD_ERROR_TYPE_TAG_KEY, value) + elif key == EXCEPTION_MSG_ATTR_KEY: + datadog_span.set_tag(DD_ERROR_MSG_TAG_KEY, value) + elif key == EXCEPTION_STACK_ATTR_KEY: + datadog_span.set_tag(DD_ERROR_STACK_TAG_KEY, value) diff --git a/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py b/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py index 406732615b..c366175f8c 100644 --- a/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py +++ b/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py @@ -388,6 +388,7 @@ def test_errors(self): self.assertEqual(span["error"], 1) self.assertEqual(span["meta"]["error.msg"], "bar") self.assertEqual(span["meta"]["error.type"], "ValueError") + self.assertTrue(span["meta"]["error.stack"] is not None) def test_shutdown(self): span_names = ["xxx", "bar", "foo"]