Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support http/json protocol #1585

Merged
merged 40 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1482d5c
Add http-json feature flag
ramgdev Feb 27, 2024
576a56d
prototype json serialization
ramgdev Feb 27, 2024
d13ab4b
fix default protocol
ramgdev Feb 27, 2024
ed4fc03
fix typo
ramgdev Feb 27, 2024
587c341
use default protocol for HttpConfig
ramgdev Feb 28, 2024
d460bda
Update opentelemetry-otlp/src/exporter/http/trace.rs
ramgdev Feb 28, 2024
da76734
address review comments
ramgdev Feb 29, 2024
aa74b9c
run pre-commit script
ramgdev Feb 29, 2024
efa8588
fix dependency
ramgdev Feb 29, 2024
e47a80f
Merge branch 'main' into otel-json
ramgdev Mar 3, 2024
04593ec
Update mod.rs
ramgdev Mar 3, 2024
277df67
unit test default endpoint
ramgdev Mar 3, 2024
5d5e06e
test default protocol
ramgdev Mar 4, 2024
359babe
Merge branch 'main' into otel-json
ramgdev Mar 12, 2024
8c9577f
Merge branch 'main' into otel-json
ramgdev Mar 12, 2024
f175980
Merge branch 'main' into otel-json
ramgdev Mar 15, 2024
f93058c
enable http-json for all signals
ramgdev Mar 18, 2024
46f92c4
Merge branch 'main' into otel-json
ramgdev Mar 18, 2024
0f1a805
fix lint
ramgdev Mar 18, 2024
6ebea63
Merge branch 'main' into otel-json
ramgdev Mar 19, 2024
e50ac0e
run precommit
ramgdev Mar 19, 2024
049e96a
Merge branch 'main' into otel-json
ramgdev Mar 20, 2024
d64bda0
Merge branch 'main' into otel-json
ramgdev Mar 24, 2024
ad8322b
add unit test
ramgdev Mar 25, 2024
e457a85
Merge branch 'main' into otel-json
ramgdev Mar 25, 2024
5222f8d
Merge branch 'main' into otel-json
ramgdev Mar 25, 2024
25b0857
Merge branch 'main' into otel-json
ramgdev Mar 29, 2024
63fb044
update changelog
ramgdev Mar 29, 2024
bcde320
Merge branch 'main' into otel-json
ramgdev Mar 29, 2024
f9abf24
address code review
ramgdev Mar 29, 2024
250fab4
remove duplicate line
ramgdev Mar 29, 2024
67552f8
Update opentelemetry-otlp/src/exporter/http/metrics.rs
ramgdev Apr 1, 2024
765ab99
Merge branch 'main' into otel-json
ramgdev Apr 5, 2024
67d6875
Merge branch 'main' into otel-json
ramgdev Apr 15, 2024
680b191
Merge branch 'main' into otel-json
lalitb Apr 16, 2024
c6d1817
Merge branch 'main' into otel-json
ramgdev Apr 20, 2024
4ed5158
move build_body to otlphttpclient
ramgdev Apr 20, 2024
ebc3cc3
allow builder to accept protocol
ramgdev Apr 20, 2024
86a89d5
Merge branch 'main' into otel-json
ramgdev Apr 21, 2024
aa2cd05
Merge branch 'main' into otel-json
TommyCpp Apr 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions opentelemetry-otlp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ futures-core = { workspace = true }
opentelemetry = { version = "0.22", default-features = false, path = "../opentelemetry" }
opentelemetry_sdk = { version = "0.22", default-features = false, path = "../opentelemetry-sdk" }
opentelemetry-http = { version = "0.11", path = "../opentelemetry-http", optional = true }
opentelemetry-proto = { version = "0.5", path = "../opentelemetry-proto", default-features = false }
opentelemetry-proto = { version = "0.5", path = "../opentelemetry-proto", default-features = false, features=["with-serde"] }
opentelemetry-semantic-conventions = { version = "0.14", path = "../opentelemetry-semantic-conventions" }

