@@ -12,7 +12,7 @@ use opentelemetry::{global, trace::TracerProvider, KeyValue, Value};
1212use opentelemetry_otlp:: { SpanExporter , WithExportConfig } ;
1313use opentelemetry_sdk:: {
1414 propagation:: TraceContextPropagator ,
15- trace:: { SdkTracer , SdkTracerProvider } ,
15+ trace:: { Sampler , SdkTracer , SdkTracerProvider } ,
1616 Resource ,
1717} ;
1818use opentelemetry_semantic_conventions:: { attribute:: SERVICE_VERSION , SCHEMA_URL } ;
@@ -29,36 +29,92 @@ const HTTP_TRACE_ENDPOINT: &str = "/v1/traces";
2929///
3030/// This layer can be added to a [`tracing_subscriber::Registry`] to enable `OpenTelemetry` tracing
3131/// with OTLP export to an url.
32- pub fn span_layer < S > (
33- service_name : impl Into < Value > ,
34- endpoint : & Url ,
35- protocol : OtlpProtocol ,
36- ) -> eyre:: Result < OpenTelemetryLayer < S , SdkTracer > >
32+ pub fn span_layer < S > ( otlp_config : OtlpConfig ) -> eyre:: Result < OpenTelemetryLayer < S , SdkTracer > >
3733where
3834 for < ' span > S : Subscriber + LookupSpan < ' span > ,
3935{
4036 global:: set_text_map_propagator ( TraceContextPropagator :: new ( ) ) ;
4137
42- let resource = build_resource ( service_name) ;
38+ let resource = build_resource ( otlp_config . service_name . clone ( ) ) ;
4339
4440 let span_builder = SpanExporter :: builder ( ) ;
4541
46- let span_exporter = match protocol {
47- OtlpProtocol :: Http => span_builder. with_http ( ) . with_endpoint ( endpoint. as_str ( ) ) . build ( ) ?,
48- OtlpProtocol :: Grpc => span_builder. with_tonic ( ) . with_endpoint ( endpoint. as_str ( ) ) . build ( ) ?,
42+ let span_exporter = match otlp_config. protocol {
43+ OtlpProtocol :: Http => {
44+ span_builder. with_http ( ) . with_endpoint ( otlp_config. endpoint . as_str ( ) ) . build ( ) ?
45+ }
46+ OtlpProtocol :: Grpc => {
47+ span_builder. with_tonic ( ) . with_endpoint ( otlp_config. endpoint . as_str ( ) ) . build ( ) ?
48+ }
4949 } ;
5050
51+ let sampler = build_sampler ( otlp_config. sample_ratio ) ?;
52+
5153 let tracer_provider = SdkTracerProvider :: builder ( )
5254 . with_resource ( resource)
55+ . with_sampler ( sampler)
5356 . with_batch_exporter ( span_exporter)
5457 . build ( ) ;
5558
5659 global:: set_tracer_provider ( tracer_provider. clone ( ) ) ;
5760
58- let tracer = tracer_provider. tracer ( "reth" ) ;
61+ let tracer = tracer_provider. tracer ( otlp_config . service_name ) ;
5962 Ok ( tracing_opentelemetry:: layer ( ) . with_tracer ( tracer) )
6063}
6164
65+ /// Configuration for OTLP trace export.
66+ #[ derive( Debug , Clone ) ]
67+ pub struct OtlpConfig {
68+ /// Service name for trace identification
69+ service_name : String ,
70+ /// Otlp endpoint URL
71+ endpoint : Url ,
72+ /// Transport protocol, HTTP or gRPC
73+ protocol : OtlpProtocol ,
74+ /// Optional sampling ratio, from 0.0 to 1.0
75+ sample_ratio : Option < f64 > ,
76+ }
77+
78+ impl OtlpConfig {
79+ /// Creates a new OTLP configuration.
80+ pub fn new (
81+ service_name : impl Into < String > ,
82+ endpoint : Url ,
83+ protocol : OtlpProtocol ,
84+ sample_ratio : Option < f64 > ,
85+ ) -> eyre:: Result < Self > {
86+ if let Some ( ratio) = sample_ratio {
87+ ensure ! (
88+ ( 0.0 ..=1.0 ) . contains( & ratio) ,
89+ "Sample ratio must be between 0.0 and 1.0, got: {}" ,
90+ ratio
91+ ) ;
92+ }
93+
94+ Ok ( Self { service_name : service_name. into ( ) , endpoint, protocol, sample_ratio } )
95+ }
96+
97+ /// Returns the service name.
98+ pub fn service_name ( & self ) -> & str {
99+ & self . service_name
100+ }
101+
102+ /// Returns the OTLP endpoint URL.
103+ pub const fn endpoint ( & self ) -> & Url {
104+ & self . endpoint
105+ }
106+
107+ /// Returns the transport protocol.
108+ pub const fn protocol ( & self ) -> OtlpProtocol {
109+ self . protocol
110+ }
111+
112+ /// Returns the sampling ratio.
113+ pub const fn sample_ratio ( & self ) -> Option < f64 > {
114+ self . sample_ratio
115+ }
116+ }
117+
62118// Builds OTLP resource with service information.
63119fn build_resource ( service_name : impl Into < Value > ) -> Resource {
64120 Resource :: builder ( )
@@ -67,6 +123,18 @@ fn build_resource(service_name: impl Into<Value>) -> Resource {
67123 . build ( )
68124}
69125
126+ /// Builds the appropriate sampler based on the sample ratio.
127+ fn build_sampler ( sample_ratio : Option < f64 > ) -> eyre:: Result < Sampler > {
128+ match sample_ratio {
129+ // Default behavior: sample all traces
130+ None | Some ( 1.0 ) => Ok ( Sampler :: ParentBased ( Box :: new ( Sampler :: AlwaysOn ) ) ) ,
131+ // Don't sample anything
132+ Some ( 0.0 ) => Ok ( Sampler :: ParentBased ( Box :: new ( Sampler :: AlwaysOff ) ) ) ,
133+ // Sample based on trace ID ratio
134+ Some ( ratio) => Ok ( Sampler :: ParentBased ( Box :: new ( Sampler :: TraceIdRatioBased ( ratio) ) ) ) ,
135+ }
136+ }
137+
70138/// OTLP transport protocol type
71139#[ derive( Debug , Clone , Copy , PartialEq , Eq , ValueEnum ) ]
72140pub enum OtlpProtocol {
0 commit comments