1pub mod tracing {
2 use tracing_subscriber::EnvFilter;
3
4 use crate::errors::Result;
5
6 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 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 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 #[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}