Files
nym/common/bin-common/src/opentelemetry/mod.rs
T
Floriane TUERNAL SABOTINOV c2a06298d1 fmt
2025-10-22 09:17:40 +02:00

187 lines
6.4 KiB
Rust

pub mod compact_id_generator;
pub mod context;
mod trace_id_format;
use tracing::{Level, info};
use tracing_subscriber::filter::Directive;
use tracing_subscriber::fmt;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use crate::logging::default_tracing_env_filter;
use crate::logging::error::TracingError;
use crate::opentelemetry::compact_id_generator::Compact13BytesIdGenerator;
use opentelemetry::trace::TracerProvider;
use opentelemetry::{KeyValue, global};
use opentelemetry_otlp::tonic_types::metadata::MetadataMap;
use opentelemetry_otlp::tonic_types::transport::ClientTlsConfig;
use opentelemetry_otlp::{WithExportConfig, WithTonicConfig};
use opentelemetry_sdk::metrics::{MeterProviderBuilder, PeriodicReader, SdkMeterProvider};
use opentelemetry_sdk::trace::SdkTracerProvider;
use opentelemetry_sdk::{Resource, trace::Sampler};
use opentelemetry_semantic_conventions::SCHEMA_URL;
use opentelemetry_semantic_conventions::resource::{DEPLOYMENT_ENVIRONMENT_NAME, SERVICE_VERSION};
use tracing_opentelemetry::{MetricsLayer, OpenTelemetryLayer};
use tracing_subscriber::fmt::format::FmtSpan;
pub struct TracerProviderGuard(Option<SdkTracerProvider>);
impl Drop for TracerProviderGuard {
fn drop(&mut self) {
if let Some(tracer_provider) = self.0.take() {
// Ensure all spans are flushed before exit
if let Err(e) = tracer_provider.shutdown() {
eprintln!("Error shutting down tracer provider: {:?}", e);
}
}
}
}
pub(crate) fn granual_filtered_env() -> Result<tracing_subscriber::filter::EnvFilter, TracingError>
{
fn directive_checked(directive: impl Into<String>) -> Result<Directive, TracingError> {
directive.into().parse().map_err(From::from)
}
let mut filter = default_tracing_env_filter();
// these crates are more granularly filtered
let filter_crates = ["defguard_wireguard_rs"];
for crate_name in filter_crates {
filter = filter.add_directive(directive_checked(format!("{crate_name}=warn"))?);
}
Ok(filter)
}
pub fn setup_tracing_logger(service_name: String) -> Result<TracerProviderGuard, TracingError> {
if tracing::dispatcher::has_been_set() {
// It shouldn't be - this is really checking that it is torn down between async command executions
return Err(TracingError::TracingLoggerAlreadyInitialised);
}
// define ingestion points
let endpoint = std::env::var("SIGNOZ_ENDPOINT").expect("SIGNOZ_ENDPOINT not set");
let key = std::env::var("SIGNOZ_INGESTION_KEY").expect("SIGNOZ_INGESTION_KEY not set");
let mut metadata = MetadataMap::new();
metadata.insert(
"signoz-ingestion-key",
key.parse().expect("Could not parse signoz ingestion key"),
);
// Build resources
let resource = build_resource(&service_name);
// Initialize tracer and meter providers
let tracer_provider = init_tracer_provider(&endpoint, metadata.clone(), resource.clone())?;
let meter_provider = init_meter_provider(&endpoint, metadata.clone(), resource.clone())?;
// Bridge tracing and opentelemetry
let tracer = tracer_provider.tracer("otel-subscriber");
let fmt_layer = fmt::layer()
.json()
.with_writer(std::io::stderr)
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
.with_span_list(false)
.with_current_span(true)
.event_format(trace_id_format::TraceIdFormat);
let registry = tracing_subscriber::registry()
.with(fmt_layer)
.with(granual_filtered_env()?)
.with(tracing_subscriber::filter::LevelFilter::from_level(
Level::INFO,
))
.with(MetricsLayer::new(meter_provider.clone()))
.with(OpenTelemetryLayer::new(tracer));
registry
.try_init()
.map_err(TracingError::TracingTryInitError)?;
global::set_tracer_provider(tracer_provider.clone());
global::set_meter_provider(meter_provider.clone());
info!("Tracing initialized with service name: {}", service_name);
Ok(TracerProviderGuard(Some(tracer_provider)))
}
fn build_resource(service_name: &str) -> Resource {
Resource::builder()
.with_service_name(service_name.to_string())
.with_schema_url(
[
KeyValue::new(SERVICE_VERSION, env!("CARGO_PKG_VERSION")),
KeyValue::new(DEPLOYMENT_ENVIRONMENT_NAME, "develop"),
],
SCHEMA_URL,
)
.build()
}
fn init_tracer_provider(
endpoint: &str,
metadata: MetadataMap,
resource: Resource,
) -> Result<SdkTracerProvider, TracingError> {
let mut exporter_builder = opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.with_metadata(metadata)
.with_endpoint(endpoint);
if endpoint.starts_with("https://") {
exporter_builder =
exporter_builder.with_tls_config(ClientTlsConfig::new().with_enabled_roots());
}
let exporter = exporter_builder.build()?;
let tracer = SdkTracerProvider::builder()
.with_sampler(Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased(
1.0,
))))
.with_id_generator(Compact13BytesIdGenerator)
.with_resource(resource)
.with_batch_exporter(exporter)
.build();
global::set_tracer_provider(tracer.clone());
Ok(tracer)
}
fn init_meter_provider(
endpoint: &str,
metadata: MetadataMap,
resource: Resource,
) -> Result<SdkMeterProvider, TracingError> {
let mut exporter_builder = opentelemetry_otlp::MetricExporter::builder()
.with_tonic()
.with_metadata(metadata)
.with_endpoint(endpoint)
.with_temporality(opentelemetry_sdk::metrics::Temporality::default());
if endpoint.starts_with("https://") {
exporter_builder =
exporter_builder.with_tls_config(ClientTlsConfig::new().with_enabled_roots());
}
let exporter = exporter_builder.build()?;
let reader = PeriodicReader::builder(exporter)
.with_interval(std::time::Duration::from_secs(30))
.build();
let stdout_reader =
PeriodicReader::builder(opentelemetry_stdout::MetricExporter::default()).build();
let meter_provider = MeterProviderBuilder::default()
.with_resource(resource)
.with_reader(reader)
.with_reader(stdout_reader)
.build();
global::set_meter_provider(meter_provider.clone());
Ok(meter_provider)
}