LCOV - code coverage report
Current view: top level - libs/tracing-utils/src - lib.rs (source / functions) Coverage Total Hit
Test: 5fe7fa8d483b39476409aee736d6d5e32728bfac.info Lines: 0.0 % 109 0
Test Date: 2025-03-12 16:10:49 Functions: 0.0 % 11 0

            Line data    Source code
       1              : //! Helper functions to set up OpenTelemetry tracing.
       2              : //!
       3              : //! This comes in two variants, depending on whether you have a Tokio runtime available.
       4              : //! If you do, call `init_tracing()`. It sets up the trace processor and exporter to use
       5              : //! the current tokio runtime. If you don't have a runtime available, or you don't want
       6              : //! to share the runtime with the tracing tasks, call `init_tracing_without_runtime()`
       7              : //! instead. It sets up a dedicated single-threaded Tokio runtime for the tracing tasks.
       8              : //!
       9              : //! Example:
      10              : //!
      11              : //! ```rust,no_run
      12              : //! use tracing_subscriber::prelude::*;
      13              : //!
      14              : //! #[tokio::main]
      15              : //! async fn main() {
      16              : //!     // Set up logging to stderr
      17              : //!     let env_filter = tracing_subscriber::EnvFilter::try_from_default_env()
      18              : //!         .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info"));
      19              : //!     let fmt_layer = tracing_subscriber::fmt::layer()
      20              : //!         .with_target(false)
      21              : //!         .with_writer(std::io::stderr);
      22              : //!
      23              : //!     // Initialize OpenTelemetry. Exports tracing spans as OpenTelemetry traces
      24              : //!     let otlp_layer = tracing_utils::init_tracing("my_application", tracing_utils::ExportConfig::default()).await;
      25              : //!
      26              : //!     // Put it all together
      27              : //!     tracing_subscriber::registry()
      28              : //!         .with(env_filter)
      29              : //!         .with(otlp_layer)
      30              : //!         .with(fmt_layer)
      31              : //!         .init();
      32              : //! }
      33              : //! ```
      34              : #![deny(unsafe_code)]
      35              : #![deny(clippy::undocumented_unsafe_blocks)]
      36              : 
      37              : pub mod http;
      38              : 
      39              : use opentelemetry::KeyValue;
      40              : use opentelemetry::trace::TracerProvider;
      41              : use opentelemetry_otlp::WithExportConfig;
      42              : pub use opentelemetry_otlp::{ExportConfig, Protocol};
      43              : use tracing::level_filters::LevelFilter;
      44              : use tracing::{Dispatch, Subscriber};
      45              : use tracing_subscriber::Layer;
      46              : use tracing_subscriber::layer::SubscriberExt;
      47              : use tracing_subscriber::registry::LookupSpan;
      48              : 
      49              : /// Set up OpenTelemetry exporter, using configuration from environment variables.
      50              : ///
      51              : /// `service_name` is set as the OpenTelemetry 'service.name' resource (see
      52              : /// <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/README.md#service>)
      53              : ///
      54              : /// We try to follow the conventions for the environment variables specified in
      55              : /// <https://opentelemetry.io/docs/reference/specification/sdk-environment-variables/>
      56              : ///
      57              : /// However, we only support a subset of those options:
      58              : ///
      59              : /// - OTEL_SDK_DISABLED is supported. The default is "false", meaning tracing
      60              : ///   is enabled by default. Set it to "true" to disable.
      61              : ///
      62              : /// - We use the OTLP exporter, with HTTP protocol. Most of the OTEL_EXPORTER_OTLP_*
      63              : ///   settings specified in
      64              : ///   <https://opentelemetry.io/docs/reference/specification/protocol/exporter/>
      65              : ///   are supported, as they are handled by the `opentelemetry-otlp` crate.
      66              : ///   Settings related to other exporters have no effect.
      67              : ///
      68              : /// - Some other settings are supported by the `opentelemetry` crate.
      69              : ///
      70              : /// If you need some other setting, please test if it works first. And perhaps
      71              : /// add a comment in the list above to save the effort of testing for the next
      72              : /// person.
      73              : ///
      74              : /// This doesn't block, but is marked as 'async' to hint that this must be called in
      75              : /// asynchronous execution context.
      76            0 : pub async fn init_tracing<S>(
      77            0 :     service_name: &str,
      78            0 :     export_config: ExportConfig,
      79            0 : ) -> Option<impl Layer<S>>
      80            0 : where
      81            0 :     S: Subscriber + for<'span> LookupSpan<'span>,
      82            0 : {
      83            0 :     if std::env::var("OTEL_SDK_DISABLED") == Ok("true".to_string()) {
      84            0 :         return None;
      85            0 :     };
      86            0 :     Some(init_tracing_internal(
      87            0 :         service_name.to_string(),
      88            0 :         export_config,
      89            0 :     ))
      90            0 : }
      91              : 
      92              : /// Like `init_tracing`, but creates a separate tokio Runtime for the tracing
      93              : /// tasks.
      94            0 : pub fn init_tracing_without_runtime<S>(
      95            0 :     service_name: &str,
      96            0 :     export_config: ExportConfig,
      97            0 : ) -> Option<impl Layer<S>>
      98            0 : where
      99            0 :     S: Subscriber + for<'span> LookupSpan<'span>,
     100            0 : {
     101            0 :     if std::env::var("OTEL_SDK_DISABLED") == Ok("true".to_string()) {
     102            0 :         return None;
     103            0 :     };
     104            0 : 
     105            0 :     // The opentelemetry batch processor and the OTLP exporter needs a Tokio
     106            0 :     // runtime. Create a dedicated runtime for them. One thread should be
     107            0 :     // enough.
     108            0 :     //
     109            0 :     // (Alternatively, instead of batching, we could use the "simple
     110            0 :     // processor", which doesn't need Tokio, and use "reqwest-blocking"
     111            0 :     // feature for the OTLP exporter, which also doesn't need Tokio.  However,
     112            0 :     // batching is considered best practice, and also I have the feeling that
     113            0 :     // the non-Tokio codepaths in the opentelemetry crate are less used and
     114            0 :     // might be more buggy, so better to stay on the well-beaten path.)
     115            0 :     //
     116            0 :     // We leak the runtime so that it keeps running after we exit the
     117            0 :     // function.
     118            0 :     let runtime = Box::leak(Box::new(
     119            0 :         tokio::runtime::Builder::new_multi_thread()
     120            0 :             .enable_all()
     121            0 :             .thread_name("otlp runtime thread")
     122            0 :             .worker_threads(1)
     123            0 :             .build()
     124            0 :             .unwrap(),
     125            0 :     ));
     126            0 :     let _guard = runtime.enter();
     127            0 : 
     128            0 :     Some(init_tracing_internal(
     129            0 :         service_name.to_string(),
     130            0 :         export_config,
     131            0 :     ))
     132            0 : }
     133              : 
     134            0 : fn init_tracing_internal<S>(service_name: String, export_config: ExportConfig) -> impl Layer<S>
     135            0 : where
     136            0 :     S: Subscriber + for<'span> LookupSpan<'span>,
     137            0 : {
     138            0 :     // Sets up exporter from the provided [`ExportConfig`] parameter.
     139            0 :     // If the endpoint is not specified, it is loaded from the
     140            0 :     // OTEL_EXPORTER_OTLP_ENDPOINT environment variable.
     141            0 :     let exporter = opentelemetry_otlp::SpanExporter::builder()
     142            0 :         .with_http()
     143            0 :         .with_export_config(export_config)
     144            0 :         .build()
     145            0 :         .expect("could not initialize opentelemetry exporter");
     146            0 : 
     147            0 :     // TODO: opentelemetry::global::set_error_handler() with custom handler that
     148            0 :     //       bypasses default tracing layers, but logs regular looking log
     149            0 :     //       messages.
     150            0 : 
     151            0 :     // Propagate trace information in the standard W3C TraceContext format.
     152            0 :     opentelemetry::global::set_text_map_propagator(
     153            0 :         opentelemetry_sdk::propagation::TraceContextPropagator::new(),
     154            0 :     );
     155            0 : 
     156            0 :     let tracer = opentelemetry_sdk::trace::TracerProvider::builder()
     157            0 :         .with_batch_exporter(exporter, opentelemetry_sdk::runtime::Tokio)
     158            0 :         .with_resource(opentelemetry_sdk::Resource::new(vec![KeyValue::new(
     159            0 :             opentelemetry_semantic_conventions::resource::SERVICE_NAME,
     160            0 :             service_name,
     161            0 :         )]))
     162            0 :         .build()
     163            0 :         .tracer("global");
     164            0 : 
     165            0 :     tracing_opentelemetry::layer().with_tracer(tracer)
     166            0 : }
     167              : 
     168              : // Shutdown trace pipeline gracefully, so that it has a chance to send any
     169              : // pending traces before we exit.
     170            0 : pub fn shutdown_tracing() {
     171            0 :     opentelemetry::global::shutdown_tracer_provider();
     172            0 : }
     173              : 
     174              : pub enum OtelEnablement {
     175              :     Disabled,
     176              :     Enabled {
     177              :         service_name: String,
     178              :         export_config: ExportConfig,
     179              :         runtime: &'static tokio::runtime::Runtime,
     180              :     },
     181              : }
     182              : 
     183              : pub struct OtelGuard {
     184              :     pub dispatch: Dispatch,
     185              : }
     186              : 
     187              : impl Drop for OtelGuard {
     188            0 :     fn drop(&mut self) {
     189            0 :         shutdown_tracing();
     190            0 :     }
     191              : }
     192              : 
     193              : /// Initializes OTEL infrastructure for performance tracing according to the provided configuration
     194              : ///
     195              : /// Performance tracing is handled by a different [`tracing::Subscriber`]. This functions returns
     196              : /// an [`OtelGuard`] containing a [`tracing::Dispatch`] associated with a newly created subscriber.
     197              : /// Applications should use this dispatch for their performance traces.
     198              : ///
     199              : /// The lifetime of the guard should match taht of the application. On drop, it tears down the
     200              : /// OTEL infra.
     201            0 : pub fn init_performance_tracing(otel_enablement: OtelEnablement) -> Option<OtelGuard> {
     202            0 :     let otel_subscriber = match otel_enablement {
     203            0 :         OtelEnablement::Disabled => None,
     204              :         OtelEnablement::Enabled {
     205            0 :             service_name,
     206            0 :             export_config,
     207            0 :             runtime,
     208            0 :         } => {
     209            0 :             let otel_layer = runtime
     210            0 :                 .block_on(init_tracing(&service_name, export_config))
     211            0 :                 .with_filter(LevelFilter::INFO);
     212            0 :             let otel_subscriber = tracing_subscriber::registry().with(otel_layer);
     213            0 :             let otel_dispatch = Dispatch::new(otel_subscriber);
     214            0 : 
     215            0 :             Some(otel_dispatch)
     216              :         }
     217              :     };
     218              : 
     219            0 :     otel_subscriber.map(|dispatch| OtelGuard { dispatch })
     220            0 : }
        

Generated by: LCOV version 2.1-beta