Skip to content

Commit 58ad33a

Browse files
authored
Add compression support for otlp tonic (#1165)
1 parent ba6f924 commit 58ad33a

File tree

10 files changed

+181
-35
lines changed

10 files changed

+181
-35
lines changed

opentelemetry-otlp/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Added
66
- Add OTLP HTTP Metrics Exporter [#1020](https://github.com/open-telemetry/opentelemetry-rust/pull/1020).
7+
- Add tonic compression support [#1165](https://github.com/open-telemetry/opentelemetry-rust/pull/1165).
78

89
## v0.12.0
910

opentelemetry-otlp/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ default = ["grpc-tonic", "trace"]
7070

7171
# grpc using tonic
7272
grpc-tonic = ["tonic", "prost", "http", "tokio", "opentelemetry-proto/gen-tonic"]
73+
gzip-tonic = ["tonic/gzip"]
7374
tls = ["tonic/tls"]
7475
tls-roots = ["tls", "tonic/tls-roots"]
7576

opentelemetry-otlp/src/exporter/grpcio.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::exporter::Compression;
12
use crate::ExportConfig;
23
#[cfg(feature = "serialize")]
34
use serde::{Deserialize, Serialize};
@@ -47,14 +48,6 @@ pub struct Credentials {
4748
pub key: String,
4849
}
4950

50-
/// The compression algorithm to use when sending data.
51-
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
52-
#[derive(Clone, Copy, Debug)]
53-
pub enum Compression {
54-
/// Compresses data using gzip.
55-
Gzip,
56-
}
57-
5851
impl From<Compression> for grpcio::CompressionAlgorithms {
5952
fn from(compression: Compression) -> Self {
6053
match compression {

opentelemetry-otlp/src/exporter/mod.rs

+47-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ use crate::exporter::grpcio::GrpcioExporterBuilder;
88
use crate::exporter::http::HttpExporterBuilder;
99
#[cfg(feature = "grpc-tonic")]
1010
use crate::exporter::tonic::TonicExporterBuilder;
11-
use crate::Protocol;
11+
use crate::{Error, Protocol};
12+
#[cfg(feature = "serialize")]
13+
use serde::{Deserialize, Serialize};
1214
use std::collections::HashMap;
15+
use std::fmt::{Display, Formatter};
1316
use std::str::FromStr;
1417
use std::time::Duration;
1518

@@ -21,6 +24,8 @@ pub const OTEL_EXPORTER_OTLP_ENDPOINT: &str = "OTEL_EXPORTER_OTLP_ENDPOINT";
2124
pub const OTEL_EXPORTER_OTLP_ENDPOINT_DEFAULT: &str = OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT;
2225
/// Protocol the exporter will use. Either `http/protobuf` or `grpc`.
2326
pub const OTEL_EXPORTER_OTLP_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_PROTOCOL";
27+
/// Compression algorithm to use, defaults to none.
28+
pub const OTEL_EXPORTER_OTLP_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_COMPRESSION";
2429

2530
#[cfg(feature = "http-proto")]
2631
/// Default protocol, using http-proto.
@@ -79,6 +84,33 @@ impl Default for ExportConfig {
7984
}
8085
}
8186

87+
/// The compression algorithm to use when sending data.
88+
#[cfg_attr(feature = "serialize", derive(Deserialize, Serialize))]
89+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
90+
pub enum Compression {
91+
/// Compresses data using gzip.
92+
Gzip,
93+
}
94+
95+
impl Display for Compression {
96+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
97+
match self {
98+
Compression::Gzip => write!(f, "gzip"),
99+
}
100+
}
101+
}
102+
103+
impl FromStr for Compression {
104+
type Err = Error;
105+
106+
fn from_str(s: &str) -> Result<Self, Self::Err> {
107+
match s {
108+
"gzip" => Ok(Compression::Gzip),
109+
_ => Err(Error::UnsupportedCompressionAlgorithm(s.to_string())),
110+
}
111+
}
112+
}
113+
82114
/// default protocol based on enabled features
83115
fn default_protocol() -> Protocol {
84116
match OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT {
@@ -217,13 +249,15 @@ impl<B: HasExportConfig> WithExportConfig for B {
217249
mod tests {
218250
// If an env test fails then the mutex will be poisoned and the following error will be displayed.
219251
const LOCK_POISONED_MESSAGE: &str = "one of the other pipeline builder from env tests failed";
252+
220253
use crate::exporter::{
221254
default_endpoint, default_protocol, WithExportConfig, OTEL_EXPORTER_OTLP_ENDPOINT,
222255
OTEL_EXPORTER_OTLP_GRPC_ENDPOINT_DEFAULT, OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT,
223256
OTEL_EXPORTER_OTLP_PROTOCOL_GRPC, OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF,
224257
OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT,
225258
};
226-
use crate::{new_exporter, Protocol, OTEL_EXPORTER_OTLP_PROTOCOL};
259+
use crate::{new_exporter, Compression, Protocol, OTEL_EXPORTER_OTLP_PROTOCOL};
260+
use std::str::FromStr;
227261
use std::sync::Mutex;
228262

229263
// Make sure env tests are not running concurrently
@@ -345,4 +379,15 @@ mod tests {
345379
std::env::remove_var(OTEL_EXPORTER_OTLP_TIMEOUT);
346380
assert!(std::env::var(OTEL_EXPORTER_OTLP_TIMEOUT).is_err());
347381
}
382+
383+
#[test]
384+
fn test_compression_parse() {
385+
assert_eq!(Compression::from_str("gzip").unwrap(), Compression::Gzip);
386+
Compression::from_str("bad_compression").expect_err("bad compression");
387+
}
388+
389+
#[test]
390+
fn test_compression_to_str() {
391+
assert_eq!(Compression::Gzip.to_string(), "gzip");
392+
}
348393
}

opentelemetry-otlp/src/exporter/tonic.rs

+55-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
use crate::ExportConfig;
1+
use crate::exporter::Compression;
2+
use crate::{ExportConfig, OTEL_EXPORTER_OTLP_COMPRESSION};
23
use std::fmt::{Debug, Formatter};
4+
use tonic::codec::CompressionEncoding;
35
use tonic::metadata::MetadataMap;
46
#[cfg(feature = "tls")]
57
use tonic::transport::ClientTlsConfig;
@@ -18,6 +20,39 @@ pub struct TonicConfig {
1820
/// TLS settings for the collector endpoint.
1921
#[cfg(feature = "tls")]
2022
pub tls_config: Option<ClientTlsConfig>,
23+
24+
/// The compression algorithm to use when communicating with the collector.
25+
pub compression: Option<Compression>,
26+
}
27+
28+
impl TryFrom<Compression> for tonic::codec::CompressionEncoding {
29+
type Error = crate::Error;
30+
31+
fn try_from(value: Compression) -> Result<Self, Self::Error> {
32+
match value {
33+
#[cfg(feature = "gzip-tonic")]
34+
Compression::Gzip => Ok(tonic::codec::CompressionEncoding::Gzip),
35+
#[cfg(not(feature = "gzip-tonic"))]
36+
Compression::Gzip => Err(crate::Error::UnsupportedCompressionAlgorithm(
37+
value.to_string(),
38+
)),
39+
}
40+
}
41+
}
42+
43+
pub(crate) fn resolve_compression(
44+
tonic_config: &TonicConfig,
45+
env_override: &'static str,
46+
) -> Result<Option<CompressionEncoding>, crate::Error> {
47+
if let Some(compression) = tonic_config.compression {
48+
Ok(Some(compression.try_into()?))
49+
} else if let Ok(compression) = std::env::var(env_override) {
50+
Ok(Some(compression.parse::<Compression>()?.try_into()?))
51+
} else if let Ok(compression) = std::env::var(OTEL_EXPORTER_OTLP_COMPRESSION) {
52+
Ok(Some(compression.parse::<Compression>()?.try_into()?))
53+
} else {
54+
Ok(None)
55+
}
2156
}
2257

2358
/// Build a trace exporter that uses [tonic] as grpc layer and opentelemetry protocol.
@@ -60,6 +95,7 @@ impl Default for TonicExporterBuilder {
6095
)),
6196
#[cfg(feature = "tls")]
6297
tls_config: None,
98+
compression: None,
6399
};
64100

65101
TonicExporterBuilder {
@@ -94,6 +130,12 @@ impl TonicExporterBuilder {
94130
self
95131
}
96132

133+
/// Set the compression algorithm to use when communicating with the collector.
134+
pub fn with_compression(mut self, compression: Compression) -> Self {
135+
self.tonic_config.compression = Some(compression);
136+
self
137+
}
138+
97139
/// Use `channel` as tonic's transport channel.
98140
/// this will override tls config and should only be used
99141
/// when working with non-HTTP transports.
@@ -119,6 +161,8 @@ impl TonicExporterBuilder {
119161

120162
#[cfg(test)]
121163
mod tests {
164+
#[cfg(feature = "gzip-tonic")]
165+
use crate::exporter::Compression;
122166
use crate::TonicExporterBuilder;
123167
use tonic::metadata::{MetadataMap, MetadataValue};
124168

@@ -151,4 +195,14 @@ mod tests {
151195
.len()
152196
);
153197
}
198+
199+
#[test]
200+
#[cfg(feature = "gzip-tonic")]
201+
fn test_with_compression() {
202+
// metadata should merge with the current one with priority instead of just replacing it
203+
let mut metadata = MetadataMap::new();
204+
metadata.insert("foo", "bar".parse().unwrap());
205+
let builder = TonicExporterBuilder::default().with_compression(Compression::Gzip);
206+
assert_eq!(builder.tonic_config.compression.unwrap(), Compression::Gzip);
207+
}
154208
}

opentelemetry-otlp/src/lib.rs

+14-6
Original file line numberDiff line numberDiff line change
@@ -189,24 +189,28 @@ mod metric;
189189
mod span;
190190
mod transform;
191191

192+
pub use crate::exporter::Compression;
192193
pub use crate::exporter::ExportConfig;
193194
#[cfg(feature = "trace")]
194195
pub use crate::span::{
195-
OtlpTracePipeline, SpanExporter, SpanExporterBuilder, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
196-
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
196+
OtlpTracePipeline, SpanExporter, SpanExporterBuilder, OTEL_EXPORTER_OTLP_TRACES_COMPRESSION,
197+
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
197198
};
198199

199200
#[cfg(feature = "metrics")]
200201
pub use crate::metric::{
201202
MetricsExporter, MetricsExporterBuilder, OtlpMetricPipeline,
202-
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
203+
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
204+
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
203205
};
204206

205207
#[cfg(feature = "logs")]
206-
pub use crate::logs::*;
208+
pub use crate::logs::{
209+
LogExporter, LogExporterBuilder, OtlpLogPipeline, OTEL_EXPORTER_OTLP_LOGS_COMPRESSION,
210+
};
207211

208212
pub use crate::exporter::{
209-
HasExportConfig, WithExportConfig, OTEL_EXPORTER_OTLP_ENDPOINT,
213+
HasExportConfig, WithExportConfig, OTEL_EXPORTER_OTLP_COMPRESSION, OTEL_EXPORTER_OTLP_ENDPOINT,
210214
OTEL_EXPORTER_OTLP_ENDPOINT_DEFAULT, OTEL_EXPORTER_OTLP_PROTOCOL,
211215
OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT, OTEL_EXPORTER_OTLP_TIMEOUT,
212216
OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT,
@@ -217,7 +221,7 @@ use opentelemetry_sdk::export::ExportError;
217221
use std::time::{Duration, SystemTime, UNIX_EPOCH};
218222

219223
#[cfg(feature = "grpc-sys")]
220-
pub use crate::exporter::grpcio::{Compression, Credentials, GrpcioConfig, GrpcioExporterBuilder};
224+
pub use crate::exporter::grpcio::{Credentials, GrpcioConfig, GrpcioExporterBuilder};
221225
#[cfg(feature = "http-proto")]
222226
pub use crate::exporter::http::HttpExporterBuilder;
223227
#[cfg(feature = "grpc-tonic")]
@@ -347,6 +351,10 @@ pub enum Error {
347351
/// The pipeline will need a exporter to complete setup. Throw this error if none is provided.
348352
#[error("no exporter builder is provided, please provide one using with_exporter() method")]
349353
NoExporterBuilder,
354+
355+
/// Unsupported compression algorithm.
356+
#[error("unsupported compression algorithm '{0}'")]
357+
UnsupportedCompressionAlgorithm(String),
350358
}
351359

352360
#[cfg(feature = "grpc-tonic")]

opentelemetry-otlp/src/logs.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
#[cfg(feature = "grpc-tonic")]
66
use {
7-
crate::exporter::tonic::{TonicConfig, TonicExporterBuilder},
7+
crate::exporter::tonic::{resolve_compression, TonicConfig, TonicExporterBuilder},
88
opentelemetry_proto::tonic::collector::logs::v1::{
99
logs_service_client::LogsServiceClient as TonicLogsServiceClient,
1010
ExportLogsServiceRequest as TonicRequest,
@@ -60,6 +60,9 @@ use opentelemetry_api::{
6060
};
6161
use opentelemetry_sdk::{self, export::logs::LogData, logs::BatchMessage, runtime::RuntimeChannel};
6262

63+
/// Compression algorithm to use, defaults to none.
64+
pub const OTEL_EXPORTER_OTLP_LOGS_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_LOGS_COMPRESSION";
65+
6366
impl OtlpPipeline {
6467
/// Create a OTLP logging pipeline.
6568
pub fn logging(self) -> OtlpLogPipeline {
@@ -235,10 +238,16 @@ impl LogExporter {
235238
tonic_config: TonicConfig,
236239
channel: tonic::transport::Channel,
237240
) -> Result<Self, crate::Error> {
241+
let mut log_exporter = TonicLogsServiceClient::new(channel);
242+
if let Some(compression) =
243+
resolve_compression(&tonic_config, OTEL_EXPORTER_OTLP_LOGS_COMPRESSION)?
244+
{
245+
log_exporter = log_exporter.send_compressed(compression);
246+
}
238247
Ok(LogExporter::Tonic {
239248
timeout: config.timeout,
240249
metadata: tonic_config.metadata,
241-
log_exporter: TonicLogsServiceClient::new(channel),
250+
log_exporter,
242251
})
243252
}
244253

0 commit comments

Comments
 (0)