Skip to content

Commit cd77bf2

Browse files
committed
Support configurable OTLP exporter protocol for traces and logs
1 parent 1769d69 commit cd77bf2

4 files changed

Lines changed: 127 additions & 9 deletions

File tree

LICENSE-3rdparty.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ concurrent-queue,https://github.com/smol-rs/concurrent-queue,Apache-2.0 OR MIT,"
154154
console,https://github.com/console-rs/console,MIT,The console Authors
155155
console-api,https://github.com/tokio-rs/console,MIT,"Eliza Weisman <eliza@buoyant.io>, Tokio Contributors <team@tokio.rs>"
156156
console-subscriber,https://github.com/tokio-rs/console,MIT,"Eliza Weisman <eliza@buoyant.io>, Tokio Contributors <team@tokio.rs>"
157+
const-hex,https://github.com/danipopes/const-hex,MIT OR Apache-2.0,DaniPopes <57450786+DaniPopes@users.noreply.github.com>
157158
const-oid,https://github.com/RustCrypto/formats/tree/master/const-oid,Apache-2.0 OR MIT,RustCrypto Developers
158159
const-random,https://github.com/tkaitchuck/constrandom,MIT OR Apache-2.0,Tom Kaitchuck <Tom.Kaitchuck@gmail.com>
159160
const-random-macro,https://github.com/tkaitchuck/constrandom,MIT OR Apache-2.0,Tom Kaitchuck <Tom.Kaitchuck@gmail.com>
@@ -545,6 +546,7 @@ proc-macro2-diagnostics,https://github.com/SergioBenitez/proc-macro2-diagnostics
545546
procfs,https://github.com/eminence/procfs,MIT OR Apache-2.0,Andrew Chin <achin@eminence32.net>
546547
procfs-core,https://github.com/eminence/procfs,MIT OR Apache-2.0,Andrew Chin <achin@eminence32.net>
547548
prometheus,https://github.com/tikv/rust-prometheus,Apache-2.0,"overvenus@gmail.com, siddontang@gmail.com, vistaswx@gmail.com"
549+
proptest,https://github.com/proptest-rs/proptest,MIT OR Apache-2.0,Jason Lingle
548550
prost,https://github.com/tokio-rs/prost,Apache-2.0,"Dan Burkert <dan@danburkert.com>, Lucio Franco <luciofranco14@gmail.com>, Casper Meijn <casper@meijn.net>, Tokio Contributors <team@tokio.rs>"
549551
prost-build,https://github.com/tokio-rs/prost,Apache-2.0,"Dan Burkert <dan@danburkert.com>, Lucio Franco <luciofranco14@gmail.com>, Casper Meijn <casper@meijn.net>, Tokio Contributors <team@tokio.rs>"
550552
prost-derive,https://github.com/tokio-rs/prost,Apache-2.0,"Dan Burkert <dan@danburkert.com>, Lucio Franco <luciofranco14@gmail.com>, Casper Meijn <casper@meijn.net>, Tokio Contributors <team@tokio.rs>"

quickwit/Cargo.lock

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

quickwit/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ openssl-probe = "0.1"
169169
opentelemetry = "0.31"
170170
opentelemetry-appender-tracing = "0.31"
171171
opentelemetry_sdk = { version = "0.31", features = ["rt-tokio"] }
172-
opentelemetry-otlp = { version = "0.31", features = ["grpc-tonic"] }
172+
opentelemetry-otlp = { version = "0.31", features = ["grpc-tonic", "http-json"] }
173173
ouroboros = "0.18"
174174
parquet = { version = "57", default-features = false, features = ["arrow", "zstd", "snap", "variant_experimental"] }
175175
percent-encoding = "2.3"

quickwit/quickwit-cli/src/logger.rs

Lines changed: 107 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use anyhow::Context;
1919
use opentelemetry::trace::TracerProvider;
2020
use opentelemetry::{KeyValue, global};
2121
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
22+
use opentelemetry_otlp::{Protocol as OtlpWireProtocol, WithExportConfig};
2223
use opentelemetry_sdk::logs::SdkLoggerProvider;
2324
use opentelemetry_sdk::propagation::TraceContextPropagator;
2425
use opentelemetry_sdk::trace::{BatchConfigBuilder, SdkTracerProvider};
@@ -39,6 +40,54 @@ use tracing_subscriber::prelude::*;
3940
use tracing_subscriber::registry::LookupSpan;
4041

