Skip to main content

reth_tracing_otlp/
lib.rs

1//! With the `otlp` feature, provides tracing layers for `OpenTelemetry` that export spans, logs,
2//! and metrics to an OTLP endpoint.
3//!
4//! This module simplifies the integration of `OpenTelemetry` with OTLP export in Rust
5//! applications. It allows for easily capturing and exporting distributed traces, logs,
6//! and metrics to compatible backends like Jaeger, Zipkin, or any other
7//! OpenTelemetry-compatible system.
8
9use clap::ValueEnum;
10use eyre::ensure;
11use std::path::PathBuf;
12use url::Url;
13
14#[cfg(feature = "otlp")]
15mod file;
16#[cfg(feature = "otlp")]
17mod otlp;
18#[cfg(feature = "otlp")]
19pub use otlp::*;
20
21// Otlp http endpoint is expected to end with this path.
22// See also <https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_traces_endpoint>.
23const HTTP_TRACE_ENDPOINT: &str = "/v1/traces";
24const HTTP_LOGS_ENDPOINT: &str = "/v1/logs";
25
26/// OTLP transport protocol type
27#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
28pub enum OtlpProtocol {
29    /// HTTP/Protobuf transport, port 4318, requires `/v1/traces` path
30    Http,
31    /// gRPC transport, port 4317
32    Grpc,
33}
34
35impl OtlpProtocol {
36    /// Validate and correct the URL to match protocol requirements for traces.
37    ///
38    /// For HTTP: Ensures the path ends with `/v1/traces`, appending it if necessary.
39    /// For gRPC: Ensures the path does NOT include `/v1/traces`.
40    /// For file URLs: Requires a local absolute path without a query or fragment.
41    /// The protocol is ignored for file export.
42    pub fn validate_endpoint(&self, url: &mut Url) -> eyre::Result<()> {
43        if url.scheme() == "file" {
44            trace_file_path(url)?;
45            return Ok(());
46        }
47        self.validate_endpoint_with_path(url, HTTP_TRACE_ENDPOINT)
48    }
49
50    /// Validate and correct the URL to match protocol requirements for logs.
51    ///
52    /// For HTTP: Ensures the path ends with `/v1/logs`, appending it if necessary.
53    /// For gRPC: Ensures the path does NOT include `/v1/logs`.
54    pub fn validate_logs_endpoint(&self, url: &mut Url) -> eyre::Result<()> {
55        ensure!(url.scheme() != "file", "OTLP logs do not support file endpoints");
56        self.validate_endpoint_with_path(url, HTTP_LOGS_ENDPOINT)
57    }
58
59    fn validate_endpoint_with_path(&self, url: &mut Url, http_path: &str) -> eyre::Result<()> {
60        match self {
61            Self::Http => {
62                if !url.path().ends_with(http_path) {
63                    let path = url.path().trim_end_matches('/');
64                    url.set_path(&format!("{}{}", path, http_path));
65                }
66            }
67            Self::Grpc => {
68                ensure!(
69                    !url.path().ends_with(http_path),
70                    "OTLP gRPC endpoint should not include {} path, got: {}",
71                    http_path,
72                    url
73                );
74            }
75        }
76        Ok(())
77    }
78}
79
80/// Validates a local trace file URL without applying network endpoint path rules.
81fn trace_file_path(url: &Url) -> eyre::Result<PathBuf> {
82    ensure!(url.scheme() == "file", "OTLP trace file endpoint must use the file scheme");
83    ensure!(
84        url.host_str().is_none() && url.query().is_none() && url.fragment().is_none(),
85        "OTLP trace file endpoint must be local and have no query or fragment"
86    );
87    let path = url.to_file_path().map_err(|()| eyre::eyre!("Invalid OTLP trace file path"))?;
88    ensure!(
89        path.is_absolute() && path.file_name().is_some(),
90        "OTLP trace file path must be absolute and name a file"
91    );
92    Ok(path)
93}