pub(crate) mod json_serializer;
pub mod mapper;
pub use mapper::map_traces_to_otlp;
pub use libdd_trace_protobuf::opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest as ProtoExportTraceServiceRequest;
use prost::Message;
pub fn encode_otlp_protobuf(req: &ProtoExportTraceServiceRequest) -> Vec<u8> {
req.encode_to_vec()
}
pub fn encode_otlp_json(req: &ProtoExportTraceServiceRequest) -> serde_json::Result<Vec<u8>> {
json_serializer::to_otlp_json_vec(req)
}
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct OtlpResourceInfo {
pub service: String,
pub env: String,
pub app_version: String,
pub language: String,
pub tracer_version: String,
pub runtime_id: String,
pub hostname: String,
pub process_tags: String,
pub tracer_tags: Vec<String>,
pub instrumentation_scope_name: String,
pub instrumentation_scope_version: String,
pub client_computed_stats: bool,
}
#[cfg(test)]
mod encode_tests {
use super::*;
use crate::span::v04::Span;
use crate::span::BytesData;
use libdd_trace_protobuf::opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest as ProtoReq;
use libdd_trace_protobuf::opentelemetry::proto::common::v1::any_value::Value as ProtoValue;
use prost::Message;
fn sample_native() -> (Vec<Vec<Span<BytesData>>>, OtlpResourceInfo) {
let resource_info = OtlpResourceInfo {
service: "svc".to_string(),
..Default::default()
};
let mut span: Span<BytesData> = Span {
trace_id: 0x5b8efff798038103_d269b633813fc60c_u128,
span_id: 0xEEE19B7EC3C1B174,
name: libdd_tinybytes::BytesString::from_static("op"),
resource: libdd_tinybytes::BytesString::from_static("res"),
start: 1,
duration: 2,
error: 1,
..Default::default()
};
span.meta.insert(
"error.msg".into(),
libdd_tinybytes::BytesString::from_static("boom"),
);
span.meta.insert(
"http.method".into(),
libdd_tinybytes::BytesString::from_static("GET"),
);
(vec![vec![span]], resource_info)
}
#[test]
fn json_and_protobuf_carry_same_span() {
let (chunks, info) = sample_native();
let req = map_traces_to_otlp(chunks, &info, false);
let json = encode_otlp_json(&req).unwrap();
let pb = encode_otlp_protobuf(&req);
let jv: serde_json::Value = serde_json::from_slice(&json).unwrap();
let jspan = &jv["resourceSpans"][0]["scopeSpans"][0]["spans"][0];
let proto = ProtoReq::decode(pb.as_slice()).unwrap();
let pspan = &proto.resource_spans[0].scope_spans[0].spans[0];
assert_eq!(jspan["name"].as_str().unwrap(), pspan.name);
assert_eq!(
jspan["spanId"].as_str().unwrap(),
hex::encode(&pspan.span_id)
);
assert_eq!(
jspan["traceId"].as_str().unwrap(),
hex::encode(&pspan.trace_id)
);
let pst = pspan.status.as_ref().unwrap();
assert_eq!(jspan["status"]["code"].as_i64().unwrap() as i32, pst.code);
assert_eq!(jspan["status"]["message"].as_str().unwrap(), pst.message);
let jattr = jspan["attributes"]
.as_array()
.unwrap()
.iter()
.find(|a| a["key"] == "http.method")
.unwrap();
let pattr = pspan
.attributes
.iter()
.find(|a| a.key == "http.method")
.unwrap();
let pval = match pattr.value.as_ref().unwrap().value.as_ref().unwrap() {
ProtoValue::StringValue(v) => v.as_str(),
other => panic!("expected string, got {other:?}"),
};
assert_eq!(jattr["value"]["stringValue"].as_str().unwrap(), pval);
assert_eq!(jattr["value"]["stringValue"].as_str().unwrap(), "GET");
}
#[test]
fn protobuf_round_trips_through_prost() {
let (chunks, info) = sample_native();
let req = map_traces_to_otlp(chunks, &info, false);
let decoded = ProtoReq::decode(encode_otlp_protobuf(&req).as_slice()).unwrap();
assert_eq!(decoded, req);
}
}