From 6a0a9af1f5eb3ee4894aa9d865473c858d82f574 Mon Sep 17 00:00:00 2001 From: Garry Filakhtov Date: Fri, 5 Dec 2025 15:04:44 +1100 Subject: [PATCH 1/3] Add exception.type to error events Make sure we always append `exception.type` to the span events any time we convert an error to an exception. This is required to make Datadog platform happy and avoid the "Missing error message and stack trace" error. --- src/layer.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/layer.rs b/src/layer.rs index 1171ac8..df42f40 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -35,6 +35,7 @@ const SPAN_EVENT_COUNT_FIELD: &str = "otel.tracing_event_count"; const EVENT_EXCEPTION_NAME: &str = "exception"; const FIELD_EXCEPTION_MESSAGE: &str = "exception.message"; const FIELD_EXCEPTION_STACKTRACE: &str = "exception.stacktrace"; +const FIELD_EXCEPTION_TYPE: &str = "exception.type"; /// An [OpenTelemetry] propagation layer for use in a project that uses /// [tracing]. @@ -287,6 +288,9 @@ impl field::Visit for SpanEventVisitor<'_, '_> { self.event_builder .attributes .push(KeyValue::new(FIELD_EXCEPTION_MESSAGE, format!("{value:?}"))); + self.event_builder + .attributes + .push(KeyValue::new(FIELD_EXCEPTION_TYPE, "&str")); } else { self.event_builder .attributes @@ -326,6 +330,9 @@ impl field::Visit for SpanEventVisitor<'_, '_> { self.event_builder .attributes .push(KeyValue::new(FIELD_EXCEPTION_MESSAGE, format!("{value:?}"))); + self.event_builder + .attributes + .push(KeyValue::new(FIELD_EXCEPTION_TYPE, "&dyn fmt::Debug")); } else { self.event_builder .attributes @@ -367,6 +374,10 @@ impl field::Visit for SpanEventVisitor<'_, '_> { Key::new(FIELD_EXCEPTION_MESSAGE), Value::String(StringValue::from(error_msg.clone())), )); + self.event_builder.attributes.push(KeyValue::new( + Key::new(FIELD_EXCEPTION_TYPE), + "&dyn std::error::Error", + )); // NOTE: This is actually not the stacktrace of the exception. This is // the "source chain". It represents the heirarchy of errors from the @@ -391,6 +402,10 @@ impl field::Visit for SpanEventVisitor<'_, '_> { FIELD_EXCEPTION_MESSAGE, Value::String(error_msg.clone().into()), )); + attributes.push(KeyValue::new( + FIELD_EXCEPTION_TYPE, + "&dyn std::error::Error", + )); // NOTE: This is actually not the stacktrace of the exception. This is // the "source chain". It represents the heirarchy of errors from the @@ -548,6 +563,10 @@ impl field::Visit for SpanAttributeVisitor<'_> { Key::new(FIELD_EXCEPTION_MESSAGE), Value::from(error_msg.clone()), )); + self.record(KeyValue::new( + Key::new(FIELD_EXCEPTION_TYPE), + "&dyn std::error::Error", + )); // NOTE: This is actually not the stacktrace of the exception. This is // the "source chain". It represents the heirarchy of errors from the @@ -1737,6 +1756,10 @@ mod tests { ); assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); + assert_eq!( + attributes[FIELD_EXCEPTION_TYPE].as_str(), + "&dyn std::error::Error" + ); assert_eq!( attributes[FIELD_EXCEPTION_STACKTRACE], Value::Array( @@ -1881,6 +1904,10 @@ mod tests { ); assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); + assert_eq!( + attributes[FIELD_EXCEPTION_TYPE].as_str(), + "&dyn std::error::Error" + ); assert_eq!( attributes[FIELD_EXCEPTION_STACKTRACE], Value::Array( @@ -2137,6 +2164,10 @@ mod tests { let attributes = tracer.attributes(); assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); + assert_eq!( + attributes[FIELD_EXCEPTION_TYPE].as_str(), + "&dyn std::error::Error" + ); assert_eq!( attributes[FIELD_EXCEPTION_STACKTRACE], Value::Array( @@ -2184,6 +2215,10 @@ mod tests { let attributes = tracer.attributes(); assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); + assert_eq!( + attributes[FIELD_EXCEPTION_TYPE].as_str(), + "&dyn std::error::Error" + ); assert_eq!( attributes[FIELD_EXCEPTION_STACKTRACE], Value::Array( From 37c098d80f70bd67e194bc8de9c5000f90a2a83f Mon Sep 17 00:00:00 2001 From: Garry Filakhtov Date: Fri, 5 Dec 2025 15:04:54 +1100 Subject: [PATCH 2/3] Fix build error Current default branch is broken, failing the build. I am doing a change that I think is appropriate here, but open to feedback. --- src/layer/filtered.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/layer/filtered.rs b/src/layer/filtered.rs index 131cfb4..6844e3b 100644 --- a/src/layer/filtered.rs +++ b/src/layer/filtered.rs @@ -144,8 +144,10 @@ where OtelDataState::Builder { builder, parent_cx: _, + status, } => { builder.attributes.get_or_insert(Vec::new()).push(key_value); + builder.status = status.clone(); } OtelDataState::Context { current_cx } => { let span = current_cx.span(); From 502b6cba7ba47fda3234538573393f47f843d1e2 Mon Sep 17 00:00:00 2001 From: Garry Filakhtov Date: Sun, 7 Dec 2025 12:26:14 +1100 Subject: [PATCH 3/3] Update exception.type to Unknown Use `Unknown` as an `exception.type` instead of `dyn ...` types as per our conversation in the original issue. --- src/layer.rs | 41 +++++++++++------------------------------ 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/src/layer.rs b/src/layer.rs index df42f40..164f4a3 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -290,7 +290,7 @@ impl field::Visit for SpanEventVisitor<'_, '_> { .push(KeyValue::new(FIELD_EXCEPTION_MESSAGE, format!("{value:?}"))); self.event_builder .attributes - .push(KeyValue::new(FIELD_EXCEPTION_TYPE, "&str")); + .push(KeyValue::new(FIELD_EXCEPTION_TYPE, "Unknown")); } else { self.event_builder .attributes @@ -332,7 +332,7 @@ impl field::Visit for SpanEventVisitor<'_, '_> { .push(KeyValue::new(FIELD_EXCEPTION_MESSAGE, format!("{value:?}"))); self.event_builder .attributes - .push(KeyValue::new(FIELD_EXCEPTION_TYPE, "&dyn fmt::Debug")); + .push(KeyValue::new(FIELD_EXCEPTION_TYPE, "Unknown")); } else { self.event_builder .attributes @@ -374,10 +374,9 @@ impl field::Visit for SpanEventVisitor<'_, '_> { Key::new(FIELD_EXCEPTION_MESSAGE), Value::String(StringValue::from(error_msg.clone())), )); - self.event_builder.attributes.push(KeyValue::new( - Key::new(FIELD_EXCEPTION_TYPE), - "&dyn std::error::Error", - )); + self.event_builder + .attributes + .push(KeyValue::new(Key::new(FIELD_EXCEPTION_TYPE), "Unkonwn")); // NOTE: This is actually not the stacktrace of the exception. This is // the "source chain". It represents the heirarchy of errors from the @@ -402,10 +401,7 @@ impl field::Visit for SpanEventVisitor<'_, '_> { FIELD_EXCEPTION_MESSAGE, Value::String(error_msg.clone().into()), )); - attributes.push(KeyValue::new( - FIELD_EXCEPTION_TYPE, - "&dyn std::error::Error", - )); + attributes.push(KeyValue::new(FIELD_EXCEPTION_TYPE, "Unknown")); // NOTE: This is actually not the stacktrace of the exception. This is // the "source chain". It represents the heirarchy of errors from the @@ -563,10 +559,7 @@ impl field::Visit for SpanAttributeVisitor<'_> { Key::new(FIELD_EXCEPTION_MESSAGE), Value::from(error_msg.clone()), )); - self.record(KeyValue::new( - Key::new(FIELD_EXCEPTION_TYPE), - "&dyn std::error::Error", - )); + self.record(KeyValue::new(Key::new(FIELD_EXCEPTION_TYPE), "Unknown")); // NOTE: This is actually not the stacktrace of the exception. This is // the "source chain". It represents the heirarchy of errors from the @@ -1756,10 +1749,7 @@ mod tests { ); assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); - assert_eq!( - attributes[FIELD_EXCEPTION_TYPE].as_str(), - "&dyn std::error::Error" - ); + assert_eq!(attributes[FIELD_EXCEPTION_TYPE].as_str(), "Unknown"); assert_eq!( attributes[FIELD_EXCEPTION_STACKTRACE], Value::Array( @@ -1904,10 +1894,7 @@ mod tests { ); assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); - assert_eq!( - attributes[FIELD_EXCEPTION_TYPE].as_str(), - "&dyn std::error::Error" - ); + assert_eq!(attributes[FIELD_EXCEPTION_TYPE].as_str(), "Unknown"); assert_eq!( attributes[FIELD_EXCEPTION_STACKTRACE], Value::Array( @@ -2164,10 +2151,7 @@ mod tests { let attributes = tracer.attributes(); assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); - assert_eq!( - attributes[FIELD_EXCEPTION_TYPE].as_str(), - "&dyn std::error::Error" - ); + assert_eq!(attributes[FIELD_EXCEPTION_TYPE].as_str(), "Unknown"); assert_eq!( attributes[FIELD_EXCEPTION_STACKTRACE], Value::Array( @@ -2215,10 +2199,7 @@ mod tests { let attributes = tracer.attributes(); assert_eq!(attributes[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); - assert_eq!( - attributes[FIELD_EXCEPTION_TYPE].as_str(), - "&dyn std::error::Error" - ); + assert_eq!(attributes[FIELD_EXCEPTION_TYPE].as_str(), "Unknown"); assert_eq!( attributes[FIELD_EXCEPTION_STACKTRACE], Value::Array(