/* The benchmark results: criterion = "0.5.1" OS: Ubuntu 22.04.3 LTS (5.15.146.1-microsoft-standard-WSL2) Hardware: AMD EPYC 7763 64-Core Processor - 2.44 GHz, 16vCPUs, RAM: 64.0 GB | Test | Average time| |--------------------------------|-------------| | LogExporterWithFuture | 111 ns | | LogExporterWithoutFuture | 92 ns | */ use opentelemetry::time::now; use opentelemetry_sdk::error::OTelSdkResult; use std::sync::Mutex; use criterion::{criterion_group, criterion_main, Criterion}; use opentelemetry::logs::{LogRecord as _, Logger, LoggerProvider, Severity}; use opentelemetry::InstrumentationScope; use opentelemetry_sdk::logs::LogBatch; use opentelemetry_sdk::logs::LogProcessor; use opentelemetry_sdk::logs::SdkLogRecord; use opentelemetry_sdk::logs::SdkLoggerProvider; #[cfg(all(not(target_os = "windows"), feature = "bench_profiling"))] use pprof::criterion::{Output, PProfProfiler}; use std::fmt::Debug; // Run this benchmark with: // cargo bench --bench log_exporter pub trait LogExporterWithFuture: Send + Sync + Debug { fn export(&mut self, batch: LogBatch<'_>) -> impl std::future::Future + Send; } pub trait LogExporterWithoutFuture: Send + Sync + Debug { fn export(&mut self, batch: LogBatch<'_>); } #[derive(Debug)] struct NoOpExporterWithFuture {} impl LogExporterWithFuture for NoOpExporterWithFuture { async fn export(&mut self, _batch: LogBatch<'_>) {} } #[derive(Debug)] struct NoOpExporterWithoutFuture {} impl LogExporterWithoutFuture for NoOpExporterWithoutFuture { fn export(&mut self, _batch: LogBatch<'_>) {} } #[derive(Debug)] struct ExportingProcessorWithFuture { exporter: Mutex, } impl ExportingProcessorWithFuture { fn new(exporter: NoOpExporterWithFuture) -> Self { ExportingProcessorWithFuture { exporter: Mutex::new(exporter), } } } impl LogProcessor for ExportingProcessorWithFuture { fn emit(&self, record: &mut SdkLogRecord, scope: &InstrumentationScope) { let mut exporter = self.exporter.lock().expect("lock error"); let logs = [(record as &SdkLogRecord, scope)]; futures_executor::block_on(exporter.export(LogBatch::new(&logs))); } fn force_flush(&self) -> OTelSdkResult { Ok(()) } fn shutdown_with_timeout(&self, _timeout: std::time::Duration) -> OTelSdkResult { Ok(()) } } #[derive(Debug)] struct ExportingProcessorWithoutFuture { exporter: Mutex, } impl ExportingProcessorWithoutFuture { fn new(exporter: NoOpExporterWithoutFuture) -> Self { ExportingProcessorWithoutFuture { exporter: Mutex::new(exporter), } } } impl LogProcessor for ExportingProcessorWithoutFuture { fn emit(&self, record: &mut SdkLogRecord, scope: &InstrumentationScope) { let logs = [(record as &SdkLogRecord, scope)]; self.exporter .lock() .expect("lock error") .export(LogBatch::new(&logs)); } fn force_flush(&self) -> OTelSdkResult { Ok(()) } fn shutdown_with_timeout(&self, _timeout: std::time::Duration) -> OTelSdkResult { Ok(()) } } fn criterion_benchmark(c: &mut Criterion) { exporter_with_future(c); exporter_without_future(c); } fn exporter_with_future(c: &mut Criterion) { let provider = SdkLoggerProvider::builder() .with_log_processor(ExportingProcessorWithFuture::new(NoOpExporterWithFuture {})) .build(); let logger = provider.logger("benchmark"); c.bench_function("LogExporterWithFuture", |b| { b.iter(|| { let mut log_record = logger.create_log_record(); let now = now(); log_record.set_observed_timestamp(now); log_record.set_target("my-target".to_string()); log_record.set_event_name("CheckoutFailed"); log_record.set_severity_number(Severity::Warn); log_record.set_severity_text("WARN"); log_record.add_attribute("book_id", "12345"); log_record.add_attribute("book_title", "Rust Programming Adventures"); log_record.add_attribute("message", "Unable to process checkout."); logger.emit(log_record); }); }); } fn exporter_without_future(c: &mut Criterion) { let provider = SdkLoggerProvider::builder() .with_log_processor(ExportingProcessorWithoutFuture::new( NoOpExporterWithoutFuture {}, )) .build(); let logger = provider.logger("benchmark"); c.bench_function("LogExporterWithoutFuture", |b| { b.iter(|| { let mut log_record = logger.create_log_record(); let now = now(); log_record.set_observed_timestamp(now); log_record.set_target("my-target".to_string()); log_record.set_event_name("CheckoutFailed"); log_record.set_severity_number(Severity::Warn); log_record.set_severity_text("WARN"); log_record.add_attribute("book_id", "12345"); log_record.add_attribute("book_title", "Rust Programming Adventures"); log_record.add_attribute("message", "Unable to process checkout."); logger.emit(log_record); }); }); } #[cfg(all(not(target_os = "windows"), feature = "bench_profiling"))] criterion_group! { name = benches; config = Criterion::default() .warm_up_time(std::time::Duration::from_secs(1)) .measurement_time(std::time::Duration::from_secs(2)) .with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); targets = criterion_benchmark } #[cfg(any(target_os = "windows", not(feature = "bench_profiling")))] 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);