-
Notifications
You must be signed in to change notification settings - Fork 21
feat(data-pipeline): OTLP HTTP/protobuf trace export #2115
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
base: main
Are you sure you want to change the base?
Changes from all commits
5190ab4
946f116
07c7296
6f385ba
a52e30b
4a4846a
54d8856
772be3e
46d72a7
8f3c38e
2633427
e493d8d
4a81412
3091b57
664f16f
9bc5cf2
a8a305f
58ba1b8
1cb1dfb
b479255
55541eb
421cb6d
a790182
af79de7
b21be02
809914e
855f4c1
5952434
21769ac
f60fa27
3431c1d
18f28c5
00261ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,20 +6,77 @@ | |
| use http::HeaderMap; | ||
| use std::time::Duration; | ||
|
|
||
| /// OTLP trace export protocol. HTTP/JSON is currently supported. | ||
| /// OTLP trace export protocol. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like grpc should still be excluded here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] | ||
| pub(crate) enum OtlpProtocol { | ||
| #[non_exhaustive] | ||
| pub enum OtlpProtocol { | ||
| /// HTTP with JSON body (Content-Type: application/json). Default for HTTP. | ||
| #[default] | ||
| HttpJson, | ||
| /// HTTP with protobuf body. (Not supported yet) | ||
| #[allow(dead_code)] | ||
| /// HTTP with protobuf body (Content-Type: application/x-protobuf). | ||
| HttpProtobuf, | ||
| /// gRPC. (Not supported yet) | ||
| #[allow(dead_code)] | ||
| /// gRPC. Parsed by `FromStr` so callers get a clean error, but rejected at export time | ||
| /// (unsupported). | ||
| Grpc, | ||
| } | ||
|
|
||
| impl std::str::FromStr for OtlpProtocol { | ||
| type Err = String; | ||
| fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
| match s { | ||
| "http/json" => Ok(OtlpProtocol::HttpJson), | ||
| "http/protobuf" => Ok(OtlpProtocol::HttpProtobuf), | ||
| "grpc" => Ok(OtlpProtocol::Grpc), | ||
| other => Err(format!("unknown OTLP protocol: {other}")), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// The wire encoding actually used to send OTLP traces over HTTP. Internal, closed set: the | ||
| /// only encodings the exporter supports. The user-facing [`OtlpProtocol`] (which also carries the | ||
| /// unsupported `Grpc`) converts into this at the send boundary via `TryFrom`. | ||
| #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| pub enum OtlpWireProtocol { | ||
| Json, | ||
| Protobuf, | ||
| } | ||
|
|
||
| impl std::convert::TryFrom<OtlpProtocol> for OtlpWireProtocol { | ||
| type Error = OtlpProtocol; | ||
| /// Maps the user-facing protocol to a supported wire encoding. `Grpc` is unsupported and | ||
| /// returns `Err(Grpc)` so the caller surfaces a clean error instead of silently downgrading. | ||
| fn try_from(p: OtlpProtocol) -> Result<Self, Self::Error> { | ||
| match p { | ||
| OtlpProtocol::HttpJson => Ok(OtlpWireProtocol::Json), | ||
| OtlpProtocol::HttpProtobuf => Ok(OtlpWireProtocol::Protobuf), | ||
| other => Err(other), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl OtlpWireProtocol { | ||
| /// The HTTP `Content-Type` for this encoding. | ||
| pub fn content_type(&self) -> http::HeaderValue { | ||
| match self { | ||
| OtlpWireProtocol::Json => libdd_common::header::APPLICATION_JSON, | ||
| OtlpWireProtocol::Protobuf => libdd_common::header::APPLICATION_PROTOBUF, | ||
| } | ||
| } | ||
|
|
||
| /// Encode the prost OTLP request to this wire format. | ||
| pub fn encode( | ||
| &self, | ||
| req: &libdd_trace_utils::otlp_encoder::ProtoExportTraceServiceRequest, | ||
| ) -> Result<Vec<u8>, serde_json::Error> { | ||
| match self { | ||
| OtlpWireProtocol::Json => libdd_trace_utils::otlp_encoder::encode_otlp_json(req), | ||
| OtlpWireProtocol::Protobuf => { | ||
| Ok(libdd_trace_utils::otlp_encoder::encode_otlp_protobuf(req)) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Default timeout for OTLP export requests. | ||
| pub const DEFAULT_OTLP_TIMEOUT: Duration = Duration::from_secs(10); | ||
|
|
||
|
|
@@ -32,7 +89,51 @@ pub struct OtlpTraceConfig { | |
| pub headers: HeaderMap, | ||
| /// Request timeout. | ||
| pub timeout: Duration, | ||
| /// Protocol (for future use; currently only HttpJson is supported). | ||
| #[allow(dead_code)] | ||
| pub(crate) protocol: OtlpProtocol, | ||
| /// OTLP export protocol (selects body encoding and content-type). | ||
| pub protocol: OtlpProtocol, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're matching protocol several times in this PR and it's kind of awkward because impl OtlpWireProtocol {
fn content_type(&self) -> HeaderValue { /* 2 arms */ }
fn encode(&self, req: &ExportTraceServiceRequest) -> Result<Vec<u8>> { /* 2 arms */ }
} and encapsulate the correct behavior in the type.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated in 809914e |
||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| use std::str::FromStr; | ||
| #[test] | ||
| fn protocol_from_str() { | ||
| assert_eq!( | ||
| OtlpProtocol::from_str("http/json").unwrap(), | ||
| OtlpProtocol::HttpJson | ||
| ); | ||
| assert_eq!( | ||
| OtlpProtocol::from_str("http/protobuf").unwrap(), | ||
| OtlpProtocol::HttpProtobuf | ||
| ); | ||
| assert_eq!(OtlpProtocol::from_str("grpc").unwrap(), OtlpProtocol::Grpc); | ||
| assert!(OtlpProtocol::from_str("nonsense").is_err()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn wire_protocol_from_user_protocol() { | ||
| use std::convert::TryFrom; | ||
| assert_eq!( | ||
| OtlpWireProtocol::try_from(OtlpProtocol::HttpJson).unwrap(), | ||
| OtlpWireProtocol::Json | ||
| ); | ||
| assert_eq!( | ||
| OtlpWireProtocol::try_from(OtlpProtocol::HttpProtobuf).unwrap(), | ||
| OtlpWireProtocol::Protobuf | ||
| ); | ||
| assert!(OtlpWireProtocol::try_from(OtlpProtocol::Grpc).is_err()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn wire_protocol_content_types() { | ||
| assert_eq!( | ||
| OtlpWireProtocol::Json.content_type(), | ||
| libdd_common::header::APPLICATION_JSON | ||
| ); | ||
| assert_eq!( | ||
| OtlpWireProtocol::Protobuf.content_type(), | ||
| libdd_common::header::APPLICATION_PROTOBUF | ||
| ); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The doc should probably make it clear that this function is inert if an otlp endpoint isn't set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated in a790182