From 8ba71167bdb9865e2d47edff7a466e05c911e77d Mon Sep 17 00:00:00 2001 From: Nikhil Sinha Date: Thu, 16 Oct 2025 19:11:38 +0100 Subject: [PATCH 1/3] changes to otel data 1. prefix `SEVERITY_TEXT_` removed from severity_text in logs 2. prefix `AGGREGATION_TEMPORALITY_` removed from aggregation_temporality_description in metrics 3. field `event_duration_ms` and `span_duration_ms` added in traces 4. prefix `STATUS_CODE_` removed from span_status_description in traces 5. prefix `SPAN_FLAGS_` removed from span_flags_description in traces 6. prefix `SPAN_KIND_` removed from span_kind_description in traces --- src/otel/logs.rs | 8 +++++- src/otel/metrics.rs | 10 +++---- src/otel/traces.rs | 63 +++++++++++++++++++++++++++++++++------------ 3 files changed, 58 insertions(+), 23 deletions(-) diff --git a/src/otel/logs.rs b/src/otel/logs.rs index 9f6ab14d6..420f5e872 100644 --- a/src/otel/logs.rs +++ b/src/otel/logs.rs @@ -55,9 +55,15 @@ fn flatten_severity(severity_number: i32) -> Map { Value::Number(severity_number.into()), ); let severity = SeverityNumber::try_from(severity_number).unwrap(); + let severity_text = severity.as_str_name().to_string(); severity_json.insert( "severity_text".to_string(), - Value::String(severity.as_str_name().to_string()), + Value::String( + severity_text + .strip_prefix("SEVERITY_NUMBER_") + .unwrap_or(&severity_text) + .to_string(), + ), ); severity_json } diff --git a/src/otel/metrics.rs b/src/otel/metrics.rs index 6974af565..6206cd0d5 100644 --- a/src/otel/metrics.rs +++ b/src/otel/metrics.rs @@ -620,9 +620,9 @@ fn flatten_aggregation_temporality(aggregation_temporality: i32) -> Map "AGGREGATION_TEMPORALITY_UNSPECIFIED", - 1 => "AGGREGATION_TEMPORALITY_DELTA", - 2 => "AGGREGATION_TEMPORALITY_CUMULATIVE", + 0 => "UNSPECIFIED", + 1 => "DELTA", + 2 => "CUMULATIVE", _ => "", }; aggregation_temporality_json.insert( @@ -637,8 +637,8 @@ fn flatten_data_point_flags(flags: u32) -> Map { let mut data_point_flags_json = Map::new(); data_point_flags_json.insert("data_point_flags".to_string(), Value::Number(flags.into())); let description = match flags { - 0 => "DATA_POINT_FLAGS_DO_NOT_USE", - 1 => "DATA_POINT_FLAGS_NO_RECORDED_VALUE_MASK", + 0 => "DO_NOT_USE", + 1 => "NO_RECORDED_VALUE_MASK", _ => "", }; data_point_flags_json.insert( diff --git a/src/otel/traces.rs b/src/otel/traces.rs index 2fe7743c3..0ac9c17a5 100644 --- a/src/otel/traces.rs +++ b/src/otel/traces.rs @@ -27,7 +27,7 @@ use serde_json::{Map, Value}; use super::otel_utils::convert_epoch_nano_to_timestamp; use super::otel_utils::insert_attributes; -pub const OTEL_TRACES_KNOWN_FIELD_LIST: [&str; 30] = [ +pub const OTEL_TRACES_KNOWN_FIELD_LIST: [&str; 32] = [ "scope_name", "scope_version", "scope_schema_url", @@ -43,8 +43,10 @@ pub const OTEL_TRACES_KNOWN_FIELD_LIST: [&str; 30] = [ "span_kind_description", "span_start_time_unix_nano", "span_end_time_unix_nano", + "span_duration_ms", "event_name", "event_time_unix_nano", + "event_duration_ms", "event_dropped_attributes_count", "link_span_id", "link_trace_id", @@ -169,7 +171,7 @@ pub fn flatten_otel_traces(message: &TracesData) -> Vec { /// otel traces has json array of events /// this function flattens the `Event` object /// and returns a `Vec` of `Map` of the flattened json -fn flatten_events(events: &[Event]) -> Vec> { +fn flatten_events(events: &[Event], span_start_time_unix_nano: u64) -> Vec> { events .iter() .map(|event| { @@ -181,6 +183,20 @@ fn flatten_events(events: &[Event]) -> Vec> { ), ); event_json.insert("event_name".to_string(), Value::String(event.name.clone())); + + // Calculate event duration in milliseconds from span start + let duration_nanos = event + .time_unix_nano + .saturating_sub(span_start_time_unix_nano); + let duration_ms = duration_nanos as f64 / 1_000_000.0; // Convert nanoseconds to milliseconds + event_json.insert( + "event_duration_ms".to_string(), + Value::Number( + serde_json::Number::from_f64(duration_ms) + .unwrap_or(serde_json::Number::from(0)), + ), + ); + insert_attributes(&mut event_json, &event.attributes); event_json.insert( "event_dropped_attributes_count".to_string(), @@ -233,9 +249,9 @@ fn flatten_status(status: &Status) -> Map { Value::Number(status.code.into()), ); let description = match status.code { - 0 => "STATUS_CODE_UNSET", - 1 => "STATUS_CODE_OK", - 2 => "STATUS_CODE_ERROR", + 0 => "UNSET", + 1 => "OK", + 2 => "ERROR", _ => "", }; status_json.insert( @@ -254,10 +270,10 @@ fn flatten_flags(flags: u32) -> Map { let mut flags_json = Map::new(); flags_json.insert("span_flags".to_string(), Value::Number(flags.into())); let description = match flags { - 0 => "SPAN_FLAGS_DO_NOT_USE", - 255 => "SPAN_FLAGS_TRACE_FLAGS_MASK", - 256 => "SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK", - 512 => "SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK", + 0 => "DO_NOT_USE", + 255 => "TRACE_FLAGS_MASK", + 256 => "CONTEXT_HAS_IS_REMOTE_MASK", + 512 => "CONTEXT_IS_REMOTE_MASK", _ => "", }; flags_json.insert( @@ -276,12 +292,12 @@ fn flatten_kind(kind: i32) -> Map { let mut kind_json = Map::new(); kind_json.insert("span_kind".to_string(), Value::Number(kind.into())); let description = match kind { - 0 => "SPAN_KIND_UNSPECIFIED", - 1 => "SPAN_KIND_INTERNAL", - 2 => "SPAN_KIND_SERVER", - 3 => "SPAN_KIND_CLIENT", - 4 => "SPAN_KIND_PRODUCER", - 5 => "SPAN_KIND_CONSUMER", + 0 => "UNSPECIFIED", + 1 => "INTERNAL", + 2 => "SERVER", + 3 => "CLIENT", + 4 => "PRODUCER", + 5 => "CONSUMER", _ => "", }; kind_json.insert( @@ -332,12 +348,25 @@ fn flatten_span_record(span_record: &Span) -> Vec> { span_record.end_time_unix_nano as i64, )), ); + + // Calculate span duration in milliseconds + let duration_nanos = span_record + .end_time_unix_nano + .saturating_sub(span_record.start_time_unix_nano); + let duration_ms = duration_nanos as f64 / 1_000_000.0; // Convert nanoseconds to milliseconds + span_record_json.insert( + "span_duration_ms".to_string(), + Value::Number( + serde_json::Number::from_f64(duration_ms).unwrap_or(serde_json::Number::from(0)), + ), + ); + insert_attributes(&mut span_record_json, &span_record.attributes); span_record_json.insert( "span_dropped_attributes_count".to_string(), Value::Number(span_record.dropped_attributes_count.into()), ); - let events_json = flatten_events(&span_record.events); + let events_json = flatten_events(&span_record.events, span_record.start_time_unix_nano); span_records_json.extend(events_json); span_record_json.insert( "span_dropped_events_count".to_string(), @@ -523,7 +552,7 @@ mod tests { }, ]; - let result = flatten_events(&events); + let result = flatten_events(&events, 1640995200000000000); assert_eq!(result.len(), 2, "Should have two flattened events"); From 445f013a9be8cfa2bcf74b966d857db543876e78 Mon Sep 17 00:00:00 2001 From: Nikhil Sinha Date: Thu, 16 Oct 2025 19:23:03 +0100 Subject: [PATCH 2/3] deepsource fix --- src/otel/traces.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/otel/traces.rs b/src/otel/traces.rs index 0ac9c17a5..e071c5385 100644 --- a/src/otel/traces.rs +++ b/src/otel/traces.rs @@ -193,7 +193,7 @@ fn flatten_events(events: &[Event], span_start_time_unix_nano: u64) -> Vec Vec> { span_record_json.insert( "span_duration_ms".to_string(), Value::Number( - serde_json::Number::from_f64(duration_ms).unwrap_or(serde_json::Number::from(0)), + serde_json::Number::from_f64(duration_ms) + .unwrap_or_else(|| serde_json::Number::from(0)), ), ); From 22d4ef3138c3e8f76b8bd7dc486809ce18c13300 Mon Sep 17 00:00:00 2001 From: Nikhil Sinha Date: Thu, 16 Oct 2025 19:38:20 +0100 Subject: [PATCH 3/3] update tests --- src/otel/traces.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/otel/traces.rs b/src/otel/traces.rs index e071c5385..291cc6781 100644 --- a/src/otel/traces.rs +++ b/src/otel/traces.rs @@ -444,9 +444,9 @@ mod tests { fn test_flatten_status_code_mapping() { // Test that status codes are correctly mapped to descriptions let test_cases = vec![ - (0, "STATUS_CODE_UNSET"), - (1, "STATUS_CODE_OK"), - (2, "STATUS_CODE_ERROR"), + (0, "UNSET"), + (1, "OK"), + (2, "ERROR"), (999, ""), // Unknown status code should return empty string ]; @@ -481,12 +481,12 @@ mod tests { fn test_flatten_span_kind_mapping() { // Test that span kinds are correctly mapped to descriptions let test_cases = vec![ - (0, "SPAN_KIND_UNSPECIFIED"), - (1, "SPAN_KIND_INTERNAL"), - (2, "SPAN_KIND_SERVER"), - (3, "SPAN_KIND_CLIENT"), - (4, "SPAN_KIND_PRODUCER"), - (5, "SPAN_KIND_CONSUMER"), + (0, "UNSPECIFIED"), + (1, "INTERNAL"), + (2, "SERVER"), + (3, "CLIENT"), + (4, "PRODUCER"), + (5, "CONSUMER"), (999, ""), // Unknown kind should return empty string ]; @@ -511,10 +511,10 @@ mod tests { fn test_flatten_flags_mapping() { // Test that flags are correctly mapped to descriptions let test_cases = vec![ - (0, "SPAN_FLAGS_DO_NOT_USE"), - (255, "SPAN_FLAGS_TRACE_FLAGS_MASK"), - (256, "SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK"), - (512, "SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK"), + (0, "DO_NOT_USE"), + (255, "TRACE_FLAGS_MASK"), + (256, "CONTEXT_HAS_IS_REMOTE_MASK"), + (512, "CONTEXT_IS_REMOTE_MASK"), (999, ""), // Unknown flag should return empty string ]; @@ -686,7 +686,7 @@ mod tests { ); assert_eq!( record.get("span_kind_description").unwrap(), - &Value::String("SPAN_KIND_SERVER".to_string()), + &Value::String("SERVER".to_string()), "All records should contain span kind description" ); assert!( @@ -927,7 +927,7 @@ mod tests { ); assert_eq!( record.get("span_kind_description").unwrap(), - &Value::String("SPAN_KIND_CLIENT".to_string()), + &Value::String("CLIENT".to_string()), "Should contain span kind description" ); assert_eq!( @@ -937,7 +937,7 @@ mod tests { ); assert_eq!( record.get("span_status_description").unwrap(), - &Value::String("STATUS_CODE_OK".to_string()), + &Value::String("OK".to_string()), "Should contain status description" ); } @@ -964,12 +964,14 @@ mod tests { "event_name", "event_time_unix_nano", "event_dropped_attributes_count", + "event_duration_ms", "link_span_id", "link_trace_id", "link_dropped_attributes_count", "span_dropped_events_count", "span_dropped_links_count", "span_dropped_attributes_count", + "span_duration_ms", "span_trace_state", "span_flags", "span_flags_description",