datadog_api/apis/traces.rs
1use crate::{
2 client::DatadogClient,
3 models::{
4 ServiceDependencies, ServiceStats, Span, Trace, TraceQuery, TraceSearchResponse,
5 TraceSubmitRequest, TraceSubmitResponse,
6 },
7 Result,
8};
9use serde::Serialize;
10
11/// API client for Datadog APM/Traces endpoints.
12///
13/// Enables distributed tracing, service performance monitoring, and dependency mapping.
14pub struct TracesApi {
15 client: DatadogClient,
16}
17
18impl TracesApi {
19 /// Creates a new traces API client.
20 #[must_use]
21 pub const fn new(client: DatadogClient) -> Self {
22 Self { client }
23 }
24
25 /// Submit traces to Datadog APM.
26 ///
27 /// # Arguments
28 ///
29 /// * `traces` - Array of traces (each trace is an array of spans)
30 ///
31 /// # Errors
32 ///
33 /// Returns an error if the API request fails.
34 ///
35 /// # Example
36 ///
37 /// ```no_run
38 /// use datadog_api::{DatadogClient, DatadogConfig, models::Span};
39 /// use datadog_api::apis::TracesApi;
40 ///
41 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
42 /// let config = DatadogConfig::from_env()?;
43 /// let client = DatadogClient::new(config)?;
44 /// let traces_api = TracesApi::new(client);
45 ///
46 /// let span = Span {
47 /// span_id: 12345,
48 /// trace_id: 67890,
49 /// parent_id: 0,
50 /// service: "my-service".to_string(),
51 /// resource: "GET /api/users".to_string(),
52 /// name: "web.request".to_string(),
53 /// start: 1234567890000000000,
54 /// duration: 15000000,
55 /// error: 0,
56 /// meta: Default::default(),
57 /// metrics: Default::default(),
58 /// span_type: Some("web".to_string()),
59 /// };
60 ///
61 /// traces_api.send_traces(vec![vec![span]]).await?;
62 /// # Ok(())
63 /// # }
64 /// ```
65 pub async fn send_traces(&self, traces: Vec<Vec<Span>>) -> Result<TraceSubmitResponse> {
66 let request = TraceSubmitRequest { traces };
67 self.client.post("/v0.4/traces", &request).await
68 }
69
70 /// Search for traces matching the given criteria.
71 ///
72 /// # Errors
73 ///
74 /// Returns an error if the API request fails.
75 pub async fn search_traces(&self, query: &TraceQuery) -> Result<TraceSearchResponse> {
76 self.client.get_with_query("/api/v2/traces", query).await
77 }
78
79 /// Get a specific trace by ID.
80 ///
81 /// # Errors
82 ///
83 /// Returns an error if the API request fails.
84 pub async fn get_trace(&self, trace_id: &str) -> Result<Trace> {
85 let endpoint = format!("/api/v2/traces/{trace_id}");
86 self.client.get(&endpoint).await
87 }
88
89 /// Get performance statistics for a service.
90 ///
91 /// # Arguments
92 ///
93 /// * `service` - Service name
94 /// * `start` - Start time (seconds since epoch)
95 /// * `end` - End time (seconds since epoch)
96 ///
97 /// # Errors
98 ///
99 /// Returns an error if the API request fails.
100 pub async fn get_service_stats(
101 &self,
102 service: &str,
103 start: i64,
104 end: i64,
105 ) -> Result<ServiceStats> {
106 #[derive(Serialize)]
107 struct QueryParams<'a> {
108 service: &'a str,
109 start: i64,
110 end: i64,
111 }
112
113 let params = QueryParams {
114 service,
115 start,
116 end,
117 };
118
119 self.client
120 .get_with_query("/api/v2/apm/stats", ¶ms)
121 .await
122 }
123
124 /// Get service dependencies (which services call which).
125 ///
126 /// # Arguments
127 ///
128 /// * `service` - Service name
129 /// * `start` - Start time (seconds since epoch)
130 /// * `end` - End time (seconds since epoch)
131 ///
132 /// # Errors
133 ///
134 /// Returns an error if the API request fails.
135 pub async fn get_service_dependencies(
136 &self,
137 service: &str,
138 start: i64,
139 end: i64,
140 ) -> Result<ServiceDependencies> {
141 #[derive(Serialize)]
142 struct QueryParams<'a> {
143 service: &'a str,
144 start: i64,
145 end: i64,
146 }
147
148 let params = QueryParams {
149 service,
150 start,
151 end,
152 };
153
154 self.client
155 .get_with_query("/api/v2/apm/dependencies", ¶ms)
156 .await
157 }
158
159 /// List all services that have sent traces.
160 ///
161 /// # Arguments
162 ///
163 /// * `start` - Start time (seconds since epoch)
164 /// * `end` - End time (seconds since epoch)
165 ///
166 /// # Errors
167 ///
168 /// Returns an error if the API request fails.
169 pub async fn list_services(&self, start: i64, end: i64) -> Result<Vec<String>> {
170 #[derive(Serialize)]
171 struct QueryParams {
172 start: i64,
173 end: i64,
174 }
175
176 let params = QueryParams { start, end };
177
178 self.client
179 .get_with_query("/api/v2/apm/services", ¶ms)
180 .await
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use crate::DatadogConfig;
188 use std::collections::HashMap;
189
190 #[test]
191 fn test_traces_api_creation() {
192 let config = DatadogConfig::new("test_key".to_string(), "test_app_key".to_string());
193 let client = DatadogClient::new(config).unwrap();
194 let _traces_api = TracesApi::new(client);
195 }
196
197 #[test]
198 fn test_span_serialization() {
199 let span = Span {
200 span_id: 12345,
201 trace_id: 67890,
202 parent_id: 0,
203 service: "test-service".to_string(),
204 resource: "GET /test".to_string(),
205 name: "web.request".to_string(),
206 start: 1234567890000000000,
207 duration: 15000000,
208 error: 0,
209 meta: HashMap::new(),
210 metrics: HashMap::new(),
211 span_type: Some("web".to_string()),
212 };
213
214 let json = serde_json::to_string(&span).unwrap();
215 assert!(json.contains("test-service"));
216 assert!(json.contains("12345"));
217 }
218
219 #[test]
220 fn test_trace_query_serialization() {
221 let query = TraceQuery {
222 service: Some("my-service".to_string()),
223 operation: Some("web.request".to_string()),
224 resource: None,
225 start: 1234567890,
226 end: 1234567900,
227 limit: Some(100),
228 };
229
230 let json = serde_json::to_string(&query).unwrap();
231 assert!(json.contains("my-service"));
232 assert!(json.contains("web.request"));
233 }
234}