4142
use crate::QW_ENABLE_OPENTELEMETRY_OTLP_EXPORTER_ENV_KEY;
43+
44+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45+
enum OtlpProtocol {
46+
Grpc,
47+
HttpProtobuf,
48+
HttpJson,
49+
}
50+
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),
72+
}
73+
}
74+
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
80+
.with_http()
81+
.with_protocol(OtlpWireProtocol::HttpBinary)
82+
.build(),
83+
OtlpProtocol::HttpJson => $builder
84+
.with_http()
85+
.with_protocol(OtlpWireProtocol::HttpJson)
86+
.build(),
87+
}
88+
};
89+
}
90+
4291
#[cfg(feature = "tokio-console")]
4392
use crate::QW_ENABLE_TOKIO_CONSOLE_ENV_KEY;
4493

@@ -98,10 +147,16 @@ pub fn setup_logging_and_tracing(
98147
// Note on disabling ANSI characters: setting the ansi boolean on event format is insufficient.
99148
// It is thus set on layers, see https://github.com/tokio-rs/tracing/issues/1817
100149
let provider_opt = if get_bool_from_env(QW_ENABLE_OPENTELEMETRY_OTLP_EXPORTER_ENV_KEY, false) {
101-
let span_exporter = opentelemetry_otlp::SpanExporter::builder()
102-
.with_tonic()
103-
.build()
104-
.context("failed to initialize OpenTelemetry OTLP exporter")?;
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")?;
105160
let span_processor = trace::BatchSpanProcessor::builder(span_exporter)
106161
.with_batch_config(
107162
BatchConfigBuilder::default()
@@ -117,10 +172,13 @@ pub fn setup_logging_and_tracing(
117172
.with_attribute(KeyValue::new("service.version", build_info.version.clone()))
118173
.build();
119174

120-
let logs_exporter = opentelemetry_otlp::LogExporter::builder()
121-
.with_tonic()
122-
.build()
123-
.context("failed to initialize OpenTelemetry OTLP logs")?;
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")?;
124182

125183
let logger_provider = SdkLoggerProvider::builder()
126184
.with_resource(resource.clone())
@@ -363,3 +421,44 @@ pub(super) mod jemalloc_profiled {
363421
))
364422
}
365423
}
424+
425+
#[cfg(test)]
426+
mod tests {
427+
use super::*;
428+
429+
#[test]
430+
fn test_resolve_otlp_protocol_defaults_to_grpc() {
431+
let protocol = resolve_otlp_protocol(&[None, None]).unwrap();
432+
assert_eq!(protocol, OtlpProtocol::Grpc);
433+
}
434+
435+
#[test]
436+
fn test_resolve_otlp_protocol_first_candidate_takes_priority() {
437+
let protocol = resolve_otlp_protocol(&[Some("http/protobuf"), Some("grpc")]).unwrap();
438+
assert_eq!(protocol, OtlpProtocol::HttpProtobuf);
439+
}
440+
441+
#[test]
442+
fn test_resolve_otlp_protocol_falls_back_to_later_candidate() {
443+
let protocol = resolve_otlp_protocol(&[None, Some("http/protobuf")]).unwrap();
444+
assert_eq!(protocol, OtlpProtocol::HttpProtobuf);
445+
}
446+
447+
#[test]
448+
fn test_resolve_otlp_protocol_grpc_explicit() {
449+
let protocol = resolve_otlp_protocol(&[Some("grpc")]).unwrap();
450+
assert_eq!(protocol, OtlpProtocol::Grpc);
451+
}
452+
453+
#[test]
454+
fn test_resolve_otlp_protocol_http_json_explicit() {
455+
let protocol = resolve_otlp_protocol(&[Some("http/json")]).unwrap();
456+
assert_eq!(protocol, OtlpProtocol::HttpJson);
457+
}
458+
459+
#[test]
460+
fn test_resolve_otlp_protocol_rejects_unsupported_value() {
461+
let result = resolve_otlp_protocol(&[Some("http/xml")]);
462+
assert!(result.is_err());
463+
}
464+
}

0 commit comments

Comments
 (0)