use opentelemetry::trace::TracerProvider as _;
use opentelemetry_otlp::{SpanExporter, WithExportConfig};
use opentelemetry_sdk::Resource;
use opentelemetry_sdk::trace::SdkTracerProvider;
use tracing_subscriber::prelude::*;
type BoxError = Box<dyn std::error::Error + Send + Sync>;
pub struct OtelGuard {
provider: SdkTracerProvider,
}
impl Drop for OtelGuard {
fn drop(&mut self) {
let _ = self.provider.shutdown();
}
}
pub fn otlp_tracer(
service_name: impl Into<String>,
endpoint: impl Into<String>,
) -> Result<(opentelemetry_sdk::trace::Tracer, OtelGuard), BoxError> {
let exporter = SpanExporter::builder()
.with_tonic()
.with_endpoint(endpoint.into())
.build()?;
let provider = SdkTracerProvider::builder()
.with_batch_exporter(exporter)
.with_resource(
Resource::builder()
.with_service_name(service_name.into())
.build(),
)
.build();
let tracer = provider.tracer("harness-rs-loop");
Ok((tracer, OtelGuard { provider }))
}
pub fn telemetry_layer<S>(
tracer: opentelemetry_sdk::trace::Tracer,
) -> impl tracing_subscriber::Layer<S>
where
S: tracing::Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
{
use tracing_subscriber::Layer as _;
tracing_opentelemetry::layer()
.with_tracer(tracer)
.with_filter(
tracing_subscriber::filter::Targets::new()
.with_target("harness.telemetry", tracing::Level::INFO),
)
}
pub fn otlp_layer<S>(
service_name: impl Into<String>,
endpoint: impl Into<String>,
) -> Result<(impl tracing_subscriber::Layer<S>, OtelGuard), BoxError>
where
S: tracing::Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
{
let (tracer, guard) = otlp_tracer(service_name, endpoint)?;
Ok((telemetry_layer(tracer), guard))
}
pub fn init_tracing_with_otlp(
service_name: impl Into<String>,
endpoint: impl Into<String>,
) -> Result<OtelGuard, BoxError> {
use tracing_subscriber::Layer as _;
let (otel, guard) = otlp_layer(service_name, endpoint)?;
let logs = tracing_subscriber::fmt::layer()
.with_writer(std::io::stderr)
.with_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("warn")),
);
tracing_subscriber::registry()
.with(logs)
.with(otel)
.try_init()?;
Ok(guard)
}