diff --git a/opentelemetry-sdk/CHANGELOG.md b/opentelemetry-sdk/CHANGELOG.md index 06e6b39598..c232d21e2e 100644 --- a/opentelemetry-sdk/CHANGELOG.md +++ b/opentelemetry-sdk/CHANGELOG.md @@ -2,6 +2,8 @@ ## vNext +- Implement `LogRecord::set_trace_context` for `LogRecord`. Respect any trace context set on a `LogRecord` when emitting through a `Logger`. + ## v0.26.0 Released 2024-Sep-30 diff --git a/opentelemetry-sdk/src/logs/log_emitter.rs b/opentelemetry-sdk/src/logs/log_emitter.rs index 1b4e1b8c2c..0317a33774 100644 --- a/opentelemetry-sdk/src/logs/log_emitter.rs +++ b/opentelemetry-sdk/src/logs/log_emitter.rs @@ -256,14 +256,17 @@ impl opentelemetry::logs::Logger for Logger { fn emit(&self, mut record: Self::LogRecord) { let provider = self.provider(); let processors = provider.log_processors(); - let trace_context = Context::map_current(|cx| { - cx.has_active_span() - .then(|| TraceContext::from(cx.span().span_context())) - }); //let mut log_record = record; - if let Some(ref trace_context) = trace_context { - record.trace_context = Some(trace_context.clone()); + if record.trace_context.is_none() { + let trace_context = Context::map_current(|cx| { + cx.has_active_span() + .then(|| TraceContext::from(cx.span().span_context())) + }); + + if let Some(ref trace_context) = trace_context { + record.trace_context = Some(trace_context.clone()); + } } if record.observed_timestamp.is_none() { record.observed_timestamp = Some(SystemTime::now()); @@ -293,13 +296,18 @@ impl opentelemetry::logs::Logger for Logger { #[cfg(test)] mod tests { - use crate::resource::{ - SERVICE_NAME, TELEMETRY_SDK_LANGUAGE, TELEMETRY_SDK_NAME, TELEMETRY_SDK_VERSION, + use crate::{ + resource::{ + SERVICE_NAME, TELEMETRY_SDK_LANGUAGE, TELEMETRY_SDK_NAME, TELEMETRY_SDK_VERSION, + }, + testing::logs::InMemoryLogsExporter, + trace::TracerProvider, + Resource, }; - use crate::Resource; use super::*; - use opentelemetry::logs::{Logger, LoggerProvider as _}; + use opentelemetry::logs::{AnyValue, LogRecord as _, Logger as _, LoggerProvider as _}; + use opentelemetry::trace::{SpanId, TraceId, Tracer as _, TracerProvider as _}; use opentelemetry::{Key, KeyValue, Value}; use std::fmt::{Debug, Formatter}; use std::sync::atomic::AtomicU64; @@ -461,6 +469,72 @@ mod tests { assert_eq!(no_service_name.resource().len(), 0); } + #[test] + fn trace_context_test() { + let exporter = InMemoryLogsExporter::default(); + + let logger_provider = LoggerProvider::builder() + .with_simple_exporter(exporter.clone()) + .build(); + + let logger = logger_provider.logger("test-logger"); + + let tracer_provider = TracerProvider::builder().build(); + + let tracer = tracer_provider.tracer("test-tracer"); + + tracer.in_span("test-span", |cx| { + let ambient_ctxt = cx.span().span_context().clone(); + let explicit_ctxt = TraceContext { + trace_id: TraceId::from_u128(13), + span_id: SpanId::from_u64(14), + trace_flags: None, + }; + + let mut ambient_ctxt_record = logger.create_log_record(); + ambient_ctxt_record.set_body(AnyValue::String("ambient".into())); + + let mut explicit_ctxt_record = logger.create_log_record(); + explicit_ctxt_record.set_body(AnyValue::String("explicit".into())); + explicit_ctxt_record.set_trace_context( + explicit_ctxt.trace_id, + explicit_ctxt.span_id, + explicit_ctxt.trace_flags, + ); + + logger.emit(ambient_ctxt_record); + logger.emit(explicit_ctxt_record); + + let emitted = exporter.get_emitted_logs().unwrap(); + + assert_eq!( + Some(AnyValue::String("ambient".into())), + emitted[0].record.body + ); + assert_eq!( + ambient_ctxt.trace_id(), + emitted[0].record.trace_context.as_ref().unwrap().trace_id + ); + assert_eq!( + ambient_ctxt.span_id(), + emitted[0].record.trace_context.as_ref().unwrap().span_id + ); + + assert_eq!( + Some(AnyValue::String("explicit".into())), + emitted[1].record.body + ); + assert_eq!( + explicit_ctxt.trace_id, + emitted[1].record.trace_context.as_ref().unwrap().trace_id + ); + assert_eq!( + explicit_ctxt.span_id, + emitted[1].record.trace_context.as_ref().unwrap().span_id + ); + }); + } + #[test] fn shutdown_test() { let counter = Arc::new(AtomicU64::new(0)); diff --git a/opentelemetry-sdk/src/logs/record.rs b/opentelemetry-sdk/src/logs/record.rs index f1ecfa7d7e..24f3f6a165 100644 --- a/opentelemetry-sdk/src/logs/record.rs +++ b/opentelemetry-sdk/src/logs/record.rs @@ -102,6 +102,19 @@ impl opentelemetry::logs::LogRecord for LogRecord { { self.attributes.push(Some((key.into(), value.into()))); } + + fn set_trace_context( + &mut self, + trace_id: TraceId, + span_id: SpanId, + trace_flags: Option, + ) { + self.trace_context = Some(TraceContext { + trace_id, + span_id, + trace_flags, + }); + } } impl LogRecord { diff --git a/opentelemetry/CHANGELOG.md b/opentelemetry/CHANGELOG.md index 86b43f4f50..9ca361503e 100644 --- a/opentelemetry/CHANGELOG.md +++ b/opentelemetry/CHANGELOG.md @@ -2,6 +2,8 @@ ## vNext +- Add `LogRecord::set_trace_context`; an optional method conditional on the `trace` feature for setting trace context on a log record. + ## v0.26.0 Released 2024-Sep-30 diff --git a/opentelemetry/src/logs/record.rs b/opentelemetry/src/logs/record.rs index 17b4071220..5e57b0c51b 100644 --- a/opentelemetry/src/logs/record.rs +++ b/opentelemetry/src/logs/record.rs @@ -1,4 +1,8 @@ use crate::{Key, StringValue}; + +#[cfg(feature = "trace")] +use crate::trace::{SpanId, TraceFlags, TraceId}; + use std::{borrow::Cow, collections::HashMap, time::SystemTime}; /// SDK implemented trait for managing log records @@ -41,6 +45,19 @@ pub trait LogRecord { where K: Into, V: Into; + + /// Sets the trace context of the log. + #[cfg(feature = "trace")] + fn set_trace_context( + &mut self, + trace_id: TraceId, + span_id: SpanId, + trace_flags: Option, + ) { + let _ = trace_id; + let _ = span_id; + let _ = trace_flags; + } } /// Value types for representing arbitrary values in a log record.