observability_kit/
lib.rs

1#![deny(
2    explicit_outlives_requirements,
3    macro_use_extern_crate,
4    missing_debug_implementations,
5    trivial_casts,
6    trivial_numeric_casts,
7    unreachable_pub,
8    unsafe_code,
9    unused_qualifications,
10    unused_results,
11    variant_size_differences,
12    unused_variables,
13    clippy::complexity,
14    clippy::nursery,
15    clippy::pedantic,
16    clippy::perf,
17    clippy::style,
18    clippy::suspicious,
19    clippy::clone_on_ref_ptr,
20    clippy::create_dir,
21    clippy::dbg_macro,
22    clippy::default_numeric_fallback,
23    clippy::else_if_without_else,
24    clippy::empty_structs_with_brackets,
25    clippy::expect_used,
26    clippy::get_unwrap,
27    clippy::let_underscore_must_use,
28    clippy::map_err_ignore,
29    clippy::multiple_inherent_impl,
30    clippy::panic,
31    clippy::panic_in_result_fn,
32    clippy::pub_use,
33    clippy::rc_mutex,
34    clippy::rest_pat_in_fully_bound_structs,
35    clippy::same_name_method,
36    clippy::self_named_module_files,
37    clippy::shadow_reuse,
38    clippy::shadow_same,
39    clippy::shadow_unrelated,
40    clippy::unseparated_literal_suffix,
41    clippy::string_to_string,
42    clippy::todo,
43    clippy::unimplemented,
44    clippy::unreachable,
45    clippy::unwrap_in_result,
46    clippy::unwrap_used,
47    clippy::use_debug,
48    clippy::verbose_file_reads,
49    clippy::wildcard_enum_match_arm
50)]
51
52pub mod tracing {
53    use eyre::{eyre, Result};
54    use tracing_subscriber::EnvFilter;
55
56    /// Initialize default tracing infrastructure for the service.
57    ///
58    /// # Errors
59    ///
60    /// This function will return an error if some error occurs or initialization was already done before
61    /// (it may be the case if you run several tests in parallel).
62    pub fn init() -> Result<()> {
63        tracing_subscriber::fmt()
64            .with_env_filter(EnvFilter::from_default_env())
65            .try_init()
66            .map_err(|error| eyre!("failed to initialize subscriber: {}", error))
67    }
68}
69
70pub mod otlp_tracing {
71    //! Open Telemetry Protocol configured tracing.
72
73    use eyre::Result;
74    use opentelemetry::KeyValue;
75    use opentelemetry_otlp::WithExportConfig;
76    use opentelemetry_sdk::{
77        runtime,
78        trace::{BatchConfig, RandomIdGenerator, Sampler},
79        Resource,
80    };
81    use opentelemetry_semantic_conventions::{
82        resource::{SERVICE_NAME, SERVICE_VERSION},
83        SCHEMA_URL,
84    };
85    use serde::Deserialize;
86    use tracing::instrument;
87    use tracing_opentelemetry::OpenTelemetryLayer;
88    use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
89
90    pub const DEFAULT_OTLP_GRPC_ENDPOINT: &str = "http://localhost:4317";
91
92    #[derive(Clone, Debug, Deserialize)]
93    #[serde(default)]
94    pub struct Configuration {
95        pub otlp_grpc_endpoint: String,
96    }
97
98    /// Initialize Open Telemetry Protocol tracing infrastructure for the service.
99    /// It should be supported by modern tracing tools, like [Jaeger](https://medium.com/jaegertracing/introducing-native-support-for-opentelemetry-in-jaeger-eb661be8183c).
100    ///
101    /// # Errors
102    ///
103    /// This function will return an error if some error occurs or initialization was already done before
104    /// (it may be the case if you run several tests in parallel).
105    pub fn try_init(service_name: &'static str) -> Result<()> {
106        let configuration = Configuration::from_env()?;
107        tracing_subscriber::registry()
108            .with(EnvFilter::from_default_env())
109            .with(tracing_subscriber::fmt::layer())
110            .with(OpenTelemetryLayer::new(
111                opentelemetry_otlp::new_pipeline()
112                    .tracing()
113                    .with_trace_config(
114                        opentelemetry_sdk::trace::Config::default()
115                            .with_sampler(Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased(1.0))))
116                            .with_id_generator(RandomIdGenerator::default())
117                            .with_resource(Resource::from_schema_url(
118                                [
119                                    KeyValue::new(SERVICE_NAME, service_name),
120                                    KeyValue::new(SERVICE_VERSION, env!("CARGO_PKG_VERSION")),
121                                ],
122                                SCHEMA_URL,
123                            )),
124                    )
125                    .with_batch_config(BatchConfig::default())
126                    .with_exporter(
127                        opentelemetry_otlp::new_exporter()
128                            .tonic()
129                            .with_endpoint(configuration.otlp_grpc_endpoint),
130                    )
131                    .install_batch(runtime::Tokio)?,
132            ))
133            .try_init()?;
134        Ok(())
135    }
136
137    impl Configuration {
138        #[instrument(err(Debug), ret, level = "trace")]
139        pub fn from_env() -> Result<Self> {
140            Ok(envy::prefixed("OBSERVABILITY_").from_env::<Self>()?)
141        }
142    }
143
144    impl Default for Configuration {
145        fn default() -> Self {
146            Self {
147                otlp_grpc_endpoint: DEFAULT_OTLP_GRPC_ENDPOINT.to_string(),
148            }
149        }
150    }
151}