-
Notifications
You must be signed in to change notification settings - Fork 118
fix(eap): Substitute nil trace ID #6079
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
8b89e01
c730fcb
ce39cef
33584e2
3db42c5
e0a8b96
9766ef6
3f94dd5
10018e0
4b98c24
8fe3905
ddfa747
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -66,61 +66,74 @@ impl TraceId { | |
|
|
||
| relay_common::impl_str_serde!(TraceId, "a trace identifier"); | ||
|
|
||
| /// Error for an invalid trace ID. | ||
| #[derive(Debug)] | ||
| pub enum InvalidTraceId { | ||
| /// The trace ID is all zeros. | ||
| Nil, | ||
| /// The trace ID is syntactically invalid. | ||
| Invalid, | ||
| } | ||
|
|
||
| impl FromStr for TraceId { | ||
| type Err = Error; | ||
| type Err = InvalidTraceId; | ||
|
|
||
| fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
| Uuid::parse_str(s) | ||
| .map(Into::into) | ||
| .map_err(|_| Error::invalid("the trace id is not valid")) | ||
| let uuid = Uuid::from_str(s).map_err(|_| InvalidTraceId::Uuid)?; | ||
| Self::try_from(uuid) | ||
| } | ||
| } | ||
|
|
||
| impl TryFrom<&str> for TraceId { | ||
| type Error = Error; | ||
| type Error = InvalidTraceId; | ||
|
|
||
| fn try_from(value: &str) -> Result<Self, Self::Error> { | ||
| value.parse() | ||
|
sentry[bot] marked this conversation as resolved.
|
||
| } | ||
| } | ||
|
|
||
| impl TryFrom<&[u8]> for TraceId { | ||
| type Error = Error; | ||
| type Error = InvalidTraceId; | ||
|
|
||
| fn try_from(value: &[u8]) -> Result<Self, Self::Error> { | ||
| let uuid = | ||
| Uuid::from_slice(value).map_err(|_| Error::invalid("the trace id is not valid"))?; | ||
| Ok(Self(uuid)) | ||
| Uuid::from_slice(value) | ||
| .map_err(|_| InvalidTraceId::Uuid) | ||
| .and_then(Self::try_from) | ||
| } | ||
| } | ||
|
|
||
| impl fmt::Display for TraceId { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| write!(f, "{}", self.0.as_simple()) | ||
| impl TryFrom<Uuid> for TraceId { | ||
| type Error = InvalidTraceId; | ||
| fn try_from(uuid: Uuid) -> Result<Self, Self::Error> { | ||
| if uuid.is_nil() { | ||
| return Err(InvalidTraceId::Nil); | ||
| } | ||
| Ok(TraceId(uuid)) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Serde nil IDs not substitutedMedium Severity
Additional Locations (1)Reviewed by Cursor Bugbot for commit 4b98c24. Configure here.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In a follow-up PR I will make parsing more restrictive and always reject on nil IDs. For now, an error in the DSC will hit the error boundary and default to |
||
| } | ||
| } | ||
|
|
||
| impl fmt::Debug for TraceId { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| write!(f, "TraceId(\"{}\")", self.0.as_simple()) | ||
| impl TryFrom<EventId> for TraceId { | ||
| type Error = InvalidTraceId; | ||
| fn try_from(event_id: EventId) -> Result<Self, Self::Error> { | ||
| Self::try_from(event_id.0) | ||
| } | ||
| } | ||
|
|
||
| impl From<Uuid> for TraceId { | ||
| fn from(uuid: Uuid) -> Self { | ||
| TraceId(uuid) | ||
| impl From<TraceId> for Uuid { | ||
| fn from(trace_id: TraceId) -> Self { | ||
| trace_id.0 | ||
| } | ||
| } | ||
|
|
||
| impl From<EventId> for TraceId { | ||
| fn from(event_id: EventId) -> Self { | ||
| Self::from(event_id.0) | ||
| impl fmt::Display for TraceId { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| write!(f, "{}", self.0.as_simple()) | ||
| } | ||
| } | ||
|
|
||
| impl From<TraceId> for Uuid { | ||
| fn from(trace_id: TraceId) -> Self { | ||
| trace_id.0 | ||
| impl fmt::Debug for TraceId { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| write!(f, "TraceId(\"{}\")", self.0.as_simple()) | ||
|
Comment on lines
+115
to
+136
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I moved the constructors up so they are all in one place. Messes with the diff but makes for better readability over all IMO. |
||
| } | ||
| } | ||
|
|
||
|
|
@@ -138,9 +151,14 @@ impl FromValue for TraceId { | |
| Self: Sized, | ||
| { | ||
| match value { | ||
| Annotated(Some(Value::String(value)), mut meta) => match value.parse() { | ||
| Annotated(Some(Value::String(value)), mut meta) => match value.parse::<TraceId>() { | ||
| Ok(trace_id) => Annotated(Some(trace_id), meta), | ||
| Err(_) => { | ||
| Err(InvalidTraceId::Nil) => { | ||
| meta.add_remark(Remark::new(RemarkType::Substituted, "nil_trace_id")); | ||
| meta.set_original_value(Some(value)); | ||
| Annotated(Some(TraceId::random()), meta) | ||
| } | ||
| Err(InvalidTraceId::Uuid) => { | ||
| meta.add_error(Error::invalid("not a valid trace id")); | ||
| meta.set_original_value(Some(value)); | ||
| Annotated(None, meta) | ||
|
|
@@ -411,18 +429,18 @@ mod tests { | |
| assert_eq!(trace_id.as_u128(), 0x4c79f60c11214eb38604f4ae0781bfb2); | ||
|
|
||
| // Test empty string (should return 0) | ||
| let empty_trace_id: Result<TraceId, Error> = "".parse(); | ||
| let empty_trace_id: Result<TraceId, _> = "".parse(); | ||
| assert!(empty_trace_id.is_err()); | ||
|
|
||
| // Test string with invalid length (should return 0) | ||
| let short_trace_id: Result<TraceId, Error> = "4c79f60c11214eb38604f4ae0781bfb".parse(); // 31 chars | ||
| let short_trace_id: Result<TraceId, _> = "4c79f60c11214eb38604f4ae0781bfb".parse(); // 31 chars | ||
| assert!(short_trace_id.is_err()); | ||
|
|
||
| let long_trace_id: Result<TraceId, Error> = "4c79f60c11214eb38604f4ae0781bfb2a".parse(); // 33 chars | ||
| let long_trace_id: Result<TraceId, _> = "4c79f60c11214eb38604f4ae0781bfb2a".parse(); // 33 chars | ||
| assert!(long_trace_id.is_err()); | ||
|
|
||
| // Test string with invalid hex characters (should return 0) | ||
| let invalid_trace_id: Result<TraceId, Error> = "4c79f60c11214eb38604f4ae0781bfbg".parse(); // 'g' is not a hex char | ||
| let invalid_trace_id: Result<TraceId, _> = "4c79f60c11214eb38604f4ae0781bfbg".parse(); // 'g' is not a hex char | ||
| assert!(invalid_trace_id.is_err()); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| { | ||
| "trace_id": "00000000-0000-0000-0000-000000000000", | ||
| "trace_id": "00000000-0000-0000-0000-000000000001", | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests with nil IDs would now fail. |
||
| "public_key": "abd0f232775f45feab79864e580d160b", | ||
| "release": "1.0", | ||
| "environment": "dev", | ||
|
|
@@ -8,4 +8,4 @@ | |
| "user_id": "some-id", | ||
| "user_segment": "all", | ||
| "user": null | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -194,8 +194,9 @@ fn otel_to_sentry_link(otel_link: OtelLink) -> Result<SpanV2Link, Error> { | |
| Some((kv.key, Annotated::new(attr_value))) | ||
| })); | ||
|
|
||
| let trace_id = TraceId::try_from_or_random(otel_link.trace_id.as_slice()); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
sentry[bot] marked this conversation as resolved.
|
||
| let span_link = SpanV2Link { | ||
| trace_id: Annotated::new(hex::encode(otel_link.trace_id).parse()?), | ||
| trace_id, | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| span_id: SpanId::try_from(otel_link.span_id.as_slice())?.into(), | ||
| sampled: (otel_link.flags & W3C_TRACE_CONTEXT_SAMPLED != 0).into(), | ||
| attributes: Annotated::new(attributes), | ||
|
jjbayer marked this conversation as resolved.
|
||
|
|
||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was another place where nil-trace IDs could originate.