Skip to content

Commit 9ca7b29

Browse files
authored
Merge branch 'main' into refactor-baggage-context
2 parents dced93e + 88cae2c commit 9ca7b29

File tree

8 files changed

+185
-31
lines changed

8 files changed

+185
-31
lines changed

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,12 @@ opentelemetry = { path = "opentelemetry" }
8585
opentelemetry_sdk = { path = "opentelemetry-sdk" }
8686
opentelemetry-stdout = { path = "opentelemetry-stdout" }
8787

88+
[workspace.lints.rust]
89+
rust_2024_compatibility = { level = "warn", priority = -1 }
90+
# No need to enable those, because it either not needed or results in ugly syntax
91+
edition_2024_expr_fragment_specifier = "allow"
92+
if_let_rescope = "allow"
93+
tail_expr_drop_order = "allow"
94+
8895
[workspace.lints.clippy]
8996
all = { level = "warn", priority = 1 }

docs/design/logs.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,19 @@ convey that decision back to logger, allowing appender to avoid even the cost of
296296
creating a `LogRecord` in the first place if there is no listener. This check is
297297
done for each log emission, and can react dynamically to changes in interest, by
298298
enabling/disabling ETW/user-event listener.
299+
5. `tracing` has a notion of "target", which is expected to be mapped to OTel's
300+
concept of Instrumentation Scope for Logs, when `OpenTelemetry-Tracing-Appender`
301+
bridges `tracing` to OpenTelemetry. Since scopes are tied to Loggers, a naive
302+
approach would require creating a separate logger for each unique target. This
303+
would necessitate an RWLock-protected HashMap lookup, introducing contention and
304+
reducing throughput. To avoid this, `OpenTelemetry-Tracing-Appender` instead
305+
stores the target directly in the LogRecord as a top-level field, ensuring fast
306+
access in the hot path. Components processing the LogRecord can retrieve the
307+
target via LogRecord.target(), treating it as the scope. The OTLP Exporter
308+
already handles this automatically, so end-users will see “target” reflected in
309+
the Instrumentation Scope. An alternative design would be to use thread-local
310+
HashMaps - but it can cause increased memory usage, as there can be 100s of
311+
unique targets. (because `tracing` defaults to using module path as target).
299312

300313
### Perf test - benchmarks
301314

