build(deps): bump opentelemetry crates to 0.27 (#6774)

This commit is contained in:
Sanchith Hegde
2024-12-10 16:35:34 +05:30
committed by GitHub
parent a52828296a
commit 47a3d2b2ab
86 changed files with 739 additions and 1086 deletions

View File

@ -3,20 +3,6 @@
use std::time::Duration;
use ::config::ConfigError;
use opentelemetry::{
global, runtime,
sdk::{
export::metrics::aggregation::cumulative_temporality_selector,
metrics::{controllers::BasicController, selectors::simple},
propagation::TraceContextPropagator,
trace,
trace::BatchConfig,
Resource,
},
trace::{TraceContextExt, TraceState},
KeyValue,
};
use opentelemetry_otlp::{TonicExporterBuilder, WithExportConfig};
use serde_json::ser::{CompactFormatter, PrettyFormatter};
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::{fmt, prelude::*, util::SubscriberInitExt, EnvFilter, Layer};
@ -27,7 +13,6 @@ use crate::{config, FormattingLayer, StorageSubscription};
#[derive(Debug)]
pub struct TelemetryGuard {
_log_guards: Vec<WorkerGuard>,
_metrics_controller: Option<BasicController>,
}
/// Setup logging sub-system specifying the logging configuration, service (binary) name, and a
@ -47,10 +32,9 @@ pub fn setup(
} else {
None
};
let _metrics_controller = if config.telemetry.metrics_enabled {
if config.telemetry.metrics_enabled {
setup_metrics_pipeline(&config.telemetry)
} else {
None
};
// Setup file logging
@ -132,21 +116,23 @@ pub fn setup(
// dropped
Ok(TelemetryGuard {
_log_guards: guards,
_metrics_controller,
})
}
fn get_opentelemetry_exporter(config: &config::LogTelemetry) -> TonicExporterBuilder {
let mut exporter_builder = opentelemetry_otlp::new_exporter().tonic();
fn get_opentelemetry_exporter_config(
config: &config::LogTelemetry,
) -> opentelemetry_otlp::ExportConfig {
let mut exporter_config = opentelemetry_otlp::ExportConfig {
protocol: opentelemetry_otlp::Protocol::Grpc,
endpoint: config.otel_exporter_otlp_endpoint.clone(),
..Default::default()
};
if let Some(ref endpoint) = config.otel_exporter_otlp_endpoint {
exporter_builder = exporter_builder.with_endpoint(endpoint);
}
if let Some(timeout) = config.otel_exporter_otlp_timeout {
exporter_builder = exporter_builder.with_timeout(Duration::from_millis(timeout));
exporter_config.timeout = Duration::from_millis(timeout);
}
exporter_builder
exporter_config
}
#[derive(Debug, Clone)]
@ -192,39 +178,41 @@ impl TraceAssertion {
/// Conditional Sampler for providing control on url based tracing
#[derive(Clone, Debug)]
struct ConditionalSampler<T: trace::ShouldSample + Clone + 'static>(TraceAssertion, T);
struct ConditionalSampler<T: opentelemetry_sdk::trace::ShouldSample + Clone + 'static>(
TraceAssertion,
T,
);
impl<T: trace::ShouldSample + Clone + 'static> trace::ShouldSample for ConditionalSampler<T> {
impl<T: opentelemetry_sdk::trace::ShouldSample + Clone + 'static>
opentelemetry_sdk::trace::ShouldSample for ConditionalSampler<T>
{
fn should_sample(
&self,
parent_context: Option<&opentelemetry::Context>,
trace_id: opentelemetry::trace::TraceId,
name: &str,
span_kind: &opentelemetry::trace::SpanKind,
attributes: &opentelemetry::trace::OrderMap<opentelemetry::Key, opentelemetry::Value>,
attributes: &[opentelemetry::KeyValue],
links: &[opentelemetry::trace::Link],
instrumentation_library: &opentelemetry::InstrumentationLibrary,
) -> opentelemetry::trace::SamplingResult {
use opentelemetry::trace::TraceContextExt;
match attributes
.get(&opentelemetry::Key::new("http.route"))
.iter()
.find(|&kv| kv.key == opentelemetry::Key::new("http.route"))
.map_or(self.0.default, |inner| {
self.0.should_trace_url(&inner.as_str())
self.0.should_trace_url(&inner.value.as_str())
}) {
true => self.1.should_sample(
parent_context,
trace_id,
name,
span_kind,
attributes,
links,
instrumentation_library,
),
true => {
self.1
.should_sample(parent_context, trace_id, name, span_kind, attributes, links)
}
false => opentelemetry::trace::SamplingResult {
decision: opentelemetry::trace::SamplingDecision::Drop,
attributes: Vec::new(),
trace_state: match parent_context {
Some(ctx) => ctx.span().span_context().trace_state().clone(),
None => TraceState::default(),
None => opentelemetry::trace::TraceState::default(),
},
},
}
@ -234,95 +222,117 @@ impl<T: trace::ShouldSample + Clone + 'static> trace::ShouldSample for Condition
fn setup_tracing_pipeline(
config: &config::LogTelemetry,
service_name: &str,
) -> Option<tracing_opentelemetry::OpenTelemetryLayer<tracing_subscriber::Registry, trace::Tracer>>
{
global::set_text_map_propagator(TraceContextPropagator::new());
) -> Option<
tracing_opentelemetry::OpenTelemetryLayer<
tracing_subscriber::Registry,
opentelemetry_sdk::trace::Tracer,
>,
> {
use opentelemetry::trace::TracerProvider;
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_sdk::trace;
let mut trace_config = trace::config()
opentelemetry::global::set_text_map_propagator(
opentelemetry_sdk::propagation::TraceContextPropagator::new(),
);
// Set the export interval to 1 second
let batch_config = trace::BatchConfigBuilder::default()
.with_scheduled_delay(Duration::from_millis(1000))
.build();
let exporter_result = opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.with_export_config(get_opentelemetry_exporter_config(config))
.build();
let exporter = if config.ignore_errors {
#[allow(clippy::print_stderr)] // The logger hasn't been initialized yet
exporter_result
.inspect_err(|error| eprintln!("Failed to build traces exporter: {error:?}"))
.ok()?
} else {
// Safety: This is conditional, there is an option to avoid this behavior at runtime.
#[allow(clippy::expect_used)]
exporter_result.expect("Failed to build traces exporter")
};
let mut provider_builder = trace::TracerProvider::builder()
.with_span_processor(
trace::BatchSpanProcessor::builder(
exporter,
// The runtime would have to be updated if a different web framework is used
opentelemetry_sdk::runtime::TokioCurrentThread,
)
.with_batch_config(batch_config)
.build(),
)
.with_sampler(trace::Sampler::ParentBased(Box::new(ConditionalSampler(
TraceAssertion {
clauses: config
.route_to_trace
.clone()
.map(|inner| inner.into_iter().map(Into::into).collect()),
.map(|inner| inner.into_iter().map(TraceUrlAssert::from).collect()),
default: false,
},
trace::Sampler::TraceIdRatioBased(config.sampling_rate.unwrap_or(1.0)),
))))
.with_resource(Resource::new(vec![KeyValue::new(
"service.name",
service_name.to_owned(),
)]));
.with_resource(opentelemetry_sdk::Resource::new(vec![
opentelemetry::KeyValue::new("service.name", service_name.to_owned()),
]));
if config.use_xray_generator {
trace_config = trace_config.with_id_generator(trace::XrayIdGenerator::default());
provider_builder = provider_builder
.with_id_generator(opentelemetry_aws::trace::XrayIdGenerator::default());
}
// Change the default export interval from 5 seconds to 1 second
let batch_config = BatchConfig::default().with_scheduled_delay(Duration::from_millis(1000));
let traces_layer_result = opentelemetry_otlp::new_pipeline()
.tracing()
.with_exporter(get_opentelemetry_exporter(config))
.with_batch_config(batch_config)
.with_trace_config(trace_config)
.install_batch(runtime::TokioCurrentThread)
.map(|tracer| tracing_opentelemetry::layer().with_tracer(tracer));
#[allow(clippy::print_stderr)] // The logger hasn't been initialized yet
if config.ignore_errors {
traces_layer_result
.map_err(|error| {
eprintln!("Failed to create an `opentelemetry_otlp` tracer: {error:?}")
})
.ok()
} else {
// Safety: This is conditional, there is an option to avoid this behavior at runtime.
#[allow(clippy::expect_used)]
Some(traces_layer_result.expect("Failed to create an `opentelemetry_otlp` tracer"))
}
Some(
tracing_opentelemetry::layer()
.with_tracer(provider_builder.build().tracer(service_name.to_owned())),
)
}
fn setup_metrics_pipeline(config: &config::LogTelemetry) -> Option<BasicController> {
let histogram_buckets = {
let mut init = 0.01;
let mut buckets: [f64; 15] = [0.0; 15];
fn setup_metrics_pipeline(config: &config::LogTelemetry) {
use opentelemetry_otlp::WithExportConfig;
for bucket in &mut buckets {
init *= 2.0;
*bucket = init;
}
buckets
};
let metrics_controller_result = opentelemetry_otlp::new_pipeline()
.metrics(
simple::histogram(histogram_buckets),
cumulative_temporality_selector(),
// This would have to be updated if a different web framework is used
runtime::TokioCurrentThread,
)
.with_exporter(get_opentelemetry_exporter(config))
.with_period(Duration::from_secs(3))
.with_timeout(Duration::from_secs(10))
.with_resource(Resource::new(vec![KeyValue::new(
"pod",
std::env::var("POD_NAME").map_or(
"hyperswitch-server-default".into(),
Into::<opentelemetry::Value>::into,
),
)]))
let exporter_result = opentelemetry_otlp::MetricExporter::builder()
.with_tonic()
.with_temporality(opentelemetry_sdk::metrics::Temporality::Cumulative)
.with_export_config(get_opentelemetry_exporter_config(config))
.build();
#[allow(clippy::print_stderr)] // The logger hasn't been initialized yet
if config.ignore_errors {
metrics_controller_result
.map_err(|error| eprintln!("Failed to setup metrics pipeline: {error:?}"))
.ok()
let exporter = if config.ignore_errors {
#[allow(clippy::print_stderr)] // The logger hasn't been initialized yet
exporter_result
.inspect_err(|error| eprintln!("Failed to build metrics exporter: {error:?}"))
.ok();
return;
} else {
// Safety: This is conditional, there is an option to avoid this behavior at runtime.
#[allow(clippy::expect_used)]
Some(metrics_controller_result.expect("Failed to setup metrics pipeline"))
}
exporter_result.expect("Failed to build metrics exporter")
};
let reader = opentelemetry_sdk::metrics::PeriodicReader::builder(
exporter,
// The runtime would have to be updated if a different web framework is used
opentelemetry_sdk::runtime::TokioCurrentThread,
)
.with_interval(Duration::from_secs(3))
.with_timeout(Duration::from_secs(10))
.build();
let provider = opentelemetry_sdk::metrics::SdkMeterProvider::builder()
.with_reader(reader)
.with_resource(opentelemetry_sdk::Resource::new([
opentelemetry::KeyValue::new(
"pod",
std::env::var("POD_NAME").unwrap_or(String::from("hyperswitch-server-default")),
),
]))
.build();
opentelemetry::global::set_meter_provider(provider);
}
fn get_envfilter(

View File

@ -1,16 +1,5 @@
//! Utilities to easily create opentelemetry contexts, meters and metrics.
/// Create a metrics [`Context`][Context] with the specified name.
///
/// [Context]: opentelemetry::Context
#[macro_export]
macro_rules! metrics_context {
($name:ident) => {
pub(crate) static $name: once_cell::sync::Lazy<$crate::opentelemetry::Context> =
once_cell::sync::Lazy::new($crate::opentelemetry::Context::current);
};
}
/// Create a global [`Meter`][Meter] with the specified name and an optional description.
///
/// [Meter]: opentelemetry::metrics::Meter
@ -20,14 +9,14 @@ macro_rules! global_meter {
static $name: once_cell::sync::Lazy<$crate::opentelemetry::metrics::Meter> =
once_cell::sync::Lazy::new(|| $crate::opentelemetry::global::meter(stringify!($name)));
};
($name:ident, $description:literal) => {
static $name: once_cell::sync::Lazy<$crate::opentelemetry::metrics::Meter> =
once_cell::sync::Lazy::new(|| $crate::opentelemetry::global::meter($description));
($meter:ident, $name:literal) => {
static $meter: once_cell::sync::Lazy<$crate::opentelemetry::metrics::Meter> =
once_cell::sync::Lazy::new(|| $crate::opentelemetry::global::meter(stringify!($name)));
};
}
/// Create a [`Counter`][Counter] metric with the specified name and an optional description,
/// associated with the specified meter. Note that the meter must be to a valid [`Meter`][Meter].
/// associated with the specified meter. Note that the meter must be a valid [`Meter`][Meter].
///
/// [Counter]: opentelemetry::metrics::Counter
/// [Meter]: opentelemetry::metrics::Meter
@ -36,36 +25,54 @@ macro_rules! counter_metric {
($name:ident, $meter:ident) => {
pub(crate) static $name: once_cell::sync::Lazy<
$crate::opentelemetry::metrics::Counter<u64>,
> = once_cell::sync::Lazy::new(|| $meter.u64_counter(stringify!($name)).init());
> = once_cell::sync::Lazy::new(|| $meter.u64_counter(stringify!($name)).build());
};
($name:ident, $meter:ident, description:literal) => {
#[doc = $description]
pub(crate) static $name: once_cell::sync::Lazy<
$crate::opentelemetry::metrics::Counter<u64>,
> = once_cell::sync::Lazy::new(|| $meter.u64_counter($description).init());
> = once_cell::sync::Lazy::new(|| {
$meter
.u64_counter(stringify!($name))
.with_description($description)
.build()
});
};
}
/// Create a [`Histogram`][Histogram] metric with the specified name and an optional description,
/// associated with the specified meter. Note that the meter must be to a valid [`Meter`][Meter].
/// Create a [`Histogram`][Histogram] f64 metric with the specified name and an optional description,
/// associated with the specified meter. Note that the meter must be a valid [`Meter`][Meter].
///
/// [Histogram]: opentelemetry::metrics::Histogram
/// [Meter]: opentelemetry::metrics::Meter
#[macro_export]
macro_rules! histogram_metric {
macro_rules! histogram_metric_f64 {
($name:ident, $meter:ident) => {
pub(crate) static $name: once_cell::sync::Lazy<
$crate::opentelemetry::metrics::Histogram<f64>,
> = once_cell::sync::Lazy::new(|| $meter.f64_histogram(stringify!($name)).init());
> = once_cell::sync::Lazy::new(|| {
$meter
.f64_histogram(stringify!($name))
.with_boundaries($crate::metrics::f64_histogram_buckets())
.build()
});
};
($name:ident, $meter:ident, $description:literal) => {
#[doc = $description]
pub(crate) static $name: once_cell::sync::Lazy<
$crate::opentelemetry::metrics::Histogram<f64>,
> = once_cell::sync::Lazy::new(|| $meter.f64_histogram($description).init());
> = once_cell::sync::Lazy::new(|| {
$meter
.f64_histogram(stringify!($name))
.with_description($description)
.with_boundaries($crate::metrics::f64_histogram_buckets())
.build()
});
};
}
/// Create a [`Histogram`][Histogram] u64 metric with the specified name and an optional description,
/// associated with the specified meter. Note that the meter must be to a valid [`Meter`][Meter].
/// associated with the specified meter. Note that the meter must be a valid [`Meter`][Meter].
///
/// [Histogram]: opentelemetry::metrics::Histogram
/// [Meter]: opentelemetry::metrics::Meter
@ -74,64 +81,72 @@ macro_rules! histogram_metric_u64 {
($name:ident, $meter:ident) => {
pub(crate) static $name: once_cell::sync::Lazy<
$crate::opentelemetry::metrics::Histogram<u64>,
> = once_cell::sync::Lazy::new(|| $meter.u64_histogram(stringify!($name)).init());
> = once_cell::sync::Lazy::new(|| {
$meter
.u64_histogram(stringify!($name))
.with_boundaries($crate::metrics::f64_histogram_buckets())
.build()
});
};
($name:ident, $meter:ident, $description:literal) => {
#[doc = $description]
pub(crate) static $name: once_cell::sync::Lazy<
$crate::opentelemetry::metrics::Histogram<u64>,
> = once_cell::sync::Lazy::new(|| $meter.u64_histogram($description).init());
> = once_cell::sync::Lazy::new(|| {
$meter
.u64_histogram(stringify!($name))
.with_description($description)
.with_boundaries($crate::metrics::f64_histogram_buckets())
.build()
});
};
}
/// Create a [`Histogram`][Histogram] i64 metric with the specified name and an optional description,
/// associated with the specified meter. Note that the meter must be to a valid [`Meter`][Meter].
/// Create a [`Gauge`][Gauge] metric with the specified name and an optional description,
/// associated with the specified meter. Note that the meter must be a valid [`Meter`][Meter].
///
/// [Histogram]: opentelemetry::metrics::Histogram
/// [Meter]: opentelemetry::metrics::Meter
#[macro_export]
macro_rules! histogram_metric_i64 {
($name:ident, $meter:ident) => {
pub(crate) static $name: once_cell::sync::Lazy<
$crate::opentelemetry::metrics::Histogram<i64>,
> = once_cell::sync::Lazy::new(|| $meter.i64_histogram(stringify!($name)).init());
};
($name:ident, $meter:ident, $description:literal) => {
pub(crate) static $name: once_cell::sync::Lazy<
$crate::opentelemetry::metrics::Histogram<i64>,
> = once_cell::sync::Lazy::new(|| $meter.i64_histogram($description).init());
};
}
/// Create a [`ObservableGauge`][ObservableGauge] metric with the specified name and an optional description,
/// associated with the specified meter. Note that the meter must be to a valid [`Meter`][Meter].
///
/// [ObservableGauge]: opentelemetry::metrics::ObservableGauge
/// [Gauge]: opentelemetry::metrics::Gauge
/// [Meter]: opentelemetry::metrics::Meter
#[macro_export]
macro_rules! gauge_metric {
($name:ident, $meter:ident) => {
pub(crate) static $name: once_cell::sync::Lazy<
$crate::opentelemetry::metrics::ObservableGauge<u64>,
> = once_cell::sync::Lazy::new(|| $meter.u64_observable_gauge(stringify!($name)).init());
pub(crate) static $name: once_cell::sync::Lazy<$crate::opentelemetry::metrics::Gauge<u64>> =
once_cell::sync::Lazy::new(|| $meter.u64_gauge(stringify!($name)).build());
};
($name:ident, $meter:ident, description:literal) => {
pub(crate) static $name: once_cell::sync::Lazy<
$crate::opentelemetry::metrics::ObservableGauge<u64>,
> = once_cell::sync::Lazy::new(|| $meter.u64_observable_gauge($description).init());
#[doc = $description]
pub(crate) static $name: once_cell::sync::Lazy<$crate::opentelemetry::metrics::Gauge<u64>> =
once_cell::sync::Lazy::new(|| {
$meter
.u64_gauge(stringify!($name))
.with_description($description)
.build()
});
};
}
pub use helpers::add_attributes;
/// Create attributes to associate with a metric from key-value pairs.
#[macro_export]
macro_rules! metric_attributes {
($(($key:expr, $value:expr $(,)?)),+ $(,)?) => {
&[$($crate::opentelemetry::KeyValue::new($key, $value)),+]
};
}
pub use helpers::f64_histogram_buckets;
mod helpers {
pub fn add_attributes<T, U>(attributes: U) -> Vec<opentelemetry::KeyValue>
where
T: Into<opentelemetry::Value>,
U: IntoIterator<Item = (&'static str, T)>,
{
attributes
.into_iter()
.map(|(key, value)| opentelemetry::KeyValue::new(key, value))
.collect::<Vec<_>>()
/// Returns the buckets to be used for a f64 histogram
#[inline(always)]
pub fn f64_histogram_buckets() -> Vec<f64> {
let mut init = 0.01;
let mut buckets: [f64; 15] = [0.0; 15];
for bucket in &mut buckets {
init *= 2.0;
*bucket = init;
}
Vec::from(buckets)
}
}