Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions opentelemetry-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
`queue_full` and `already_shutdown`, enabling operators to distinguish
successful processing from drops due to full queue or post-shutdown emits.
([#3514](https://github.com/open-telemetry/opentelemetry-rust/pull/3514))
- **Feature**: Add support for `OTEL_SDK_DISABLED` environment variable [#3088][3088]

[3088]: https://github.com/open-telemetry/opentelemetry-rust/pull/3088

## 0.32.1

Expand Down
5 changes: 5 additions & 0 deletions opentelemetry-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ name = "bound_instruments"
harness = false
required-features = ["metrics", "experimental_metrics_custom_reader", "experimental_metrics_bound_instruments", "spec_unstable_metrics_views"]

[[bench]]
name = "sdk_disabled"
harness = false
required-features = ["trace", "logs", "metrics"]

[lib]
bench = false

Expand Down
239 changes: 239 additions & 0 deletions opentelemetry-sdk/benches/sdk_disabled.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/*
The benchmark results:
criterion = "0.5.1"
rustc rustc 1.98.0-nightly (096694416 2026-06-29)
OS: macOS 26.5.2 (arm64)
Hardware: Apple M1

| Test | api-only | sdk-disabled | sdk-shutdown | sdk-enabled |
|---------|----------|--------------|--------------|-------------|
| trace | 20.3 ns | 31.9 ns | 31.4 ns | 185 ns |
| log | 0.32 ns | 1.28 ns | 1.27 ns | 38 ns |
| metrics | 6.11 ns | 6.11 ns | 6.10 ns | 66 ns |
*/

use criterion::{criterion_group, criterion_main, Criterion};
use opentelemetry::InstrumentationScope;
use opentelemetry::{
logs::{LogRecord, Logger, LoggerProvider, Severity},
metrics::{Counter, MeterProvider},
trace::{Span, Tracer, TracerProvider},
KeyValue,
};
use opentelemetry_sdk::{
error::OTelSdkResult,
logs::{LogProcessor, SdkLogRecord, SdkLoggerProvider},
metrics::{
data::ResourceMetrics, exporter::PushMetricExporter, PeriodicReader, SdkMeterProvider,
Temporality,
},
trace::{Sampler, SdkTracerProvider, SpanData, SpanExporter},
};
use std::time::Duration;

// Run the given closure with the `OTEL_SDK_DISABLED` environment variable set to "true".
fn with_sdk_disabled<T>(f: impl FnOnce() -> T) -> T {
temp_env::with_var("OTEL_SDK_DISABLED", Some("true"), f)
}

#[derive(Debug)]
struct NoOpSpanExporter;

impl SpanExporter for NoOpSpanExporter {
async fn export(&self, _spans: Vec<SpanData>) -> OTelSdkResult {
Ok(())
}
}

#[derive(Debug)]
struct NoOpLogProcessor;

impl LogProcessor for NoOpLogProcessor {
fn emit(&self, _data: &mut SdkLogRecord, _scope: &InstrumentationScope) {}

fn force_flush(&self) -> OTelSdkResult {
Ok(())
}

fn shutdown_with_timeout(&self, _timeout: Duration) -> OTelSdkResult {
Ok(())
}
}

#[derive(Debug)]
struct NoOpMetricExporter;

impl PushMetricExporter for NoOpMetricExporter {
async fn export(&self, _metrics: &ResourceMetrics) -> OTelSdkResult {
Ok(())
}

fn force_flush(&self) -> OTelSdkResult {
Ok(())
}

fn shutdown_with_timeout(&self, _timeout: Duration) -> OTelSdkResult {
Ok(())
}

fn temporality(&self) -> Temporality {
Temporality::Cumulative
}
}

fn emit_span<T: Tracer>(tracer: &T) {
let mut span = tracer.start("span-name");
if span.is_recording() {
span.set_attribute(KeyValue::new("key1", "value1"));
span.set_attribute(KeyValue::new("key2", 123));
}
span.end();
}

fn emit_log<L: Logger>(logger: &L) {
if logger.event_enabled(Severity::Info, "benchmark", None) {
let mut record = logger.create_log_record();
record.set_body("a log message".into());
record.set_severity_number(Severity::Info);
record.add_attribute("key1", "value1");
record.add_attribute("key2", 123);
logger.emit(record);
}
}

fn add_to_counter(counter: &Counter<u64>) {
counter.add(
1,
&[KeyValue::new("key1", "value1"), KeyValue::new("key2", 123)],
);
}

fn trace_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("trace");

group.bench_function("api-only", |b| {
let provider = opentelemetry::trace::noop::NoopTracerProvider::new();
let tracer = provider.tracer("bench");
b.iter(|| emit_span(&tracer));
});

group.bench_function("sdk-disabled", |b| {
let provider = with_sdk_disabled(|| {
SdkTracerProvider::builder()
.with_sampler(Sampler::AlwaysOn)
.build()
});
let tracer = provider.tracer("bench");
b.iter(|| emit_span(&tracer));
});

group.bench_function("sdk-shutdown", |b| {
let provider = SdkTracerProvider::builder()
.with_sampler(Sampler::AlwaysOn)
.with_simple_exporter(NoOpSpanExporter)
.build();
let _ = provider.shutdown();
let tracer = provider.tracer("bench");
b.iter(|| emit_span(&tracer));
});

group.bench_function("sdk-enabled", |b| {
let provider = SdkTracerProvider::builder()
.with_sampler(Sampler::AlwaysOn)
.with_simple_exporter(NoOpSpanExporter)
.build();
let tracer = provider.tracer("bench");
b.iter(|| emit_span(&tracer));
});

group.finish();
}

fn log_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("log");

group.bench_function("api-only", |b| {
let provider = opentelemetry::logs::NoopLoggerProvider::new();
let logger = provider.logger("bench");
b.iter(|| emit_log(&logger));
});

group.bench_function("sdk-disabled", |b| {
let provider = with_sdk_disabled(|| {
SdkLoggerProvider::builder()
.with_log_processor(NoOpLogProcessor)
.build()
});
let logger = provider.logger("bench");
b.iter(|| emit_log(&logger));
});

group.bench_function("sdk-shutdown", |b| {
let provider = SdkLoggerProvider::builder()
.with_log_processor(NoOpLogProcessor)
.build();
let _ = provider.shutdown();
let logger = provider.logger("bench");
b.iter(|| emit_log(&logger));
});

group.bench_function("sdk-enabled", |b| {
let provider = SdkLoggerProvider::builder()
.with_log_processor(NoOpLogProcessor)
.build();
let logger = provider.logger("bench");
b.iter(|| emit_log(&logger));
});

group.finish();
}

fn metrics_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("metrics");

group.bench_function("api-only", |b| {
let provider = opentelemetry::metrics::NoopMeterProvider::new();
let counter = provider.meter("bench").u64_counter("counter").build();
b.iter(|| add_to_counter(&counter));
});

group.bench_function("sdk-disabled", |b| {
let provider = with_sdk_disabled(|| SdkMeterProvider::builder().build());
let counter = provider.meter("bench").u64_counter("counter").build();
b.iter(|| add_to_counter(&counter));
});

group.bench_function("sdk-shutdown", |b| {
let provider = SdkMeterProvider::builder()
.with_reader(PeriodicReader::builder(NoOpMetricExporter).build())
.build();
let _ = provider.shutdown();
let counter = provider.meter("bench").u64_counter("counter").build();
b.iter(|| add_to_counter(&counter));
});

group.bench_function("sdk-enabled", |b| {
let provider = SdkMeterProvider::builder()
.with_reader(PeriodicReader::builder(NoOpMetricExporter).build())
.build();
let counter = provider.meter("bench").u64_counter("counter").build();
b.iter(|| add_to_counter(&counter));
});

group.finish();
}

