From e6af0eaa4ba3c8c3250dca516ca91b30cc6b70ac Mon Sep 17 00:00:00 2001
From: ericmustin <mustin.eric@gmail.com>
Date: Mon, 19 Apr 2021 16:33:13 -0400
Subject: [PATCH 1/9] [exporter/datadog]: add support for extracting error info
 from exception span events

---
 .../exporter/datadog/constants.py             |  7 +++++
 .../exporter/datadog/exporter.py              | 28 ++++++++++++++++---
 2 files changed, 31 insertions(+), 4 deletions(-)

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 da1794afff..fa5f6fbaa6 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,
@@ -144,11 +151,24 @@ def _translate_to_datadog(self, spans):
 
             if not span.status.is_ok:
                 datadog_span.error = 1
-                if span.status.description:
+                # 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:
+                    for event in span.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)
+                # fallback to description but only if no exception events
+               elif 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)
+                    datadog_span.set_tag(DD_ERROR_MSG_TAG_KEY, exc_val)
+                    datadog_span.set_tag(DD_ERROR_TYPE_TAG_KEY, exc_type)
 
             # combine resource attributes and span attributes, don't modify existing span attributes
             combined_span_tags = {}
@@ -177,7 +197,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)
 

From 52a1c9437bce27ba27549c13ba087ab20c07e3dd Mon Sep 17 00:00:00 2001
From: ericmustin <mustin.eric@gmail.com>
Date: Mon, 19 Apr 2021 16:35:41 -0400
Subject: [PATCH 2/9] [exporter/datadog]: update tests to check exception tags
 are set as error tags on datadog span

---
 .../tests/test_datadog_exporter.py                               | 1 +
 1 file changed, 1 insertion(+)

diff --git a/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py b/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py
index 9cc7ccbb21..d8a2db2a2e 100644
--- a/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py
+++ b/exporter/opentelemetry-exporter-datadog/tests/test_datadog_exporter.py
@@ -381,6 +381,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"]

From 6bcb44f5ce74ca4519c917f22ee6e48387651a1f Mon Sep 17 00:00:00 2001
From: ericmustin <mustin.eric@gmail.com>
Date: Mon, 19 Apr 2021 16:51:17 -0400
Subject: [PATCH 3/9] [exporter/datadog]: add changelog

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 52e351539e..5457ac5f94 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -34,6 +34,7 @@ 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))
 
 ### Removed
 - Remove `http.status_text` from span attributes

From b1b6aa307376998fcaa5e3bdfc85b8d0a7853421 Mon Sep 17 00:00:00 2001
From: ericmustin <mustin.eric@gmail.com>
Date: Mon, 19 Apr 2021 16:55:57 -0400
Subject: [PATCH 4/9] [exporter/datadog]: fix typos

---
 .../src/opentelemetry/exporter/datadog/exporter.py            | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

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 fa5f6fbaa6..4645ed41aa 100644
--- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py
+++ b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py
@@ -163,8 +163,8 @@ def _translate_to_datadog(self, spans):
                                     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)
-                # fallback to description but only if no exception events
-               elif span.status.description:
+                # fallback to description but only if no exception events.
+                elif span.status.description:
                     exc_type, exc_val = _get_exc_info(span)
                     # no mapping for error.stack since traceback not recorded
                     datadog_span.set_tag(DD_ERROR_MSG_TAG_KEY, exc_val)

From 946e9a5dad8623a8343538080c4a4ac24baaa2e6 Mon Sep 17 00:00:00 2001
From: ericmustin <mustin.eric@gmail.com>
Date: Mon, 19 Apr 2021 17:18:55 -0400
Subject: [PATCH 5/9] [exporter/datadog]: linting

---
 .../opentelemetry/exporter/datadog/exporter.py  | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

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 4645ed41aa..bea7e7a148 100644
--- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py
+++ b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py
@@ -155,14 +155,23 @@ def _translate_to_datadog(self, spans):
                 # https://github.com/open-telemetry/opentelemetry-python/blob/71e3a7a192c0fc8a7503fac967ada36a74b79e58/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py#L810-L819
                 if span.events:
                     for event in span.events:
-                        if event.name is not None and event.name == EVENT_NAME_EXCEPTION:
+                        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)
+                                    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)
+                                    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)
+                                    datadog_span.set_tag(
+                                        DD_ERROR_STACK_TAG_KEY, value
+                                    )
                 # fallback to description but only if no exception events.
                 elif span.status.description:
                     exc_type, exc_val = _get_exc_info(span)

