use anyhow::Result;
use std::str::FromStr;
use opentelemetry::{global, sdk::propagation::TraceContextPropagator};
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_semantic_conventions as semcov;
use tonic::{metadata::MetadataKey, service::Interceptor};
use tracing::Span;
use tracing_opentelemetry::OpenTelemetrySpanExt;
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer};
use self::trace_output_fmt::JsonWithTraceId;
pub mod trace_output_fmt;
pub fn set_up_logging() -> Result<()> {
global::set_text_map_propagator(TraceContextPropagator::new());
let tracer = opentelemetry_otlp::new_pipeline()
.tracing()
.with_exporter(opentelemetry_otlp::new_exporter().tonic().with_env())
.with_trace_config(opentelemetry::sdk::trace::config().with_resource(
opentelemetry::sdk::Resource::new(vec![
semcov::resource::SERVICE_NAME.string(env!("CARGO_PKG_NAME")),
semcov::resource::SERVICE_VERSION.string(env!("CARGO_PKG_VERSION")),
]),
))
.install_batch(opentelemetry::runtime::TokioCurrentThread)?;
let opentelemetry = tracing_opentelemetry::layer()
.with_exception_fields(true)
.with_exception_field_propagation(true)
.with_tracer(tracer);
let fmt_layer = fmt::Layer::default().json().event_format(JsonWithTraceId);
let otlp_enabled = std::env::var("NO_OTLP")
.unwrap_or_else(|_| "0".to_owned())
.as_str()
== "0";
let layers = match otlp_enabled {
true => opentelemetry.and_then(fmt_layer).boxed(),
false => fmt::Layer::default().pretty().boxed(),
};
let tracing_registry = tracing_subscriber::registry()
.with(layers.with_filter(
EnvFilter::try_from_default_env().unwrap_or_else(|_| {
EnvFilter::try_new("info").expect("hard-coded default directive should be valid")
}),
));
#[cfg(feature = "tokio-console")]
let tracing_registry = tracing_registry.with(console_subscriber::spawn());
tracing_registry.try_init()?;
Ok(())
}
#[derive(Clone)]
pub struct GrpcInterceptor;
impl Interceptor for GrpcInterceptor {
fn call(&mut self, mut req: tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status> {
let context = Span::current().context();
opentelemetry::global::get_text_map_propagator(|propagator| {
propagator.inject_context(&context, &mut MetadataInjector(req.metadata_mut()));
});
Ok(req)
}
}
pub struct MetadataInjector<'a>(&'a mut tonic::metadata::MetadataMap);
impl<'a> opentelemetry::propagation::Injector for MetadataInjector<'a> {
fn set(&mut self, key: &str, value: String) {
if let Ok(key) = MetadataKey::from_str(key) {
if let Ok(val) = value.parse() {
self.0.insert(key, val);
}
}
}
}
pub type InterceptedGrpcService =
tonic::codegen::InterceptedService<tonic::transport::Channel, GrpcInterceptor>;