1#![doc = include_str!("../README.md")]
4
5use std::collections::HashMap;
6use std::net::SocketAddr;
7
8use fastrace::collector::Reporter;
9use fastrace::prelude::*;
10use rmp_serde::Serializer;
11use serde::Serialize;
12
13pub struct DatadogReporter {
15 agent_addr: SocketAddr,
16 service_name: String,
17 resource: String,
18 trace_type: String,
19}
20
21impl DatadogReporter {
22 pub fn new(
23 agent_addr: SocketAddr,
24 service_name: impl Into<String>,
25 resource: impl Into<String>,
26 trace_type: impl Into<String>,
27 ) -> DatadogReporter {
28 DatadogReporter {
29 agent_addr,
30 service_name: service_name.into(),
31 resource: resource.into(),
32 trace_type: trace_type.into(),
33 }
34 }
35
36 fn convert<'a>(&'a self, spans: &'a [SpanRecord]) -> Vec<DatadogSpan<'a>> {
37 spans
38 .iter()
39 .map(move |s| DatadogSpan {
40 name: &s.name,
41 service: &self.service_name,
42 trace_type: &self.trace_type,
43 resource: &self.resource,
44 start: s.begin_time_unix_ns as i64,
45 duration: s.duration_ns as i64,
46 meta: if s.properties.is_empty() {
47 None
48 } else {
49 Some(
50 s.properties
51 .iter()
52 .map(|(k, v)| (k.as_ref(), v.as_ref()))
53 .collect(),
54 )
55 },
56 error_code: 0,
57 span_id: s.span_id.0,
58 trace_id: s.trace_id.0 as u64,
59 parent_id: s.parent_id.0,
60 })
61 .collect()
62 }
63
64 fn serialize(&self, spans: Vec<DatadogSpan>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
65 let mut buf = vec![0b10010001];
66 spans.serialize(&mut Serializer::new(&mut buf).with_struct_map())?;
67 Ok(buf)
68 }
69
70 fn try_report(&self, spans: Vec<SpanRecord>) -> Result<(), Box<dyn std::error::Error>> {
71 let datadog_spans = self.convert(&spans);
72 let bytes = self.serialize(datadog_spans)?;
73 let client = reqwest::blocking::Client::new();
74 let _rep = client
75 .post(format!("http://{}/v0.4/traces", self.agent_addr))
76 .header("Datadog-Meta-Tracer-Version", "v1.27.0")
77 .header("Content-Type", "application/msgpack")
78 .body(bytes)
79 .send()?;
80 Ok(())
81 }
82}
83
84impl Reporter for DatadogReporter {
85 fn report(&mut self, spans: Vec<SpanRecord>) {
86 if spans.is_empty() {
87 return;
88 }
89
90 if let Err(err) = self.try_report(spans) {
91 log::error!("report to datadog failed: {err}");
92 }
93 }
94}
95
96#[derive(Serialize)]
97struct DatadogSpan<'a> {
98 name: &'a str,
99 service: &'a str,
100 #[serde(rename = "type")]
101 trace_type: &'a str,
102 resource: &'a str,
103 start: i64,
104 duration: i64,
105 #[serde(skip_serializing_if = "Option::is_none")]
106 meta: Option<HashMap<&'a str, &'a str>>,
107 error_code: i32,
108 span_id: u64,
109 trace_id: u64,
110 parent_id: u64,
111}