observability_kit/
lib.rs

1pub mod tracing {
2    use tracing_subscriber::EnvFilter;
3
4    use crate::errors::Result;
5
6    /// Initialize default tracing infrastructure for the service.
7    ///
8    /// # Errors
9    ///
10    /// This function will return an error if some error occurs or initialization was already done before
11    /// (it may be the case if you run several tests in parallel).
12    pub fn init() -> Result<()> {
13        Ok(tracing_subscriber::fmt()
14            .with_env_filter(EnvFilter::from_default_env())
15            .try_init()?)
16    }
17}
18
19pub mod otlp_tracing {
20    //! Open Telemetry Protocol configured tracing.
21
22    use opentelemetry::{global, trace::TracerProvider, KeyValue};
23    use opentelemetry_otlp::{Protocol, SpanExporter, WithExportConfig};
24    use opentelemetry_sdk::{
25        trace::{RandomIdGenerator, Sampler, SdkTracerProvider},
26        Resource,
27    };
28    use opentelemetry_semantic_conventions::resource::SERVICE_VERSION;
29    use serde::Deserialize;
30    use tracing::instrument;
31    use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
32
33    use crate::errors::Result;
34
35    pub const DEFAULT_OTLP_GRPC_ENDPOINT: &str = "http://localhost:4317";
36
37    #[derive(Clone, Debug, Deserialize)]
38    #[serde(default)]
39    pub struct Configuration {
40        pub otlp_grpc_endpoint: String,
41    }
42
43    /// Initialize Open Telemetry Protocol tracing infrastructure for the service.
44    ///
45    /// It should be supported by modern tracing tools, like [Jaeger](https://medium.com/jaegertracing/introducing-native-support-for-opentelemetry-in-jaeger-eb661be8183c).
46    ///
47    /// # Errors
48    ///
49    /// This function will return an error if some error occurs or initialization was already done before
50    /// (it may be the case if you run several tests in parallel).
51    pub fn try_init(service_name: &'static str) -> Result<()> {
52        let configuration = Configuration::from_env()?;
53        let new_provider = SdkTracerProvider::builder()
54            .with_id_generator(RandomIdGenerator::default())
55            .with_sampler(Sampler::ParentBased(Box::new(Sampler::AlwaysOn)))
56            .with_resource(
57                Resource::builder()
58                    .with_service_name(service_name)
59                    .with_attribute(KeyValue::new(SERVICE_VERSION, env!("CARGO_PKG_VERSION")))
60                    .build(),
61            )
62            .with_batch_exporter(
63                SpanExporter::builder()
64                    .with_tonic()
65                    .with_protocol(Protocol::Grpc)
66                    .with_endpoint(configuration.otlp_grpc_endpoint)
67                    .build()?,
68            )
69            .build();
70        let tracer = new_provider.tracer("tracing-otel-subscriber");
71        let _previous_global_tracer_provider = global::set_tracer_provider(new_provider);
72        tracing_subscriber::registry()
73            .with(EnvFilter::from_default_env())
74            .with(tracing_subscriber::fmt::layer())
75            .with(tracing_opentelemetry::layer().with_tracer(tracer))
76            .try_init()?;
77        Ok(())
78    }
79
80    impl Configuration {
81        /// Read configuration from environment.
82        ///
83        /// # Errors
84        ///
85        /// Usually doesn't return any errors as default value is used for `OBSERVABILITY_OTLP_GRPC_ENDPOINT`
86        /// configuration parameter.
87        #[instrument(err(Debug), ret, level = "trace")]
88        pub fn from_env() -> Result<Self> {
89            Ok(envy::prefixed("OBSERVABILITY_").from_env::<Self>()?)
90        }
91    }
92
93    impl Default for Configuration {
94        fn default() -> Self {
95            Self {
96                otlp_grpc_endpoint: DEFAULT_OTLP_GRPC_ENDPOINT.to_string(),
97            }
98        }
99    }
100}
101
102pub mod errors {
103    use std::{error::Error as StdError, result::Result as StdResult};
104
105    use derive_more::{Display, Error, From};
106    use envy::Error as EnvyError;
107    use opentelemetry_otlp::ExporterBuildError;
108    use tracing_subscriber::util::TryInitError;
109
110    #[derive(Debug, Display, Error, From)]
111    pub enum Error {
112        ExporterBuildError(ExporterBuildError),
113        ConfigurationDeserializationFromEnvironmentError(EnvyError),
114        SubscriberInitError(TryInitError),
115        Other(Box<dyn StdError + Send + Sync>),
116    }
117
118    pub type Result<T> = StdResult<T, Error>;
119}