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", &params)
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", &params)
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", &params)
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}