prost = { workspace = true, optional = true }
Expand All @@ -42,6 +42,7 @@ reqwest = { workspace = true, optional = true }
http = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"], optional = true }
thiserror = { workspace = true }
serde_json = { workspace = true, optional = true }

[dev-dependencies]
tokio-stream = { workspace = true, features = ["net"] }
Expand All @@ -58,7 +59,7 @@ metrics = ["opentelemetry/metrics", "opentelemetry_sdk/metrics", "opentelemetry-
logs = ["opentelemetry/logs", "opentelemetry_sdk/logs", "opentelemetry-proto/logs"]

# add ons
serialize = ["serde"]
serialize = ["serde", "serde_json"]

default = ["grpc-tonic", "trace"]

Expand All @@ -73,6 +74,8 @@ http-proto = ["prost", "opentelemetry-http", "opentelemetry-proto/gen-tonic-mess
reqwest-blocking-client = ["reqwest/blocking", "opentelemetry-http/reqwest"]
reqwest-client = ["reqwest", "opentelemetry-http/reqwest"]
reqwest-rustls = ["reqwest", "reqwest/rustls-tls-native-roots"]
# http json
http-json = ["serde_json", "prost", "opentelemetry-http", "opentelemetry-proto/gen-tonic-messages", "http", "trace", "metrics"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can enable the with-serde feature of opentelemetry-proto here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea. I'll test it out.


# test
integration-testing = ["tonic", "prost", "tokio/full", "trace"]
2 changes: 1 addition & 1 deletion opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ publish = false
once_cell = { workspace = true }
opentelemetry = { path = "../../../opentelemetry" }
opentelemetry_sdk = { path = "../../../opentelemetry-sdk", features = ["rt-tokio", "metrics", "logs"] }
opentelemetry-otlp = { path = "../..", features = ["http-proto", "reqwest-client", "logs"] }
opentelemetry-otlp = { path = "../..", features = ["http-proto", "http-json", "reqwest-client", "logs"] }
opentelemetry-appender-tracing = { path = "../../../opentelemetry-appender-tracing", default-features = false}
opentelemetry-semantic-conventions = { path = "../../../opentelemetry-semantic-conventions" }

Expand Down
2 changes: 1 addition & 1 deletion opentelemetry-otlp/src/exporter/http/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ fn build_body(metrics: &mut ResourceMetrics) -> Result<(Vec<u8>, &'static str)>
}

#[cfg(not(feature = "http-proto"))]
fn build_body(metrics: &mut ResourceMetrics) -> Result<(Vec<u8>, &'static str)> {
fn build_body(_metrics: &mut ResourceMetrics) -> Result<(Vec<u8>, &'static str)> {
Err(MetricsError::Other(
"No http protocol configured. Enable one via `http-proto`".into(),
ramgdev marked this conversation as resolved.
Show resolved Hide resolved
))
Expand Down
8 changes: 4 additions & 4 deletions opentelemetry-otlp/src/exporter/http/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
ExportConfig, Protocol, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS,
ExportConfig, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS,
OTEL_EXPORTER_OTLP_TIMEOUT,
};
use http::{HeaderName, HeaderValue, Uri};
Expand All @@ -10,7 +10,7 @@ use std::str::FromStr;
use std::sync::{Arc, Mutex};
use std::time::Duration;

use super::{default_headers, parse_header_string};
use super::{default_headers, default_protocol, parse_header_string};

#[cfg(feature = "metrics")]
mod metrics;
Expand All @@ -22,7 +22,7 @@ mod logs;
mod trace;

/// Configuration of the http transport
#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", feature = "http-json"))]
#[derive(Debug)]
#[cfg_attr(
all(
Expand Down Expand Up @@ -98,7 +98,7 @@ impl Default for HttpExporterBuilder {
fn default() -> Self {
HttpExporterBuilder {
exporter_config: ExportConfig {
protocol: Protocol::HttpBinary,
protocol: default_protocol(),
..ExportConfig::default()
},
http_config: HttpConfig {
Expand Down
24 changes: 16 additions & 8 deletions opentelemetry-otlp/src/exporter/http/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,23 +68,31 @@ impl SpanExporter for OtlpHttpClient {
}
}

#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", feature = "http-json"))]
fn build_body(spans: Vec<SpanData>) -> TraceResult<(Vec<u8>, &'static str)> {
use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest;
use prost::Message;

let req = ExportTraceServiceRequest {
resource_spans: spans.into_iter().map(Into::into).collect(),
};
let mut buf = vec![];
req.encode(&mut buf).map_err(crate::Error::from)?;

Ok((buf, "application/x-protobuf"))
let buf;
let ctype;
#[cfg(all(feature = "http-proto", not(feature = "http-json")))]{
use prost::Message;
buf = req.encode_to_vec();
ctype = "application/x-protobuf";
}
#[cfg(all(feature = "http-json", not(feature = "http=proto")))]{
ramgdev marked this conversation as resolved.
Show resolved Hide resolved
let json_struct = serde_json::to_string_pretty(&req).unwrap();
buf = json_struct.into();
ctype = "application/json";
}
Ok((buf, ctype))
}

#[cfg(not(feature = "http-proto"))]
#[cfg(not(any(feature = "http-proto", feature = "http-json")))]
fn build_body(spans: Vec<SpanData>) -> TraceResult<(Vec<u8>, &'static str)> {
Err(TraceError::Other(
"No http protocol configured. Enable one via `http-proto`".into(),
"No http protocol configured. Enable one via `http-proto` or `http-json`".into(),
))
}
28 changes: 17 additions & 11 deletions opentelemetry-otlp/src/exporter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! OTLP supports sending data via different protocols and formats.

#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", feature = "http-json"))]
use crate::exporter::http::HttpExporterBuilder;
#[cfg(feature = "grpc-tonic")]
use crate::exporter::tonic::TonicExporterBuilder;
Expand All @@ -28,18 +28,22 @@ pub const OTEL_EXPORTER_OTLP_PROTOCOL: &str = "OTEL_EXPORTER_OTLP_PROTOCOL";
/// Compression algorithm to use, defaults to none.
pub const OTEL_EXPORTER_OTLP_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_COMPRESSION";

#[cfg(all(feature = "trace", not(feature = "http-proto"), not(feature = "grpc-tonic")))]
/// Default protocol, using http-json.
pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON;
cijothomas marked this conversation as resolved.
Show resolved Hide resolved
#[cfg(feature = "http-proto")]
/// Default protocol, using http-proto.
pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF;
#[cfg(all(feature = "grpc-tonic", not(feature = "http-proto")))]
/// Default protocol, using grpc as http-proto feature is not enabled.
#[cfg(all(feature = "grpc-tonic", not(all(feature = "http-proto", feature = "http-json"))))]
/// Default protocol, using grpc as http-proto or http-json feature is not enabled.
pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = OTEL_EXPORTER_OTLP_PROTOCOL_GRPC;
#[cfg(not(any(any(feature = "grpc-tonic", feature = "http-proto"))))]
#[cfg(not(any(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))))]
/// Default protocol if no features are enabled.
pub const OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT: &str = "";
ramgdev marked this conversation as resolved.
Show resolved Hide resolved

const OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF: &str = "http/protobuf";
const OTEL_EXPORTER_OTLP_PROTOCOL_GRPC: &str = "grpc";
const OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON: &str = "http/json";

/// Max waiting time for the backend to process each signal batch, defaults to 10 seconds.
pub const OTEL_EXPORTER_OTLP_TIMEOUT: &str = "OTEL_EXPORTER_OTLP_TIMEOUT";
Expand All @@ -50,7 +54,7 @@ pub const OTEL_EXPORTER_OTLP_TIMEOUT_DEFAULT: u64 = 10;
const OTEL_EXPORTER_OTLP_GRPC_ENDPOINT_DEFAULT: &str = "http://localhost:4317";
const OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT: &str = "http://localhost:4318";

#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", feature = "http-json"))]
pub(crate) mod http;
#[cfg(feature = "grpc-tonic")]
pub(crate) mod tonic;
Expand Down Expand Up @@ -112,6 +116,7 @@ fn default_protocol() -> Protocol {
match OTEL_EXPORTER_OTLP_PROTOCOL_DEFAULT {
OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_PROTOBUF => Protocol::HttpBinary,
OTEL_EXPORTER_OTLP_PROTOCOL_GRPC => Protocol::Grpc,
OTEL_EXPORTER_OTLP_PROTOCOL_HTTP_JSON => Protocol::HttpJson,
_ => Protocol::HttpBinary,
}
}
Expand All @@ -121,11 +126,12 @@ fn default_endpoint(protocol: Protocol) -> String {
match protocol {
Protocol::Grpc => OTEL_EXPORTER_OTLP_GRPC_ENDPOINT_DEFAULT.to_string(),
Protocol::HttpBinary => OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT.to_string(),
Protocol::HttpJson => OTEL_EXPORTER_OTLP_HTTP_ENDPOINT_DEFAULT.to_string(),
}
}

/// default user-agent headers
#[cfg(any(feature = "grpc-tonic", feature = "http-proto"))]
#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
fn default_headers() -> std::collections::HashMap<String, String> {
let mut headers = std::collections::HashMap::new();
headers.insert(
Expand All @@ -148,7 +154,7 @@ impl HasExportConfig for TonicExporterBuilder {
}
}

#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", feature = "http-json"))]
impl HasExportConfig for HttpExporterBuilder {
fn export_config(&mut self) -> &mut ExportConfig {
&mut self.exporter_config
Expand Down Expand Up @@ -210,15 +216,15 @@ impl<B: HasExportConfig> WithExportConfig for B {
}
}

#[cfg(any(feature = "grpc-tonic", feature = "http-proto"))]
#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
fn parse_header_string(value: &str) -> impl Iterator<Item = (&str, &str)> {
value
.split_terminator(',')
.map(str::trim)
.filter_map(parse_header_key_value_string)
}

#[cfg(any(feature = "grpc-tonic", feature = "http-proto"))]
#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
fn parse_header_key_value_string(key_value_string: &str) -> Option<(&str, &str)> {
key_value_string
.split_once('=')
Expand All @@ -227,7 +233,7 @@ fn parse_header_key_value_string(key_value_string: &str) -> Option<(&str, &str)>
}

#[cfg(test)]
#[cfg(any(feature = "grpc-tonic", feature = "http-proto"))]
#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
mod tests {

pub(crate) fn run_env_test<T, F>(env_vars: T, f: F)
Expand All @@ -245,7 +251,7 @@ mod tests {
)
}

#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", feature = "http-json"))]
#[test]
fn test_default_http_endpoint() {
let exporter_builder = crate::new_exporter().http();
Expand Down
23 changes: 12 additions & 11 deletions opentelemetry-otlp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
//! * `reqwest-blocking-client`: Use reqwest blocking http client.
//! * `reqwest-client`: Use reqwest http client.
//! * `reqwest-rustls`: Use reqwest with TLS.
//! * `http-json`: Use http as transport layer, JSON as body format.
//!
//! # Kitchen Sink Full Configuration
//!
Expand Down Expand Up @@ -247,7 +248,7 @@ pub use crate::exporter::{

use opentelemetry_sdk::export::ExportError;

#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", feature = "http-json"))]
pub use crate::exporter::http::HttpExporterBuilder;

#[cfg(feature = "grpc-tonic")]
Expand Down Expand Up @@ -278,7 +279,7 @@ impl OtlpExporterPipeline {
/// and build the exporter.
///
/// This exporter can be used in both `tracing` and `metrics` pipeline.
#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", feature = "http-json"))]
pub fn http(self) -> HttpExporterBuilder {
HttpExporterBuilder::default()
}
Expand Down Expand Up @@ -314,7 +315,7 @@ pub enum Error {
Transport(#[from] tonic::transport::Error),

/// Wrap the [`tonic::codegen::http::uri::InvalidUri`] error
#[cfg(any(feature = "grpc-tonic", feature = "http-proto"))]
#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
#[error("invalid URI {0}")]
InvalidUri(#[from] http::uri::InvalidUri),

Expand All @@ -329,32 +330,32 @@ pub enum Error {
},

/// Http requests failed because no http client is provided.
#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", feature = "http-json"))]
#[error(
"no http client, you must select one from features or provide your own implementation"
)]
NoHttpClient,

/// Http requests failed.
#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", feature = "http-json"))]
#[error("http request failed with {0}")]
RequestFailed(#[from] opentelemetry_http::HttpError),

/// The provided value is invalid in HTTP headers.
#[cfg(any(feature = "grpc-tonic", feature = "http-proto"))]
#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
#[error("http header value error {0}")]
InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),

/// The provided name is invalid in HTTP headers.
#[cfg(any(feature = "grpc-tonic", feature = "http-proto"))]
#[cfg(any(feature = "grpc-tonic", feature = "http-proto", feature = "http-json"))]
#[error("http header name error {0}")]
InvalidHeaderName(#[from] http::header::InvalidHeaderName),

/// Prost encode failed
#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", all(feature = "http-json", not(feature = "trace"))))]
#[error("prost encoding error {0}")]
EncodeError(#[from] prost::EncodeError),

/// The lock in exporters has been poisoned.
#[cfg(feature = "metrics")]
#[error("the lock of the {0} has been poisoned")]
Expand Down Expand Up @@ -393,10 +394,10 @@ impl ExportError for Error {
pub enum Protocol {
/// GRPC protocol
Grpc,
// TODO add support for other protocols
// HttpJson,
/// HTTP protocol with binary protobuf
HttpBinary,
/// HTTP protocol with JSON payload
HttpJson,
}

#[derive(Debug, Default)]
Expand Down
8 changes: 4 additions & 4 deletions opentelemetry-otlp/src/span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use sdk::runtime::RuntimeChannel;
#[cfg(feature = "grpc-tonic")]
use crate::exporter::tonic::TonicExporterBuilder;

#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", feature = "http-json"))]
use crate::exporter::http::HttpExporterBuilder;

use crate::{NoExporterConfig, OtlpPipeline};
Expand Down Expand Up @@ -187,7 +187,7 @@ pub enum SpanExporterBuilder {
#[cfg(feature = "grpc-tonic")]
Tonic(TonicExporterBuilder),
/// Http span exporter builder
#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", feature = "http-json"))]
Http(HttpExporterBuilder),
}

Expand All @@ -197,7 +197,7 @@ impl SpanExporterBuilder {
match self {
#[cfg(feature = "grpc-tonic")]
SpanExporterBuilder::Tonic(builder) => builder.build_span_exporter(),
#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", feature = "http-json"))]
SpanExporterBuilder::Http(builder) => builder.build_span_exporter(),
}
}
Expand All @@ -210,7 +210,7 @@ impl From<TonicExporterBuilder> for SpanExporterBuilder {
}
}

#[cfg(feature = "http-proto")]
#[cfg(any(feature = "http-proto", feature = "http-json"))]
impl From<HttpExporterBuilder> for SpanExporterBuilder {
fn from(exporter: HttpExporterBuilder) -> Self {
SpanExporterBuilder::Http(exporter)
Expand Down
Loading