|
| 1 | +/* |
| 2 | + The benchmark results: |
| 3 | + criterion = "0.5.1" |
| 4 | + OS: Ubuntu 22.04.2 LTS (5.10.102.1-microsoft-standard-WSL2) |
| 5 | + Hardware: AMD EPYC 7763 64-Core Processor - 2.44 GHz, 16vCPUs, |
| 6 | + RAM: 64.0 GB |
| 7 | + | Test | Average time| |
| 8 | + |-----------------------------|-------------| |
| 9 | + | log_no_subscriber | 313 ps | |
| 10 | + | noop_layer_disabled | 12 ns | |
| 11 | + | noop_layer_enabled | 25 ns | |
| 12 | + | ot_layer_disabled | 19 ns | |
| 13 | + | ot_layer_enabled | 561 ns | |
| 14 | +*/ |
| 15 | + |
| 16 | +use async_trait::async_trait; |
| 17 | +use criterion::{criterion_group, criterion_main, Criterion}; |
| 18 | +use opentelemetry::logs::LogResult; |
| 19 | +use opentelemetry::KeyValue; |
| 20 | +use opentelemetry_appender_tracing::layer as tracing_layer; |
| 21 | +use opentelemetry_sdk::export::logs::{LogData, LogExporter}; |
| 22 | +use opentelemetry_sdk::logs::{Config, LogProcessor, LoggerProvider}; |
| 23 | +use opentelemetry_sdk::Resource; |
| 24 | +use tracing::error; |
| 25 | +use tracing_subscriber::prelude::*; |
| 26 | +use tracing_subscriber::Layer; |
| 27 | +use tracing_subscriber::Registry; |
| 28 | + |
| 29 | +#[derive(Debug, Clone)] |
| 30 | +struct NoopExporter { |
| 31 | + enabled: bool, |
| 32 | +} |
| 33 | + |
| 34 | +#[async_trait] |
| 35 | +impl LogExporter for NoopExporter { |
| 36 | + async fn export(&mut self, _: Vec<LogData>) -> LogResult<()> { |
| 37 | + LogResult::Ok(()) |
| 38 | + } |
| 39 | + |
| 40 | + fn event_enabled(&self, _: opentelemetry::logs::Severity, _: &str, _: &str) -> bool { |
| 41 | + self.enabled |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +#[derive(Debug)] |
| 46 | +struct NoopProcessor { |
| 47 | + exporter: Box<dyn LogExporter>, |
| 48 | +} |
| 49 | + |
| 50 | +impl NoopProcessor { |
| 51 | + fn new(exporter: Box<dyn LogExporter>) -> Self { |
| 52 | + Self { exporter } |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +impl LogProcessor for NoopProcessor { |
| 57 | + fn emit(&self, _: LogData) { |
| 58 | + // no-op |
| 59 | + } |
| 60 | + |
| 61 | + fn force_flush(&self) -> LogResult<()> { |
| 62 | + Ok(()) |
| 63 | + } |
| 64 | + |
| 65 | + fn shutdown(&mut self) -> LogResult<()> { |
| 66 | + Ok(()) |
| 67 | + } |
| 68 | + |
| 69 | + fn event_enabled( |
| 70 | + &self, |
| 71 | + level: opentelemetry::logs::Severity, |
| 72 | + target: &str, |
| 73 | + name: &str, |
| 74 | + ) -> bool { |
| 75 | + self.exporter.event_enabled(level, target, name) |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +struct NoOpLogLayer { |
| 80 | + enabled: bool, |
| 81 | +} |
| 82 | + |
| 83 | +impl<S> Layer<S> for NoOpLogLayer |
| 84 | +where |
| 85 | + S: tracing::Subscriber, |
| 86 | +{ |
| 87 | + fn on_event( |
| 88 | + &self, |
| 89 | + event: &tracing::Event<'_>, |
| 90 | + _ctx: tracing_subscriber::layer::Context<'_, S>, |
| 91 | + ) { |
| 92 | + let mut visitor = NoopEventVisitor; |
| 93 | + event.record(&mut visitor); |
| 94 | + } |
| 95 | + |
| 96 | + fn event_enabled( |
| 97 | + &self, |
| 98 | + _event: &tracing::Event<'_>, |
| 99 | + _ctx: tracing_subscriber::layer::Context<'_, S>, |
| 100 | + ) -> bool { |
| 101 | + self.enabled |
| 102 | + } |
| 103 | +} |
| 104 | + |
| 105 | +struct NoopEventVisitor; |
| 106 | + |
| 107 | +impl tracing::field::Visit for NoopEventVisitor { |
| 108 | + fn record_debug(&mut self, _field: &tracing::field::Field, _value: &dyn std::fmt::Debug) {} |
| 109 | +} |
| 110 | + |
| 111 | +fn benchmark_no_subscriber(c: &mut Criterion) { |
| 112 | + c.bench_function("log_no_subscriber", |b| { |
| 113 | + b.iter(|| { |
| 114 | + error!( |
| 115 | + name = "CheckoutFailed", |
| 116 | + book_id = "12345", |
| 117 | + book_title = "Rust Programming Adventures", |
| 118 | + "Unable to process checkout." |
| 119 | + ); |
| 120 | + }); |
| 121 | + }); |
| 122 | +} |
| 123 | + |
| 124 | +fn benchmark_with_ot_layer(c: &mut Criterion, enabled: bool, bench_name: &str) { |
| 125 | + let exporter = NoopExporter { enabled }; |
| 126 | + let processor = NoopProcessor::new(Box::new(exporter)); |
| 127 | + let provider = LoggerProvider::builder() |
| 128 | + .with_config( |
| 129 | + Config::default().with_resource(Resource::new(vec![KeyValue::new( |
| 130 | + "service.name", |
| 131 | + "benchmark", |
| 132 | + )])), |
| 133 | + ) |
| 134 | + .with_log_processor(processor) |
| 135 | + .build(); |
| 136 | + let ot_layer = tracing_layer::OpenTelemetryTracingBridge::new(&provider); |
| 137 | + let subscriber = Registry::default().with(ot_layer); |
| 138 | + |
| 139 | + tracing::subscriber::with_default(subscriber, || { |
| 140 | + c.bench_function(bench_name, |b| { |
| 141 | + b.iter(|| { |
| 142 | + error!( |
| 143 | + name = "CheckoutFailed", |
| 144 | + book_id = "12345", |
| 145 | + book_title = "Rust Programming Adventures", |
| 146 | + "Unable to process checkout." |
| 147 | + ); |
| 148 | + }); |
| 149 | + }); |
| 150 | + }); |
| 151 | +} |
| 152 | + |
| 153 | +fn benchmark_with_noop_layer(c: &mut Criterion, enabled: bool, bench_name: &str) { |
| 154 | + let subscriber = Registry::default().with(NoOpLogLayer { enabled }); |
| 155 | + |
| 156 | + tracing::subscriber::with_default(subscriber, || { |
| 157 | + c.bench_function(bench_name, |b| { |
| 158 | + b.iter(|| { |
| 159 | + error!( |
| 160 | + name = "CheckoutFailed", |
| 161 | + book_id = "12345", |
| 162 | + book_title = "Rust Programming Adventures", |
| 163 | + "Unable to process checkout." |
| 164 | + ); |
| 165 | + }); |
| 166 | + }); |
| 167 | + }); |
| 168 | +} |
| 169 | + |
| 170 | +fn criterion_benchmark(c: &mut Criterion) { |
| 171 | + benchmark_no_subscriber(c); |
| 172 | + benchmark_with_ot_layer(c, true, "ot_layer_enabled"); |
| 173 | + benchmark_with_ot_layer(c, false, "ot_layer_disabled"); |
| 174 | + benchmark_with_noop_layer(c, true, "noop_layer_enabled"); |
| 175 | + benchmark_with_noop_layer(c, false, "noop_layer_disabled"); |
| 176 | +} |
| 177 | + |
| 178 | +criterion_group!(benches, criterion_benchmark); |
| 179 | +criterion_main!(benches); |
0 commit comments