diff --git a/opentelemetry-appender-tracing/CHANGELOG.md b/opentelemetry-appender-tracing/CHANGELOG.md index dacc012cf7..2817291fea 100644 --- a/opentelemetry-appender-tracing/CHANGELOG.md +++ b/opentelemetry-appender-tracing/CHANGELOG.md @@ -42,6 +42,11 @@ transparent to most users. implementations (SDK, processor, exporters) to leverage this additional information to determine if an event is enabled. +- `u64` and `usize` values are stored as `opentelemetry::logs::AnyValue::Int` +when conversion is feasible. Otherwise stored as +`opentelemetry::logs::AnyValue::String`. This avoids unnecessary string +allocation when values can be represented in their original types. + ## 0.28.1 Released 2025-Feb-12 diff --git a/opentelemetry-appender-tracing/src/layer.rs b/opentelemetry-appender-tracing/src/layer.rs index 6a15bc9dba..9333df861f 100644 --- a/opentelemetry-appender-tracing/src/layer.rs +++ b/opentelemetry-appender-tracing/src/layer.rs @@ -114,6 +114,22 @@ impl tracing::field::Visit for EventVisitor<'_, LR> { .add_attribute(Key::new(field.name()), AnyValue::from(value)); } + // TODO: We might need to do similar for record_i128,record_u128 too + // to avoid stringification, unless needed. + fn record_u64(&mut self, field: &tracing::field::Field, value: u64) { + #[cfg(feature = "experimental_metadata_attributes")] + if is_duplicated_metadata(field.name()) { + return; + } + if let Ok(signed) = i64::try_from(value) { + self.log_record + .add_attribute(Key::new(field.name()), AnyValue::from(signed)); + } else { + self.log_record + .add_attribute(Key::new(field.name()), AnyValue::from(format!("{value:?}"))); + } + } + // TODO: Remaining field types from AnyValue : Bytes, ListAny, Boolean } @@ -331,7 +347,11 @@ mod tests { let _guard = tracing::subscriber::set_default(subscriber); // Act - error!(name: "my-event-name", target: "my-system", event_id = 20, user_name = "otel", user_email = "otel@opentelemetry.io"); + let small_u64value: u64 = 42; + let big_u64value: u64 = u64::MAX; + let small_usizevalue: usize = 42; + let big_usizevalue: usize = usize::MAX; + error!(name: "my-event-name", target: "my-system", event_id = 20, small_u64value, big_u64value, small_usizevalue, big_usizevalue, user_name = "otel", user_email = "otel@opentelemetry.io"); assert!(logger_provider.force_flush().is_ok()); // Assert TODO: move to helper methods @@ -362,9 +382,9 @@ mod tests { // Validate attributes #[cfg(not(feature = "experimental_metadata_attributes"))] - assert_eq!(log.record.attributes_iter().count(), 3); - #[cfg(feature = "experimental_metadata_attributes")] assert_eq!(log.record.attributes_iter().count(), 7); + #[cfg(feature = "experimental_metadata_attributes")] + assert_eq!(log.record.attributes_iter().count(), 11); assert!(attributes_contains( &log.record, &Key::new("event_id"), @@ -380,6 +400,26 @@ mod tests { &Key::new("user_email"), &AnyValue::String("otel@opentelemetry.io".into()) )); + assert!(attributes_contains( + &log.record, + &Key::new("small_u64value"), + &AnyValue::Int(42.into()) + )); + assert!(attributes_contains( + &log.record, + &Key::new("big_u64value"), + &AnyValue::String(format!("{}", u64::MAX).into()) + )); + assert!(attributes_contains( + &log.record, + &Key::new("small_usizevalue"), + &AnyValue::Int(42.into()) + )); + assert!(attributes_contains( + &log.record, + &Key::new("big_usizevalue"), + &AnyValue::String(format!("{}", u64::MAX).into()) + )); #[cfg(feature = "experimental_metadata_attributes")] { assert!(attributes_contains( @@ -753,6 +793,10 @@ mod tests { TraceFlags::SAMPLED ); + for attribute in log.record.attributes_iter() { + println!("key: {:?}, value: {:?}", attribute.0, attribute.1); + } + // Attributes can be polluted when we don't use this feature. #[cfg(feature = "experimental_metadata_attributes")] assert_eq!(log.record.attributes_iter().count(), 4);