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 tracer_tags: Vec<String>,
42 pub instrumentation_scope_name: String,
43 pub instrumentation_scope_version: String,
44 pub client_computed_stats: bool,
47}
48
49#[cfg(test)]
50mod encode_tests {
51 use super::*;
52 use crate::span::v04::Span;
53 use crate::span::BytesData;
54 use libdd_trace_protobuf::opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest as ProtoReq;
55 use libdd_trace_protobuf::opentelemetry::proto::common::v1::any_value::Value as ProtoValue;
56 use prost::Message;
57
58 fn sample_native() -> (Vec<Vec<Span<BytesData>>>, OtlpResourceInfo) {
59 let resource_info = OtlpResourceInfo {
60 service: "svc".to_string(),
61 ..Default::default()
62 };
63 let mut span: Span<BytesData> = Span {
64 trace_id: 0x5b8efff798038103_d269b633813fc60c_u128,
65 span_id: 0xEEE19B7EC3C1B174,
66 name: libdd_tinybytes::BytesString::from_static("op"),
67 resource: libdd_tinybytes::BytesString::from_static("res"),
68 start: 1,
69 duration: 2,
70 error: 1,
71 ..Default::default()
72 };
73 span.meta.insert(
74 "error.msg".into(),
75 libdd_tinybytes::BytesString::from_static("boom"),
76 );
77 span.meta.insert(
78 "http.method".into(),
79 libdd_tinybytes::BytesString::from_static("GET"),
80 );
81 (vec![vec![span]], resource_info)
82 }
83
84 #[test]
85 fn json_and_protobuf_carry_same_span() {
86 let (chunks, info) = sample_native();
89 let req = map_traces_to_otlp(chunks, &info, false);
90 let json = encode_otlp_json(&req).unwrap();
91 let pb = encode_otlp_protobuf(&req);
92
93 let jv: serde_json::Value = serde_json::from_slice(&json).unwrap();
94 let jspan = &jv["resourceSpans"][0]["scopeSpans"][0]["spans"][0];
95 let proto = ProtoReq::decode(pb.as_slice()).unwrap();
96 let pspan = &proto.resource_spans[0].scope_spans[0].spans[0];
97
98 assert_eq!(jspan["name"].as_str().unwrap(), pspan.name);
99 assert_eq!(
100 jspan["spanId"].as_str().unwrap(),
101 hex::encode(&pspan.span_id)
102 );
103 assert_eq!(
104 jspan["traceId"].as_str().unwrap(),
105 hex::encode(&pspan.trace_id)
106 );
107 let pst = pspan.status.as_ref().unwrap();
108 assert_eq!(jspan["status"]["code"].as_i64().unwrap() as i32, pst.code);
109 assert_eq!(jspan["status"]["message"].as_str().unwrap(), pst.message);
110 let jattr = jspan["attributes"]
111 .as_array()
112 .unwrap()
113 .iter()
114 .find(|a| a["key"] == "http.method")
115 .unwrap();
116 let pattr = pspan
117 .attributes
118 .iter()
119 .find(|a| a.key == "http.method")
120 .unwrap();
121 let pval = match pattr.value.as_ref().unwrap().value.as_ref().unwrap() {
122 ProtoValue::StringValue(v) => v.as_str(),
123 other => panic!("expected string, got {other:?}"),
124 };
125 assert_eq!(jattr["value"]["stringValue"].as_str().unwrap(), pval);
126 assert_eq!(jattr["value"]["stringValue"].as_str().unwrap(), "GET");
127 }
128
129 #[test]
130 fn protobuf_round_trips_through_prost() {
131 let (chunks, info) = sample_native();
136 let req = map_traces_to_otlp(chunks, &info, false);
137 let decoded = ProtoReq::decode(encode_otlp_protobuf(&req).as_slice()).unwrap();
138 assert_eq!(decoded, req);
139 }
140}