1212// See the License for the specific language governing permissions and
1313// limitations under the License.
1414
15+ use std:: str:: FromStr ;
1516use std:: sync:: Arc ;
1617use std:: { env, fmt} ;
1718
1819use anyhow:: Context ;
1920use opentelemetry:: trace:: TracerProvider ;
2021use opentelemetry:: { KeyValue , global} ;
2122use opentelemetry_appender_tracing:: layer:: OpenTelemetryTracingBridge ;
22- use opentelemetry_otlp:: { Protocol as OtlpWireProtocol , WithExportConfig } ;
23+ use opentelemetry_otlp:: {
24+ LogExporter , Protocol as OtlpWireProtocol , SpanExporter , WithExportConfig ,
25+ } ;
2326use opentelemetry_sdk:: logs:: SdkLoggerProvider ;
2427use opentelemetry_sdk:: propagation:: TraceContextPropagator ;
2528use opentelemetry_sdk:: trace:: { BatchConfigBuilder , SdkTracerProvider } ;
2629use opentelemetry_sdk:: { Resource , trace} ;
27- use quickwit_common:: { get_bool_from_env, get_from_env_opt} ;
30+ use quickwit_common:: { get_bool_from_env, get_from_env , get_from_env_opt} ;
2831use quickwit_serve:: { BuildInfo , EnvFilterReloadFn } ;
2932use time:: format_description:: BorrowedFormatItem ;
3033use tracing:: { Event , Level , Subscriber } ;
@@ -48,44 +51,57 @@ enum OtlpProtocol {
4851 HttpJson ,
4952}
5053
51- fn parse_otlp_protocol ( protocol_str : & str ) -> anyhow:: Result < OtlpProtocol > {
52- const OTLP_PROTOCOL_GRPC : & str = "grpc" ;
53- const OTLP_PROTOCOL_HTTP_PROTOBUF : & str = "http/protobuf" ;
54- const OTLP_PROTOCOL_HTTP_JSON : & str = "http/json" ;
55-
56- match protocol_str {
57- OTLP_PROTOCOL_GRPC => Ok ( OtlpProtocol :: Grpc ) ,
58- OTLP_PROTOCOL_HTTP_PROTOBUF => Ok ( OtlpProtocol :: HttpProtobuf ) ,
59- OTLP_PROTOCOL_HTTP_JSON => Ok ( OtlpProtocol :: HttpJson ) ,
60- other => anyhow:: bail!(
61- "unsupported OTLP protocol `{other}`, supported values are `{OTLP_PROTOCOL_GRPC}`, \
62- `{OTLP_PROTOCOL_HTTP_PROTOBUF}` and `{OTLP_PROTOCOL_HTTP_JSON}`"
63- ) ,
64- }
65- }
66-
67- /// Resolves the OTLP protocol from candidates in priority order, defaulting to gRPC.
68- fn resolve_otlp_protocol ( candidates : & [ Option < & str > ] ) -> anyhow:: Result < OtlpProtocol > {
69- match candidates. iter ( ) . flatten ( ) . next ( ) {
70- Some ( protocol_str) => parse_otlp_protocol ( protocol_str) ,
71- None => Ok ( OtlpProtocol :: Grpc ) ,
54+ impl OtlpProtocol {
55+ fn log_exporter ( & self ) -> anyhow:: Result < LogExporter > {
56+ match self {
57+ OtlpProtocol :: Grpc => LogExporter :: builder ( ) . with_tonic ( ) . build ( ) ,
58+ OtlpProtocol :: HttpProtobuf => LogExporter :: builder ( )
59+ . with_http ( )
60+ . with_protocol ( OtlpWireProtocol :: HttpBinary )
61+ . build ( ) ,
62+ OtlpProtocol :: HttpJson => LogExporter :: builder ( )
63+ . with_http ( )
64+ . with_protocol ( OtlpWireProtocol :: HttpJson )
65+ . build ( ) ,
66+ }
67+ . context ( "failed to initialize OTLP logs exporter" )
7268 }
73- }
7469
75- macro_rules! build_otlp_exporter {
76- ( $builder: expr, $protocol: expr) => {
77- match $protocol {
78- OtlpProtocol :: Grpc => $builder. with_tonic( ) . build( ) ,
79- OtlpProtocol :: HttpProtobuf => $builder
70+ fn span_exporter ( & self ) -> anyhow:: Result < SpanExporter > {
71+ match self {
72+ OtlpProtocol :: Grpc => SpanExporter :: builder ( ) . with_tonic ( ) . build ( ) ,
73+ OtlpProtocol :: HttpProtobuf => SpanExporter :: builder ( )
8074 . with_http ( )
8175 . with_protocol ( OtlpWireProtocol :: HttpBinary )
8276 . build ( ) ,
83- OtlpProtocol :: HttpJson => $ builder
77+ OtlpProtocol :: HttpJson => SpanExporter :: builder ( )
8478 . with_http ( )
8579 . with_protocol ( OtlpWireProtocol :: HttpJson )
8680 . build ( ) ,
8781 }
88- } ;
82+ . context ( "failed to initialize OTLP traces exporter" )
83+ }
84+ }
85+
86+ impl FromStr for OtlpProtocol {
87+ type Err = anyhow:: Error ;
88+
89+ fn from_str ( protocol_str : & str ) -> anyhow:: Result < Self > {
90+ const OTLP_PROTOCOL_GRPC : & str = "grpc" ;
91+ const OTLP_PROTOCOL_HTTP_PROTOBUF : & str = "http/protobuf" ;
92+ const OTLP_PROTOCOL_HTTP_JSON : & str = "http/json" ;
93+
94+ match protocol_str {
95+ OTLP_PROTOCOL_GRPC => Ok ( OtlpProtocol :: Grpc ) ,
96+ OTLP_PROTOCOL_HTTP_PROTOBUF => Ok ( OtlpProtocol :: HttpProtobuf ) ,
97+ OTLP_PROTOCOL_HTTP_JSON => Ok ( OtlpProtocol :: HttpJson ) ,
98+ other => anyhow:: bail!(
99+ "unsupported OTLP protocol `{other}`, supported values are \
100+ `{OTLP_PROTOCOL_GRPC}`, `{OTLP_PROTOCOL_HTTP_PROTOBUF}` and \
101+ `{OTLP_PROTOCOL_HTTP_JSON}`"
102+ ) ,
103+ }
104+ }
89105}
90106
91107#[ cfg( feature = "tokio-console" ) ]
@@ -147,16 +163,19 @@ pub fn setup_logging_and_tracing(
147163 // Note on disabling ANSI characters: setting the ansi boolean on event format is insufficient.
148164 // It is thus set on layers, see https://github.com/tokio-rs/tracing/issues/1817
149165 let provider_opt = if get_bool_from_env ( QW_ENABLE_OPENTELEMETRY_OTLP_EXPORTER_ENV_KEY , false ) {
150- let global_protocol = env:: var ( "OTEL_EXPORTER_OTLP_PROTOCOL" ) . ok ( ) ;
151- let traces_protocol = resolve_otlp_protocol ( & [
152- env:: var ( "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL" )
153- . ok ( )
154- . as_deref ( ) ,
155- global_protocol. as_deref ( ) ,
156- ] ) ?;
157- let span_exporter =
158- build_otlp_exporter ! ( opentelemetry_otlp:: SpanExporter :: builder( ) , traces_protocol)
159- . context ( "failed to initialize OTLP traces exporter" ) ?;
166+ let global_protocol_str =
167+ get_from_env ( "OTEL_EXPORTER_OTLP_PROTOCOL" , "grpc" . to_string ( ) , false ) ;
168+ let global_protocol = OtlpProtocol :: from_str ( & global_protocol_str) ?;
169+
170+ let traces_protocol_opt =
171+ get_from_env_opt :: < String > ( "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL" , false ) ;
172+ let traces_protocol = traces_protocol_opt
173+ . as_deref ( )
174+ . map ( OtlpProtocol :: from_str)
175+ . transpose ( ) ?
176+ . unwrap_or ( global_protocol) ;
177+
178+ let span_exporter = traces_protocol. span_exporter ( ) ?;
160179 let span_processor = trace:: BatchSpanProcessor :: builder ( span_exporter)
161180 . with_batch_config (
162181 BatchConfigBuilder :: default ( )
@@ -172,17 +191,17 @@ pub fn setup_logging_and_tracing(
172191 . with_attribute ( KeyValue :: new ( "service.version" , build_info. version . clone ( ) ) )
173192 . build ( ) ;
174193
175- let logs_protocol = resolve_otlp_protocol ( & [
176- env :: var ( "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL" ) . ok ( ) . as_deref ( ) ,
177- global_protocol . as_deref ( ) ,
178- ] ) ? ;
179- let logs_exporter =
180- build_otlp_exporter ! ( opentelemetry_otlp :: LogExporter :: builder ( ) , logs_protocol )
181- . context ( "failed to initialize OTLP logs exporter" ) ? ;
182-
194+ let logs_protocol_opt =
195+ get_from_env_opt :: < String > ( "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL" , false ) ;
196+ let logs_protocol = logs_protocol_opt
197+ . as_deref ( )
198+ . map ( OtlpProtocol :: from_str )
199+ . transpose ( ) ?
200+ . unwrap_or ( global_protocol ) ;
201+ let log_exporter = logs_protocol . log_exporter ( ) ? ;
183202 let logger_provider = SdkLoggerProvider :: builder ( )
184203 . with_resource ( resource. clone ( ) )
185- . with_batch_exporter ( logs_exporter )
204+ . with_batch_exporter ( log_exporter )
186205 . build ( ) ;
187206
188207 let tracing_provider = opentelemetry_sdk:: trace:: SdkTracerProvider :: builder ( )
@@ -489,39 +508,17 @@ mod tests {
489508 use super :: * ;
490509
491510 #[ test]
492- fn test_resolve_otlp_protocol_defaults_to_grpc ( ) {
493- let protocol = resolve_otlp_protocol ( & [ None , None ] ) . unwrap ( ) ;
494- assert_eq ! ( protocol, OtlpProtocol :: Grpc ) ;
495- }
496-
497- #[ test]
498- fn test_resolve_otlp_protocol_first_candidate_takes_priority ( ) {
499- let protocol = resolve_otlp_protocol ( & [ Some ( "http/protobuf" ) , Some ( "grpc" ) ] ) . unwrap ( ) ;
500- assert_eq ! ( protocol, OtlpProtocol :: HttpProtobuf ) ;
501- }
502-
503- #[ test]
504- fn test_resolve_otlp_protocol_falls_back_to_later_candidate ( ) {
505- let protocol = resolve_otlp_protocol ( & [ None , Some ( "http/protobuf" ) ] ) . unwrap ( ) ;
506- assert_eq ! ( protocol, OtlpProtocol :: HttpProtobuf ) ;
507- }
508-
509- #[ test]
510- fn test_resolve_otlp_protocol_grpc_explicit ( ) {
511- let protocol = resolve_otlp_protocol ( & [ Some ( "grpc" ) ] ) . unwrap ( ) ;
512- assert_eq ! ( protocol, OtlpProtocol :: Grpc ) ;
513- }
514-
515- #[ test]
516- fn test_resolve_otlp_protocol_http_json_explicit ( ) {
517- let protocol = resolve_otlp_protocol ( & [ Some ( "http/json" ) ] ) . unwrap ( ) ;
518- assert_eq ! ( protocol, OtlpProtocol :: HttpJson ) ;
519- }
520-
521- #[ test]
522- fn test_resolve_otlp_protocol_rejects_unsupported_value ( ) {
523- let result = resolve_otlp_protocol ( & [ Some ( "http/xml" ) ] ) ;
524- assert ! ( result. is_err( ) ) ;
511+ fn test_otlp_protocol_from_str ( ) {
512+ assert_eq ! ( OtlpProtocol :: from_str( "grpc" ) . unwrap( ) , OtlpProtocol :: Grpc ) ;
513+ assert_eq ! (
514+ OtlpProtocol :: from_str( "http/protobuf" ) . unwrap( ) ,
515+ OtlpProtocol :: HttpProtobuf
516+ ) ;
517+ assert_eq ! (
518+ OtlpProtocol :: from_str( "http/json" ) . unwrap( ) ,
519+ OtlpProtocol :: HttpJson
520+ ) ;
521+ assert ! ( OtlpProtocol :: from_str( "http/xml" ) . is_err( ) ) ;
525522 }
526523
527524 /// A shared buffer writer for capturing log output in tests.
0 commit comments