fn criterion_benchmark(c: &mut Criterion) {
trace_benchmark(c);
log_benchmark(c);
metrics_benchmark(c);
}

criterion_group! {
name = benches;
config = Criterion::default()
.warm_up_time(std::time::Duration::from_secs(1))
.measurement_time(std::time::Duration::from_secs(2));
targets = criterion_benchmark
}
criterion_main!(benches);
13 changes: 9 additions & 4 deletions opentelemetry-sdk/src/logs/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ impl SdkLogger {
pub(crate) fn new(scope: InstrumentationScope, provider: SdkLoggerProvider) -> Self {
SdkLogger { scope, provider }
}

#[cfg(test)]
pub(crate) fn provider(&self) -> &SdkLoggerProvider {
&self.provider
}
}

impl opentelemetry::logs::Logger for SdkLogger {
Expand Down Expand Up @@ -58,13 +63,13 @@ impl opentelemetry::logs::Logger for SdkLogger {

#[inline]
fn event_enabled(&self, level: Severity, target: &str, name: Option<&str>) -> bool {
if Context::is_current_telemetry_suppressed() {
let processors = self.provider.log_processors();
// Early return if there are no processors
if processors.is_empty() || Context::is_current_telemetry_suppressed() {
return false;
}
// Returns false if there are no log processors.
// Returns true if at least one processor returns true.
self.provider
.log_processors()
processors
.iter()
.any(|processor| processor.event_enabled(level, target, name))
}
Expand Down
Loading
Loading