opentelemetry-appender-tracing/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ simple string, but require format arguments as in the below example.
3131
error!(name: "my-event-name", target: "my-system", event_id = 20, user_name = "otel", user_email = "[email protected]", "This is an example message with format arguments {} and {}", "foo", "bar");
3232
```
3333

34+
Fixes [2658](https://github.com/open-telemetry/opentelemetry-rust/issues/2658)
35+
InstrumentationScope(Logger) used by the appender now uses an empty ("") named
36+
Logger. Previously, a Logger with name and version of the crate was used.
37+
Receivers (processors, exporters) are expected to use `LogRecord.target()` as
38+
scope name. This is already done in OTLP Exporters, so this change should be
39+
transparent to most users.
40+
3441
## 0.28.1
3542

3643
Released 2025-Feb-12

opentelemetry-appender-tracing/src/layer.rs

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
use opentelemetry::{
22
logs::{AnyValue, LogRecord, Logger, LoggerProvider, Severity},
3-
InstrumentationScope, Key,
3+
Key,
44
};
5-
use std::borrow::Cow;
65
use tracing_core::Level;
76
#[cfg(feature = "experimental_metadata_attributes")]
87
use tracing_core::Metadata;
98
#[cfg(feature = "experimental_metadata_attributes")]
109
use tracing_log::NormalizeEvent;
1110
use tracing_subscriber::{registry::LookupSpan, Layer};
1211

13-
const INSTRUMENTATION_LIBRARY_NAME: &str = "opentelemetry-appender-tracing";
14-
1512
/// Visitor to record the fields from the event record.
1613
struct EventVisitor<'a, LR: LogRecord> {
1714
log_record: &'a mut LR,
@@ -135,12 +132,13 @@ where
135132
L: Logger + Send + Sync,
136133
{
137134
pub fn new(provider: &P) -> Self {
138-
let scope = InstrumentationScope::builder(INSTRUMENTATION_LIBRARY_NAME)
139-
.with_version(Cow::Borrowed(env!("CARGO_PKG_VERSION")))
140-
.build();
141-
142135
OpenTelemetryTracingBridge {
143-
logger: provider.logger_with_scope(scope),
136+
// Using empty scope name.
137+
// The name/version of this library itself can be added
138+
// as a Scope attribute, once a semantic convention is
139+
// defined for the same.
140+
// See https://github.com/open-telemetry/semantic-conventions/issues/1550
141+
logger: provider.logger(""),
144142
_phantom: Default::default(),
145143
}
146144
}
@@ -245,6 +243,7 @@ mod tests {
245243
.any(|(k, v)| k == key && v == value)
246244
}
247245

246+
#[allow(impl_trait_overcaptures)] // can only be fixed with Rust 1.82+
248247
fn create_tracing_subscriber(
249248
_exporter: InMemoryLogExporter,
250249
logger_provider: &SdkLoggerProvider,
@@ -348,8 +347,18 @@ mod tests {
348347
.expect("Atleast one log is expected to be present.");
349348

350349
// Validate common fields
351-
assert_eq!(log.instrumentation.name(), "opentelemetry-appender-tracing");
350+
assert_eq!(log.instrumentation.name(), "");
352351
assert_eq!(log.record.severity_number(), Some(Severity::Error));
352+
// Validate target
353+
assert_eq!(
354+
log.record.target().expect("target is expected").to_string(),
355+
"my-system"
356+
);
357+
// Validate event name
358+
assert_eq!(
359+
log.record.event_name().expect("event_name is expected"),
360+
"my-event-name"
361+
);
353362

354363
// Validate trace context is none.
355364
assert!(log.record.trace_context().is_none());
@@ -398,6 +407,39 @@ mod tests {
398407
assert!(attributes_key.contains(&Key::new("code.lineno")));
399408
assert!(!attributes_key.contains(&Key::new("log.target")));
400409
}
410+
411+
// Test when target, eventname are not explicitly provided
412+
exporter.reset();
413+
error!(
414+
event_id = 20,
415+
user_name = "otel",
416+
user_email = "[email protected]"
417+
);
418+
assert!(logger_provider.force_flush().is_ok());
419+
420+
// Assert TODO: move to helper methods
421+
let exported_logs = exporter
422+
.get_emitted_logs()
423+
.expect("Logs are expected to be exported.");
424+
assert_eq!(exported_logs.len(), 1);
425+
let log = exported_logs
426+
.first()
427+
.expect("Atleast one log is expected to be present.");
428+
429+
// Validate target - tracing defaults to module path
430+
assert_eq!(
431+
log.record.target().expect("target is expected").to_string(),
432+
"opentelemetry_appender_tracing::layer::tests"
433+
);
434+
// Validate event name - tracing defaults to event followed source & line number
435+
// Assert is doing "contains" check to avoid tests failing when line number changes.
436+
// and also account for the fact that the module path is different on different platforms.
437+
// Ex.: The path will be different on a Windows and Linux machine.
438+
assert!(log
439+
.record
440+
.event_name()
441+
.expect("event_name is expected")
442+
.contains("event opentelemetry-appender-tracing"),);
401443
}
402444

403445
#[test]
@@ -442,8 +484,18 @@ mod tests {
442484
.expect("Atleast one log is expected to be present.");
443485

444486
// validate common fields.
445-
assert_eq!(log.instrumentation.name(), "opentelemetry-appender-tracing");
487+
assert_eq!(log.instrumentation.name(), "");
446488
assert_eq!(log.record.severity_number(), Some(Severity::Error));
489+
// Validate target
490+
assert_eq!(
491+
log.record.target().expect("target is expected").to_string(),
492+
"my-system"
493+
);
494+
// Validate event name
495+
assert_eq!(
496+
log.record.event_name().expect("event_name is expected"),
497+
"my-event-name"
498+
);
447499

448500
// validate trace context.
449501
assert!(log.record.trace_context().is_some());
@@ -583,7 +635,7 @@ mod tests {
583635
drop(tracing_log::LogTracer::init());
584636

585637
// Act
586-
log::error!(target: "my-system", "log from log crate");
638+
log::error!("log from log crate");
587639
assert!(logger_provider.force_flush().is_ok());
588640

589641
// Assert TODO: move to helper methods
@@ -596,8 +648,19 @@ mod tests {
596648
.expect("Atleast one log is expected to be present.");
597649

598650
// Validate common fields
599-
assert_eq!(log.instrumentation.name(), "opentelemetry-appender-tracing");
651+
assert_eq!(log.instrumentation.name(), "");
600652
assert_eq!(log.record.severity_number(), Some(Severity::Error));
653+
// Target and EventName from Log crate are "log" and "log event" respectively.
654+
// Validate target
655+
assert_eq!(
656+
log.record.target().expect("target is expected").to_string(),
657+
"log"
658+
);
659+
// Validate event name
660+
assert_eq!(
661+
log.record.event_name().expect("event_name is expected"),
662+
"log event"
663+
);
601664

602665
// Validate trace context is none.
603666
assert!(log.record.trace_context().is_none());
@@ -675,7 +738,7 @@ mod tests {
675738
.expect("Atleast one log is expected to be present.");
676739

677740
// validate common fields.
678-
assert_eq!(log.instrumentation.name(), "opentelemetry-appender-tracing");
741+
assert_eq!(log.instrumentation.name(), "");
679742
assert_eq!(log.record.severity_number(), Some(Severity::Error));
680743

681744
// validate trace context.

opentelemetry-sdk/src/logs/mod.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ pub mod log_processor_with_async_runtime;
3535
mod tests {
3636
use super::*;
3737
use crate::Resource;
38+
use opentelemetry::baggage::BaggageExt;
3839
use opentelemetry::logs::LogRecord;
3940
use opentelemetry::logs::{Logger, LoggerProvider, Severity};
40-
use opentelemetry::InstrumentationScope;
4141
use opentelemetry::{logs::AnyValue, Key, KeyValue};
42+
use opentelemetry::{Context, InstrumentationScope};
4243
use std::borrow::Borrow;
4344
use std::collections::HashMap;
4445

@@ -150,4 +151,63 @@ mod tests {
150151
.attributes()
151152
.eq(&[KeyValue::new("test_k", "test_v")]));
152153
}
154+
155+
#[derive(Debug)]
156+
struct EnrichWithBaggageProcessor;
157+
impl LogProcessor for EnrichWithBaggageProcessor {
158+
fn emit(&self, data: &mut SdkLogRecord, _instrumentation: &InstrumentationScope) {
159+
Context::map_current(|cx| {
160+
for (kk, vv) in cx.baggage().iter() {
161+
data.add_attribute(kk.clone(), vv.0.clone());
162+
}
163+
});
164+
}
165+
166+
fn force_flush(&self) -> crate::error::OTelSdkResult {
167+
Ok(())
168+
}
169+
170+
fn shutdown(&self) -> crate::error::OTelSdkResult {
171+
Ok(())
172+
}
173+
}
174+
#[test]
175+
fn log_and_baggage() {
176+
// Arrange
177+
let exporter: InMemoryLogExporter = InMemoryLogExporter::default();
178+
let logger_provider = SdkLoggerProvider::builder()
179+
.with_log_processor(EnrichWithBaggageProcessor)
180+
.with_log_processor(SimpleLogProcessor::new(exporter.clone()))
181+
.build();
182+
183+
// Act
184+
let logger = logger_provider.logger("test-logger");
185+
let context_with_baggage =
186+
Context::current_with_baggage(vec![KeyValue::new("key-from-bag", "value-from-bag")]);
187+
let _cx_guard = context_with_baggage.attach();
188+
let mut log_record = logger.create_log_record();
189+
log_record.add_attribute("key", "value");
190+
logger.emit(log_record);
191+
192+
// Assert
193+
let exported_logs = exporter
194+
.get_emitted_logs()
195+
.expect("Logs are expected to be exported.");
196+
assert_eq!(exported_logs.len(), 1);
197+
let log = exported_logs
198+
.first()
199+
.expect("Atleast one log is expected to be present.");
200+
assert_eq!(log.instrumentation.name(), "test-logger");
201+
assert_eq!(log.record.attributes_len(), 2);
202+
203+
// Assert that the log record contains the baggage attribute
204+
// and the attribute added to the log record.
205+
assert!(log
206+
.record
207+
.attributes_contains(&Key::new("key"), &AnyValue::String("value".into())));
208+
assert!(log.record.attributes_contains(
209+
&Key::new("key-from-bag"),
210+
&AnyValue::String("value-from-bag".into())
211+
));
212+
}
153213
}

opentelemetry-sdk/src/trace/id_generator/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ pub struct RandomIdGenerator {
2222

2323
impl IdGenerator for RandomIdGenerator {
2424
fn new_trace_id(&self) -> TraceId {
25-
CURRENT_RNG.with(|rng| TraceId::from(rng.borrow_mut().gen::<u128>()))
25+
CURRENT_RNG.with(|rng| TraceId::from(rng.borrow_mut().random::<u128>()))
2626
}
2727

2828
fn new_span_id(&self) -> SpanId {
29-
CURRENT_RNG.with(|rng| SpanId::from(rng.borrow_mut().gen::<u64>()))
29+
CURRENT_RNG.with(|rng| SpanId::from(rng.borrow_mut().random::<u64>()))
3030
}
3131
}
3232

opentelemetry/src/baggage.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
//! Baggage can be sent between systems using a baggage propagator in
1414
//! accordance with the [W3C Baggage] specification.
1515
//!
16+
//! Note: Baggage is not automatically added to any telemetry. Users have to
17+
//! explicitly add baggage entries to telemetry items.
18+
//!
19+
//!
1620
//! [W3C Baggage]: https://w3c.github.io/baggage
1721
use crate::{Context, Key, KeyValue, StringValue};
1822
use std::collections::hash_map::Entry;
@@ -75,10 +79,10 @@ impl Baggage {
7579
/// ```
7680
/// use opentelemetry::{baggage::Baggage, StringValue};
7781
///
78-
/// let mut cc = Baggage::new();
79-
/// let _ = cc.insert("my-name", "my-value");
82+
/// let mut baggage = Baggage::new();
83+
/// let _ = baggage.insert("my-name", "my-value");
8084
///
81-
/// assert_eq!(cc.get("my-name"), Some(&StringValue::from("my-value")))
85+
/// assert_eq!(baggage.get("my-name"), Some(&StringValue::from("my-value")))
8286
/// ```
8387
pub fn get<K: AsRef<str>>(&self, key: K) -> Option<&StringValue> {
8488
self.inner.get(key.as_ref()).map(|(value, _metadata)| value)
@@ -90,11 +94,11 @@ impl Baggage {
9094
/// ```
9195
/// use opentelemetry::{baggage::{Baggage, BaggageMetadata}, StringValue};
9296
///
93-
/// let mut cc = Baggage::new();
94-
/// let _ = cc.insert("my-name", "my-value");
97+
/// let mut baggage = Baggage::new();
98+
/// let _ = baggage.insert("my-name", "my-value");
9599
///
96100
/// // By default, the metadata is empty
97-
/// assert_eq!(cc.get_with_metadata("my-name"), Some(&(StringValue::from("my-value"), BaggageMetadata::from(""))))
101+
/// assert_eq!(baggage.get_with_metadata("my-name"), Some(&(StringValue::from("my-value"), BaggageMetadata::from(""))))
98102
/// ```
99103
pub fn get_with_metadata<K: AsRef<str>>(
100104
&self,
@@ -113,10 +117,10 @@ impl Baggage {
113117
/// ```
114118
/// use opentelemetry::{baggage::Baggage, StringValue};
115119
///
116-
/// let mut cc = Baggage::new();
117-
/// let _ = cc.insert("my-name", "my-value");
120+
/// let mut baggage = Baggage::new();
121+
/// let _ = baggage.insert("my-name", "my-value");
118122
///
119-
/// assert_eq!(cc.get("my-name"), Some(&StringValue::from("my-value")))
123+
/// assert_eq!(baggage.get("my-name"), Some(&StringValue::from("my-value")))
120124
/// ```
121125
pub fn insert<K, V>(&mut self, key: K, value: V) -> Option<StringValue>
122126
where
@@ -127,7 +131,7 @@ impl Baggage {
127131
.map(|pair| pair.0)
128132
}
129133

130-
/// Inserts a name/value pair into the baggage.
134+
/// Inserts a name/value(+metadata) pair into the baggage.
131135
///
132136
/// Same with `insert`, if the name was not present, [`None`] will be returned.
133137
/// If the name is present, the old value and metadata will be returned.
@@ -139,10 +143,10 @@ impl Baggage {
139143
/// ```
140144
/// use opentelemetry::{baggage::{Baggage, BaggageMetadata}, StringValue};
141145
///
142-
/// let mut cc = Baggage::new();
143-
/// let _ = cc.insert_with_metadata("my-name", "my-value", "test");
146+
/// let mut baggage = Baggage::new();
147+
/// let _ = baggage.insert_with_metadata("my-name", "my-value", "test");
144148
///
145-
/// assert_eq!(cc.get_with_metadata("my-name"), Some(&(StringValue::from("my-value"), BaggageMetadata::from("test"))))
149+
/// assert_eq!(baggage.get_with_metadata("my-name"), Some(&(StringValue::from("my-value"), BaggageMetadata::from("test"))))
146150
/// ```
147151
pub fn insert_with_metadata<K, V, S>(
148152
&mut self,

stress/src/throughput.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ impl<'a> UnsafeSlice<'a> {
155155
#[inline(always)]
156156
unsafe fn increment(&self, i: usize) {
157157
let value = self.slice[i].get();
158-
(*value).count += 1;
158+
unsafe { (*value).count += 1 };
159159
}
160160

161161
#[inline(always)]

0 commit comments

Comments
 (0)