libdd_trace_utils/otlp_encoder/
mod.rs1pub(crate) mod json_serializer;
8pub mod mapper;
9
10pub use mapper::map_traces_to_otlp;
11
12pub use libdd_trace_protobuf::opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest as ProtoExportTraceServiceRequest;
13use prost::Message;
14
15pub fn encode_otlp_protobuf(req: &ProtoExportTraceServiceRequest) -> Vec<u8> {
17 req.encode_to_vec()
18}
19
20pub fn encode_otlp_json(req: &ProtoExportTraceServiceRequest) -> serde_json::Result<Vec<u8>> {
22 json_serializer::to_otlp_json_vec(req)
23}
24
25#[derive(Clone, Debug, Default)]
31#[non_exhaustive]
32pub struct OtlpResourceInfo {
33 pub service: String,
34 pub env: String,
35 pub app_version: String,
36 pub language: String,
37 pub tracer_version: String,
38 pub runtime_id: String,
39 pub hostname: String,
40 pub process_tags: String,
41 pub instrumentation_scope_name: String,
42 pub instrumentation_scope_version: String,
43 pub client_computed_stats: bool,
46}
47
48#[cfg(test)]
49mod encode_tests {
50 use super::*;
51 use crate::span::v04::Span;
52 use crate::span::BytesData;
53 use libdd_trace_protobuf::opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest as ProtoReq;
54 use libdd_trace_protobuf::opentelemetry::proto::common::v1::any_value::Value as ProtoValue;
55 use prost::Message;
56
57 fn sample_native() -> (Vec<Vec<Span<BytesData>>>, OtlpResourceInfo) {
58 let resource_info = OtlpResourceInfo {
59 service: "svc".to_string(),
60 ..Default::default()
61 };
62 let mut span: Span<BytesData> = Span {
63 trace_id: 0x5b8efff798038103_d269b633813fc60c_u128,
64 span_id: 0xEEE19B7EC3C1B174,
65 name: libdd_tinybytes::BytesString::from_static("op"),
66 resource: libdd_tinybytes::BytesString::from_static("res"),
67 start: 1,
68 duration: 2,
69 error: 1,
70 ..Default::default()
71 };
72 span.meta.insert(
73 "error.msg".into(),
74 libdd_tinybytes::BytesString::from_static("boom"),
75 );
76 span.meta.insert(
77 "http.method".into(),
78 libdd_tinybytes::BytesString::from_static("GET"),
79 );
80 (vec![vec![span]], resource_info)
81 }
82
83 #[test]
84 fn json_and_protobuf_carry_same_span() {
85 let (chunks, info) = sample_native();
88 let req = map_traces_to_otlp(chunks, &info, false);
89 let json = encode_otlp_json(&req).unwrap();
90 let pb = encode_otlp_protobuf(&req);
91
92 let jv: serde_json::Value = serde_json::from_slice(&json).unwrap();
93 let jspan = &jv["resourceSpans"][0]["scopeSpans"][0]["spans"][0];
94 let proto = ProtoReq::decode(pb.as_slice()).unwrap();
95 let pspan = &proto.resource_spans[0].scope_spans[0].spans[0];
96
97 assert_eq!(jspan["name"].as_str().unwrap(), pspan.name);
98 assert_eq!(
99 jspan["spanId"].as_str().unwrap(),
100 hex::encode(&pspan.span_id)
101 );
102 assert_eq!(
103 jspan["traceId"].as_str().unwrap(),
104 hex::encode(&pspan.trace_id)
105 );
106 let pst = pspan.status.as_ref().unwrap();
107 assert_eq!(jspan["status"]["code"].as_i64().unwrap() as i32, pst.code);
108 assert_eq!(jspan["status"]["message"].as_str().unwrap(), pst.message);
109 let jattr = jspan["attributes"]
110 .as_array()
111 .unwrap()
112 .iter()
113 .find(|a| a["key"] == "http.method")
114 .unwrap();
115 let pattr = pspan
116 .attributes
117 .iter()
118 .find(|a| a.key == "http.method")
119 .unwrap();
120 let pval = match pattr.value.as_ref().unwrap().value.as_ref().unwrap() {
121 ProtoValue::StringValue(v) => v.as_str(),
122 other => panic!("expected string, got {other:?}"),
123 };
124 assert_eq!(jattr["value"]["stringValue"].as_str().unwrap(), pval);
125 assert_eq!(jattr["value"]["stringValue"].as_str().unwrap(), "GET");
126 }
127
128 #[test]
129 fn protobuf_round_trips_through_prost() {
130 let (chunks, info) = sample_native();
135 let req = map_traces_to_otlp(chunks, &info, false);
136 let decoded = ProtoReq::decode(encode_otlp_protobuf(&req).as_slice()).unwrap();
137 assert_eq!(decoded, req);
138 }
139}