use opentelemetry::trace::TracerProvider as _;
use opentelemetry_otlp::{Protocol, WithExportConfig};
use opentelemetry_sdk::{
trace::{RandomIdGenerator, Sampler, SdkTracerProvider},
Resource,
};
use tracing::Subscriber;
use tracing_opentelemetry::OpenTelemetryLayer;
use tracing_subscriber::{registry::LookupSpan, Layer};
pub type OtelError = Box<dyn std::error::Error + Send + Sync + 'static>;
pub fn layer<S>(
endpoint: Option<&str>,
service_name: Option<&str>,
sample_percent: Option<u8>,
) -> Result<(Option<impl Layer<S>>, Option<SdkTracerProvider>), OtelError>
where
S: Subscriber + for<'span> LookupSpan<'span>,
{
let endpoint = match endpoint {
Some(ep) => ep,
None => return Ok((None, None)), };
let endpoint = if endpoint.ends_with("/v1/traces") {
endpoint.to_string()
} else {
format!("{}/v1/traces", endpoint.trim_end_matches('/'))
};
let service_name = service_name.unwrap_or("zebra");
let sample_rate = f64::from(sample_percent.unwrap_or(100).min(100)) / 100.0;
let exporter = opentelemetry_otlp::SpanExporter::builder()
.with_http()
.with_protocol(Protocol::HttpBinary)
.with_endpoint(&endpoint)
.build()?;
let sampler = if sample_rate >= 1.0 {
Sampler::AlwaysOn
} else if sample_rate <= 0.0 {
Sampler::AlwaysOff
} else {
Sampler::TraceIdRatioBased(sample_rate)
};
let resource = Resource::builder()
.with_service_name(service_name.to_owned())
.build();
let provider = SdkTracerProvider::builder()
.with_batch_exporter(exporter)
.with_sampler(sampler)
.with_id_generator(RandomIdGenerator::default())
.with_resource(resource)
.build();
let tracer = provider.tracer(service_name.to_owned());
let layer = OpenTelemetryLayer::new(tracer);
Ok((Some(layer), Some(provider)))
}