From dad11f3a59b6eeb1be1a1d4cbd4258d22a24536e Mon Sep 17 00:00:00 2001
From: ericmustin <mustin.eric@gmail.com>
Date: Mon, 19 Apr 2021 17:33:11 -0400
Subject: [PATCH 6/9] [exporter/datdog]: fixing linting

---
 .../exporter/datadog/exporter.py              | 42 +++++++++++--------
 1 file changed, 24 insertions(+), 18 deletions(-)

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 bea7e7a148..c2a839c320 100644
--- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py
+++ b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py
@@ -149,29 +149,13 @@ def _translate_to_datadog(self, spans):
             datadog_span.start_ns = span.start_time
             datadog_span.duration_ns = span.end_time - span.start_time
 
+
             if not span.status.is_ok:
                 datadog_span.error = 1
                 # 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:
-                    for event in span.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
-                                    )
+                    _extract_tags_from_exception_events(span.events, datadog_span)
                 # fallback to description but only if no exception events.
                 elif span.status.description:
                     exc_type, exc_val = _get_exc_info(span)
@@ -346,3 +330,25 @@ 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
+                    )

From e66290a7a9223a770986a273492b340c5b30d88a Mon Sep 17 00:00:00 2001
From: ericmustin <mustin.eric@gmail.com>
Date: Mon, 19 Apr 2021 17:39:28 -0400
Subject: [PATCH 7/9] [exporter/datadog]: more linting fixes

---
 .../exporter/datadog/exporter.py              | 22 +++++++------------
 1 file changed, 8 insertions(+), 14 deletions(-)

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 c2a839c320..f7248377c3 100644
--- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py
+++ b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py
@@ -155,7 +155,9 @@ def _translate_to_datadog(self, spans):
                 # 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)
+                    _extract_tags_from_exception_events(
+                        span.events, datadog_span
+                    )
                 # fallback to description but only if no exception events.
                 elif span.status.description:
                     exc_type, exc_val = _get_exc_info(span)
@@ -331,24 +333,16 @@ def _extract_tags_from_resource(resource):
             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
-        ):
+        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
-                    )
+                    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
-                    )
+                    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
-                    )
+                    datadog_span.set_tag(DD_ERROR_STACK_TAG_KEY, value)

From 179fe8c77c68ce5a754ede975e142a468cb1b150 Mon Sep 17 00:00:00 2001
From: ericmustin <mustin.eric@gmail.com>
Date: Mon, 19 Apr 2021 17:43:41 -0400
Subject: [PATCH 8/9] [exporter/datadog]: final lint, the lintinging

---
 .../src/opentelemetry/exporter/datadog/exporter.py               | 1 -
 1 file changed, 1 deletion(-)

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 f7248377c3..31cc1d49df 100644
--- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py
+++ b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py
@@ -149,7 +149,6 @@ def _translate_to_datadog(self, spans):
             datadog_span.start_ns = span.start_time
             datadog_span.duration_ns = span.end_time - span.start_time
 
-
             if not span.status.is_ok:
                 datadog_span.error = 1
                 # loop over events and look for exception events, extract info.

From 362dd28869d68b4a2e16e60011604dfc046eefba Mon Sep 17 00:00:00 2001
From: ericmustin <mustin.eric@gmail.com>
Date: Thu, 22 Apr 2021 14:57:10 -0400
Subject: [PATCH 9/9] [exporter/datadog]: remove old error tagging

---
 .../src/opentelemetry/exporter/datadog/exporter.py          | 6 ------
 1 file changed, 6 deletions(-)

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 31cc1d49df..bcaf070eaf 100644
--- a/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py
+++ b/exporter/opentelemetry-exporter-datadog/src/opentelemetry/exporter/datadog/exporter.py
@@ -157,12 +157,6 @@ def _translate_to_datadog(self, spans):
                     _extract_tags_from_exception_events(
                         span.events, datadog_span
                     )
-                # fallback to description but only if no exception events.
-                elif span.status.description:
-                    exc_type, exc_val = _get_exc_info(span)
-                    # no mapping for error.stack since traceback not recorded
-                    datadog_span.set_tag(DD_ERROR_MSG_TAG_KEY, exc_val)
-                    datadog_span.set_tag(DD_ERROR_TYPE_TAG_KEY, exc_type)
 
             # combine resource attributes and span attributes, don't modify existing span attributes
             combined_span_tags = {}