Line data Source code
1 : //! Helper functions to set up OpenTelemetry tracing.
2 : //!
3 : //! Example:
4 : //!
5 : //! ```rust,no_run
6 : //! use tracing_subscriber::prelude::*;
7 : //!
8 : //! #[tokio::main]
9 : //! async fn main() {
10 : //! // Set up logging to stderr
11 : //! let env_filter = tracing_subscriber::EnvFilter::try_from_default_env()
12 : //! .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info"));
13 : //! let fmt_layer = tracing_subscriber::fmt::layer()
14 : //! .with_target(false)
15 : //! .with_writer(std::io::stderr);
16 : //!
17 : //! // Initialize OpenTelemetry. Exports tracing spans as OpenTelemetry traces
18 : //! let provider = tracing_utils::init_tracing("my_application", tracing_utils::ExportConfig::default());
19 : //! let otlp_layer = provider.as_ref().map(tracing_utils::layer);
20 : //!
21 : //! // Put it all together
22 : //! tracing_subscriber::registry()
23 : //! .with(env_filter)
24 : //! .with(otlp_layer)
25 : //! .with(fmt_layer)
26 : //! .init();
27 : //! }
28 : //! ```
29 : #![deny(clippy::undocumented_unsafe_blocks)]
30 :
31 : pub mod http;
32 : pub mod perf_span;
33 :
34 : use opentelemetry::trace::TracerProvider;
35 : use opentelemetry_otlp::WithExportConfig;
36 : pub use opentelemetry_otlp::{ExportConfig, Protocol};
37 : use opentelemetry_sdk::trace::SdkTracerProvider;
38 : use tracing::level_filters::LevelFilter;
39 : use tracing::{Dispatch, Subscriber};
40 : use tracing_subscriber::Layer;
41 : use tracing_subscriber::layer::SubscriberExt;
42 : use tracing_subscriber::registry::LookupSpan;
43 :
44 : pub type Provider = SdkTracerProvider;
45 :
46 : /// Set up OpenTelemetry exporter, using configuration from environment variables.
47 : ///
48 : /// `service_name` is set as the OpenTelemetry 'service.name' resource (see
49 : /// <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/README.md#service>)
50 : ///
51 : /// We try to follow the conventions for the environment variables specified in
52 : /// <https://opentelemetry.io/docs/reference/specification/sdk-environment-variables/>
53 : ///
54 : /// However, we only support a subset of those options:
55 : ///
56 : /// - OTEL_SDK_DISABLED is supported. The default is "false", meaning tracing
57 : /// is enabled by default. Set it to "true" to disable.
58 : ///
59 : /// - We use the OTLP exporter, with HTTP protocol. Most of the OTEL_EXPORTER_OTLP_*
60 : /// settings specified in
61 : /// <https://opentelemetry.io/docs/reference/specification/protocol/exporter/>
62 : /// are supported, as they are handled by the `opentelemetry-otlp` crate.
63 : /// Settings related to other exporters have no effect.
64 : ///
65 : /// - Some other settings are supported by the `opentelemetry` crate.
66 : ///
67 : /// If you need some other setting, please test if it works first. And perhaps
68 : /// add a comment in the list above to save the effort of testing for the next
69 : /// person.
70 0 : pub fn init_tracing(service_name: &str, export_config: ExportConfig) -> Option<Provider> {
71 0 : if std::env::var("OTEL_SDK_DISABLED") == Ok("true".to_string()) {
72 0 : return None;
73 0 : };
74 0 : Some(init_tracing_internal(
75 0 : service_name.to_string(),
76 0 : export_config,
77 0 : ))
78 0 : }
79 :
80 0 : pub fn layer<S>(p: &Provider) -> impl Layer<S>
81 0 : where
82 0 : S: Subscriber + for<'span> LookupSpan<'span>,
83 : {
84 0 : tracing_opentelemetry::layer().with_tracer(p.tracer("global"))
85 0 : }
86 :
87 0 : fn init_tracing_internal(service_name: String, export_config: ExportConfig) -> Provider {
88 : // Sets up exporter from the provided [`ExportConfig`] parameter.
89 : // If the endpoint is not specified, it is loaded from the
90 : // OTEL_EXPORTER_OTLP_ENDPOINT environment variable.
91 0 : let exporter = opentelemetry_otlp::SpanExporter::builder()
92 0 : .with_http()
93 0 : .with_export_config(export_config)
94 0 : .build()
95 0 : .expect("could not initialize opentelemetry exporter");
96 :
97 : // TODO: opentelemetry::global::set_error_handler() with custom handler that
98 : // bypasses default tracing layers, but logs regular looking log
99 : // messages.
100 :
101 : // Propagate trace information in the standard W3C TraceContext format.
102 0 : opentelemetry::global::set_text_map_propagator(
103 0 : opentelemetry_sdk::propagation::TraceContextPropagator::new(),
104 : );
105 :
106 0 : Provider::builder()
107 0 : .with_batch_exporter(exporter)
108 0 : .with_resource(
109 0 : opentelemetry_sdk::Resource::builder()
110 0 : .with_service_name(service_name)
111 0 : .build(),
112 : )
113 0 : .build()
114 0 : }
115 :
116 : pub enum OtelEnablement {
117 : Disabled,
118 : Enabled {
119 : service_name: String,
120 : export_config: ExportConfig,
121 : },
122 : }
123 :
124 : pub struct OtelGuard {
125 : provider: Provider,
126 : pub dispatch: Dispatch,
127 : }
128 :
129 : impl Drop for OtelGuard {
130 0 : fn drop(&mut self) {
131 0 : _ = self.provider.shutdown();
132 0 : }
133 : }
134 :
135 : /// Initializes OTEL infrastructure for performance tracing according to the provided configuration
136 : ///
137 : /// Performance tracing is handled by a different [`tracing::Subscriber`]. This functions returns
138 : /// an [`OtelGuard`] containing a [`tracing::Dispatch`] associated with a newly created subscriber.
139 : /// Applications should use this dispatch for their performance traces.
140 : ///
141 : /// The lifetime of the guard should match taht of the application. On drop, it tears down the
142 : /// OTEL infra.
143 0 : pub fn init_performance_tracing(otel_enablement: OtelEnablement) -> Option<OtelGuard> {
144 0 : match otel_enablement {
145 0 : OtelEnablement::Disabled => None,
146 : OtelEnablement::Enabled {
147 0 : service_name,
148 0 : export_config,
149 : } => {
150 0 : let provider = init_tracing(&service_name, export_config)?;
151 :
152 0 : let otel_layer = layer(&provider).with_filter(LevelFilter::INFO);
153 0 : let otel_subscriber = tracing_subscriber::registry().with(otel_layer);
154 0 : let dispatch = Dispatch::new(otel_subscriber);
155 :
156 0 : Some(OtelGuard { dispatch, provider })
157 : }
158 : }
159 0 : }
|