datadog_api_client/datadogV2/api/
api_spans.rs

1// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License.
2// This product includes software developed at Datadog (https://www.datadoghq.com/).
3// Copyright 2019-Present Datadog, Inc.
4use crate::datadog;
5use async_stream::try_stream;
6use flate2::{
7    write::{GzEncoder, ZlibEncoder},
8    Compression,
9};
10use futures_core::stream::Stream;
11use reqwest::header::{HeaderMap, HeaderValue};
12use serde::{Deserialize, Serialize};
13use std::io::Write;
14
15/// ListSpansGetOptionalParams is a struct for passing parameters to the method [`SpansAPI::list_spans_get`]
16#[non_exhaustive]
17#[derive(Clone, Default, Debug)]
18pub struct ListSpansGetOptionalParams {
19    /// Search query following spans syntax.
20    pub filter_query: Option<String>,
21    /// Minimum timestamp for requested spans. Supports date-time ISO8601, date math, and regular timestamps (milliseconds).
22    pub filter_from: Option<String>,
23    /// Maximum timestamp for requested spans. Supports date-time ISO8601, date math, and regular timestamps (milliseconds).
24    pub filter_to: Option<String>,
25    /// Order of spans in results.
26    pub sort: Option<crate::datadogV2::model::SpansSort>,
27    /// List following results with a cursor provided in the previous query.
28    pub page_cursor: Option<String>,
29    /// Maximum number of spans in the response.
30    pub page_limit: Option<i32>,
31}
32
33impl ListSpansGetOptionalParams {
34    /// Search query following spans syntax.
35    pub fn filter_query(mut self, value: String) -> Self {
36        self.filter_query = Some(value);
37        self
38    }
39    /// Minimum timestamp for requested spans. Supports date-time ISO8601, date math, and regular timestamps (milliseconds).
40    pub fn filter_from(mut self, value: String) -> Self {
41        self.filter_from = Some(value);
42        self
43    }
44    /// Maximum timestamp for requested spans. Supports date-time ISO8601, date math, and regular timestamps (milliseconds).
45    pub fn filter_to(mut self, value: String) -> Self {
46        self.filter_to = Some(value);
47        self
48    }
49    /// Order of spans in results.
50    pub fn sort(mut self, value: crate::datadogV2::model::SpansSort) -> Self {
51        self.sort = Some(value);
52        self
53    }
54    /// List following results with a cursor provided in the previous query.
55    pub fn page_cursor(mut self, value: String) -> Self {
56        self.page_cursor = Some(value);
57        self
58    }
59    /// Maximum number of spans in the response.
60    pub fn page_limit(mut self, value: i32) -> Self {
61        self.page_limit = Some(value);
62        self
63    }
64}
65
66/// AggregateSpansError is a struct for typed errors of method [`SpansAPI::aggregate_spans`]
67#[derive(Debug, Clone, Serialize, Deserialize)]
68#[serde(untagged)]
69pub enum AggregateSpansError {
70    APIErrorResponse(crate::datadogV2::model::APIErrorResponse),
71    UnknownValue(serde_json::Value),
72}
73
74/// ListSpansError is a struct for typed errors of method [`SpansAPI::list_spans`]
75#[derive(Debug, Clone, Serialize, Deserialize)]
76#[serde(untagged)]
77pub enum ListSpansError {
78    JSONAPIErrorResponse(crate::datadogV2::model::JSONAPIErrorResponse),
79    UnknownValue(serde_json::Value),
80}
81
82/// ListSpansGetError is a struct for typed errors of method [`SpansAPI::list_spans_get`]
83#[derive(Debug, Clone, Serialize, Deserialize)]
84#[serde(untagged)]
85pub enum ListSpansGetError {
86    JSONAPIErrorResponse(crate::datadogV2::model::JSONAPIErrorResponse),
87    UnknownValue(serde_json::Value),
88}
89
90/// Search and aggregate your spans from your Datadog platform over HTTP.
91#[derive(Debug, Clone)]
92pub struct SpansAPI {
93    config: datadog::Configuration,
94    client: reqwest_middleware::ClientWithMiddleware,
95}
96
97impl Default for SpansAPI {
98    fn default() -> Self {
99        Self::with_config(datadog::Configuration::default())
100    }
101}
102
103impl SpansAPI {
104    pub fn new() -> Self {
105        Self::default()
106    }
107    pub fn with_config(config: datadog::Configuration) -> Self {
108        let mut reqwest_client_builder = reqwest::Client::builder();
109
110        if let Some(proxy_url) = &config.proxy_url {
111            let proxy = reqwest::Proxy::all(proxy_url).expect("Failed to parse proxy URL");
112            reqwest_client_builder = reqwest_client_builder.proxy(proxy);
113        }
114
115        let mut middleware_client_builder =
116            reqwest_middleware::ClientBuilder::new(reqwest_client_builder.build().unwrap());
117
118        if config.enable_retry {
119            struct RetryableStatus;
120            impl reqwest_retry::RetryableStrategy for RetryableStatus {
121                fn handle(
122                    &self,
123                    res: &Result<reqwest::Response, reqwest_middleware::Error>,
124                ) -> Option<reqwest_retry::Retryable> {
125                    match res {
126                        Ok(success) => reqwest_retry::default_on_request_success(success),
127                        Err(_) => None,
128                    }
129                }
130            }
131            let backoff_policy = reqwest_retry::policies::ExponentialBackoff::builder()
132                .build_with_max_retries(config.max_retries);
133
134            let retry_middleware =
135                reqwest_retry::RetryTransientMiddleware::new_with_policy_and_strategy(
136                    backoff_policy,
137                    RetryableStatus,
138                );
139
140            middleware_client_builder = middleware_client_builder.with(retry_middleware);
141        }
142
143        let client = middleware_client_builder.build();
144
145        Self { config, client }
146    }
147
148    pub fn with_client_and_config(
149        config: datadog::Configuration,
150        client: reqwest_middleware::ClientWithMiddleware,
151    ) -> Self {
152        Self { config, client }
153    }
154
155    /// The API endpoint to aggregate spans into buckets and compute metrics and timeseries.
156    /// This endpoint is rate limited to `300` requests per hour.
157    pub async fn aggregate_spans(
158        &self,
159        body: crate::datadogV2::model::SpansAggregateRequest,
160    ) -> Result<crate::datadogV2::model::SpansAggregateResponse, datadog::Error<AggregateSpansError>>
161    {
162        match self.aggregate_spans_with_http_info(body).await {
163            Ok(response_content) => {
164                if let Some(e) = response_content.entity {
165                    Ok(e)
166                } else {
167                    Err(datadog::Error::Serde(serde::de::Error::custom(
168                        "response content was None",
169                    )))
170                }
171            }
172            Err(err) => Err(err),
173        }
174    }
175
176    /// The API endpoint to aggregate spans into buckets and compute metrics and timeseries.
177    /// This endpoint is rate limited to `300` requests per hour.
178    pub async fn aggregate_spans_with_http_info(
179        &self,
180        body: crate::datadogV2::model::SpansAggregateRequest,
181    ) -> Result<
182        datadog::ResponseContent<crate::datadogV2::model::SpansAggregateResponse>,
183        datadog::Error<AggregateSpansError>,
184    > {
185        let local_configuration = &self.config;
186        let operation_id = "v2.aggregate_spans";
187
188        let local_client = &self.client;
189
190        let local_uri_str = format!(
191            "{}/api/v2/spans/analytics/aggregate",
192            local_configuration.get_operation_host(operation_id)
193        );
194        let mut local_req_builder =
195            local_client.request(reqwest::Method::POST, local_uri_str.as_str());
196
197        // build headers
198        let mut headers = HeaderMap::new();
199        headers.insert("Content-Type", HeaderValue::from_static("application/json"));
200        headers.insert("Accept", HeaderValue::from_static("application/json"));
201
202        // build user agent
203        match HeaderValue::from_str(local_configuration.user_agent.as_str()) {
204            Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent),
205            Err(e) => {
206                log::warn!("Failed to parse user agent header: {e}, falling back to default");
207                headers.insert(
208                    reqwest::header::USER_AGENT,
209                    HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()),
210                )
211            }
212        };
213
214        // build auth
215        if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") {
216            headers.insert(
217                "DD-API-KEY",
218                HeaderValue::from_str(local_key.key.as_str())
219                    .expect("failed to parse DD-API-KEY header"),
220            );
221        };
222        if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") {
223            headers.insert(
224                "DD-APPLICATION-KEY",
225                HeaderValue::from_str(local_key.key.as_str())
226                    .expect("failed to parse DD-APPLICATION-KEY header"),
227            );
228        };
229
230        // build body parameters
231        let output = Vec::new();
232        let mut ser = serde_json::Serializer::with_formatter(output, datadog::DDFormatter);
233        if body.serialize(&mut ser).is_ok() {
234            if let Some(content_encoding) = headers.get("Content-Encoding") {
235                match content_encoding.to_str().unwrap_or_default() {
236                    "gzip" => {
237                        let mut enc = GzEncoder::new(Vec::new(), Compression::default());
238                        let _ = enc.write_all(ser.into_inner().as_slice());
239                        match enc.finish() {
240                            Ok(buf) => {
241                                local_req_builder = local_req_builder.body(buf);
242                            }
243                            Err(e) => return Err(datadog::Error::Io(e)),
244                        }
245                    }
246                    "deflate" => {
247                        let mut enc = ZlibEncoder::new(Vec::new(), Compression::default());
248                        let _ = enc.write_all(ser.into_inner().as_slice());
249                        match enc.finish() {
250                            Ok(buf) => {
251                                local_req_builder = local_req_builder.body(buf);
252                            }
253                            Err(e) => return Err(datadog::Error::Io(e)),
254                        }
255                    }
256                    "zstd1" => {
257                        let mut enc = zstd::stream::Encoder::new(Vec::new(), 0).unwrap();
258                        let _ = enc.write_all(ser.into_inner().as_slice());
259                        match enc.finish() {
260                            Ok(buf) => {
261                                local_req_builder = local_req_builder.body(buf);
262                            }
263                            Err(e) => return Err(datadog::Error::Io(e)),
264                        }
265                    }
266                    _ => {
267                        local_req_builder = local_req_builder.body(ser.into_inner());
268                    }
269                }
270            } else {
271                local_req_builder = local_req_builder.body(ser.into_inner());
272            }
273        }
274
275        local_req_builder = local_req_builder.headers(headers);
276        let local_req = local_req_builder.build()?;
277        log::debug!("request content: {:?}", local_req.body());
278        let local_resp = local_client.execute(local_req).await?;
279
280        let local_status = local_resp.status();
281        let local_content = local_resp.text().await?;
282        log::debug!("response content: {}", local_content);
283
284        if !local_status.is_client_error() && !local_status.is_server_error() {
285            match serde_json::from_str::<crate::datadogV2::model::SpansAggregateResponse>(
286                &local_content,
287            ) {
288                Ok(e) => {
289                    return Ok(datadog::ResponseContent {
290                        status: local_status,
291                        content: local_content,
292                        entity: Some(e),
293                    })
294                }
295                Err(e) => return Err(datadog::Error::Serde(e)),
296            };
297        } else {
298            let local_entity: Option<AggregateSpansError> =
299                serde_json::from_str(&local_content).ok();
300            let local_error = datadog::ResponseContent {
301                status: local_status,
302                content: local_content,
303                entity: local_entity,
304            };
305            Err(datadog::Error::ResponseError(local_error))
306        }
307    }
308
309    /// List endpoint returns spans that match a span search query.
310    /// [Results are paginated][1].
311    ///
312    /// Use this endpoint to build complex spans filtering and search.
313    /// This endpoint is rate limited to `300` requests per hour.
314    ///
315    /// [1]: /logs/guide/collect-multiple-logs-with-pagination?tab=v2api
316    pub async fn list_spans(
317        &self,
318        body: crate::datadogV2::model::SpansListRequest,
319    ) -> Result<crate::datadogV2::model::SpansListResponse, datadog::Error<ListSpansError>> {
320        match self.list_spans_with_http_info(body).await {
321            Ok(response_content) => {
322                if let Some(e) = response_content.entity {
323                    Ok(e)
324                } else {
325                    Err(datadog::Error::Serde(serde::de::Error::custom(
326                        "response content was None",
327                    )))
328                }
329            }
330            Err(err) => Err(err),
331        }
332    }
333
334    pub fn list_spans_with_pagination(
335        &self,
336        mut body: crate::datadogV2::model::SpansListRequest,
337    ) -> impl Stream<Item = Result<crate::datadogV2::model::Span, datadog::Error<ListSpansError>>> + '_
338    {
339        try_stream! {
340            let mut page_size: i32 = 10;
341            if body.data.is_none() {
342                body.data = Some(crate::datadogV2::model::SpansListRequestData::new());
343            }
344            if body.data.as_ref().unwrap().attributes.is_none() {
345                body.data.as_mut().unwrap().attributes = Some(crate::datadogV2::model::SpansListRequestAttributes::new());
346            }
347            if body.data.as_ref().unwrap().attributes.as_ref().unwrap().page.is_none() {
348                body.data.as_mut().unwrap().attributes.as_mut().unwrap().page = Some(crate::datadogV2::model::SpansListRequestPage::new());
349            }
350            if body.data.as_ref().unwrap().attributes.as_ref().unwrap().page.as_ref().unwrap().limit.is_none() {
351                body.data.as_mut().unwrap().attributes.as_mut().unwrap().page.as_mut().unwrap().limit = Some(page_size);
352            } else {
353                page_size = body.data.as_ref().unwrap().attributes.as_ref().unwrap().page.as_ref().unwrap().limit.unwrap().clone();
354            }
355            loop {
356                let resp = self.list_spans( body.clone(),).await?;
357                let Some(data) = resp.data else { break };
358
359                let r = data;
360                let count = r.len();
361                for team in r {
362                    yield team;
363                }
364
365                if count < page_size as usize {
366                    break;
367                }
368                let Some(meta) = resp.meta else { break };
369                let Some(page) = meta.page else { break };
370                let Some(after) = page.after else { break };
371
372                body.data.as_mut().unwrap().attributes.as_mut().unwrap().page.as_mut().unwrap().cursor = Some(after);
373            }
374        }
375    }
376
377    /// List endpoint returns spans that match a span search query.
378    /// [Results are paginated][1].
379    ///
380    /// Use this endpoint to build complex spans filtering and search.
381    /// This endpoint is rate limited to `300` requests per hour.
382    ///
383    /// [1]: /logs/guide/collect-multiple-logs-with-pagination?tab=v2api
384    pub async fn list_spans_with_http_info(
385        &self,
386        body: crate::datadogV2::model::SpansListRequest,
387    ) -> Result<
388        datadog::ResponseContent<crate::datadogV2::model::SpansListResponse>,
389        datadog::Error<ListSpansError>,
390    > {
391        let local_configuration = &self.config;
392        let operation_id = "v2.list_spans";
393
394        let local_client = &self.client;
395
396        let local_uri_str = format!(
397            "{}/api/v2/spans/events/search",
398            local_configuration.get_operation_host(operation_id)
399        );
400        let mut local_req_builder =
401            local_client.request(reqwest::Method::POST, local_uri_str.as_str());
402
403        // build headers
404        let mut headers = HeaderMap::new();
405        headers.insert("Content-Type", HeaderValue::from_static("application/json"));
406        headers.insert("Accept", HeaderValue::from_static("application/json"));
407
408        // build user agent
409        match HeaderValue::from_str(local_configuration.user_agent.as_str()) {
410            Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent),
411            Err(e) => {
412                log::warn!("Failed to parse user agent header: {e}, falling back to default");
413                headers.insert(
414                    reqwest::header::USER_AGENT,
415                    HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()),
416                )
417            }
418        };
419
420        // build auth
421        if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") {
422            headers.insert(
423                "DD-API-KEY",
424                HeaderValue::from_str(local_key.key.as_str())
425                    .expect("failed to parse DD-API-KEY header"),
426            );
427        };
428        if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") {
429            headers.insert(
430                "DD-APPLICATION-KEY",
431                HeaderValue::from_str(local_key.key.as_str())
432                    .expect("failed to parse DD-APPLICATION-KEY header"),
433            );
434        };
435
436        // build body parameters
437        let output = Vec::new();
438        let mut ser = serde_json::Serializer::with_formatter(output, datadog::DDFormatter);
439        if body.serialize(&mut ser).is_ok() {
440            if let Some(content_encoding) = headers.get("Content-Encoding") {
441                match content_encoding.to_str().unwrap_or_default() {
442                    "gzip" => {
443                        let mut enc = GzEncoder::new(Vec::new(), Compression::default());
444                        let _ = enc.write_all(ser.into_inner().as_slice());
445                        match enc.finish() {
446                            Ok(buf) => {
447                                local_req_builder = local_req_builder.body(buf);
448                            }
449                            Err(e) => return Err(datadog::Error::Io(e)),
450                        }
451                    }
452                    "deflate" => {
453                        let mut enc = ZlibEncoder::new(Vec::new(), Compression::default());
454                        let _ = enc.write_all(ser.into_inner().as_slice());
455                        match enc.finish() {
456                            Ok(buf) => {
457                                local_req_builder = local_req_builder.body(buf);
458                            }
459                            Err(e) => return Err(datadog::Error::Io(e)),
460                        }
461                    }
462                    "zstd1" => {
463                        let mut enc = zstd::stream::Encoder::new(Vec::new(), 0).unwrap();
464                        let _ = enc.write_all(ser.into_inner().as_slice());
465                        match enc.finish() {
466                            Ok(buf) => {
467                                local_req_builder = local_req_builder.body(buf);
468                            }
469                            Err(e) => return Err(datadog::Error::Io(e)),
470                        }
471                    }
472                    _ => {
473                        local_req_builder = local_req_builder.body(ser.into_inner());
474                    }
475                }
476            } else {
477                local_req_builder = local_req_builder.body(ser.into_inner());
478            }
479        }
480
481        local_req_builder = local_req_builder.headers(headers);
482        let local_req = local_req_builder.build()?;
483        log::debug!("request content: {:?}", local_req.body());
484        let local_resp = local_client.execute(local_req).await?;
485
486        let local_status = local_resp.status();
487        let local_content = local_resp.text().await?;
488        log::debug!("response content: {}", local_content);
489
490        if !local_status.is_client_error() && !local_status.is_server_error() {
491            match serde_json::from_str::<crate::datadogV2::model::SpansListResponse>(&local_content)
492            {
493                Ok(e) => {
494                    return Ok(datadog::ResponseContent {
495                        status: local_status,
496                        content: local_content,
497                        entity: Some(e),
498                    })
499                }
500                Err(e) => return Err(datadog::Error::Serde(e)),
501            };
502        } else {
503            let local_entity: Option<ListSpansError> = serde_json::from_str(&local_content).ok();
504            let local_error = datadog::ResponseContent {
505                status: local_status,
506                content: local_content,
507                entity: local_entity,
508            };
509            Err(datadog::Error::ResponseError(local_error))
510        }
511    }
512
513    /// List endpoint returns spans that match a span search query.
514    /// [Results are paginated][1].
515    ///
516    /// Use this endpoint to see your latest spans.
517    /// This endpoint is rate limited to `300` requests per hour.
518    ///
519    /// [1]: /logs/guide/collect-multiple-logs-with-pagination?tab=v2api
520    pub async fn list_spans_get(
521        &self,
522        params: ListSpansGetOptionalParams,
523    ) -> Result<crate::datadogV2::model::SpansListResponse, datadog::Error<ListSpansGetError>> {
524        match self.list_spans_get_with_http_info(params).await {
525            Ok(response_content) => {
526                if let Some(e) = response_content.entity {
527                    Ok(e)
528                } else {
529                    Err(datadog::Error::Serde(serde::de::Error::custom(
530                        "response content was None",
531                    )))
532                }
533            }
534            Err(err) => Err(err),
535        }
536    }
537
538    pub fn list_spans_get_with_pagination(
539        &self,
540        mut params: ListSpansGetOptionalParams,
541    ) -> impl Stream<Item = Result<crate::datadogV2::model::Span, datadog::Error<ListSpansGetError>>> + '_
542    {
543        try_stream! {
544            let mut page_size: i32 = 10;
545            if params.page_limit.is_none() {
546                params.page_limit = Some(page_size);
547            } else {
548                page_size = params.page_limit.unwrap().clone();
549            }
550            loop {
551                let resp = self.list_spans_get(params.clone()).await?;
552                let Some(data) = resp.data else { break };
553
554                let r = data;
555                let count = r.len();
556                for team in r {
557                    yield team;
558                }
559
560                if count < page_size as usize {
561                    break;
562                }
563                let Some(meta) = resp.meta else { break };
564                let Some(page) = meta.page else { break };
565                let Some(after) = page.after else { break };
566
567                params.page_cursor = Some(after);
568            }
569        }
570    }
571
572    /// List endpoint returns spans that match a span search query.
573    /// [Results are paginated][1].
574    ///
575    /// Use this endpoint to see your latest spans.
576    /// This endpoint is rate limited to `300` requests per hour.
577    ///
578    /// [1]: /logs/guide/collect-multiple-logs-with-pagination?tab=v2api
579    pub async fn list_spans_get_with_http_info(
580        &self,
581        params: ListSpansGetOptionalParams,
582    ) -> Result<
583        datadog::ResponseContent<crate::datadogV2::model::SpansListResponse>,
584        datadog::Error<ListSpansGetError>,
585    > {
586        let local_configuration = &self.config;
587        let operation_id = "v2.list_spans_get";
588
589        // unbox and build optional parameters
590        let filter_query = params.filter_query;
591        let filter_from = params.filter_from;
592        let filter_to = params.filter_to;
593        let sort = params.sort;
594        let page_cursor = params.page_cursor;
595        let page_limit = params.page_limit;
596
597        let local_client = &self.client;
598
599        let local_uri_str = format!(
600            "{}/api/v2/spans/events",
601            local_configuration.get_operation_host(operation_id)
602        );
603        let mut local_req_builder =
604            local_client.request(reqwest::Method::GET, local_uri_str.as_str());
605
606        if let Some(ref local_query_param) = filter_query {
607            local_req_builder =
608                local_req_builder.query(&[("filter[query]", &local_query_param.to_string())]);
609        };
610        if let Some(ref local_query_param) = filter_from {
611            local_req_builder =
612                local_req_builder.query(&[("filter[from]", &local_query_param.to_string())]);
613        };
614        if let Some(ref local_query_param) = filter_to {
615            local_req_builder =
616                local_req_builder.query(&[("filter[to]", &local_query_param.to_string())]);
617        };
618        if let Some(ref local_query_param) = sort {
619            local_req_builder =
620                local_req_builder.query(&[("sort", &local_query_param.to_string())]);
621        };
622        if let Some(ref local_query_param) = page_cursor {
623            local_req_builder =
624                local_req_builder.query(&[("page[cursor]", &local_query_param.to_string())]);
625        };
626        if let Some(ref local_query_param) = page_limit {
627            local_req_builder =
628                local_req_builder.query(&[("page[limit]", &local_query_param.to_string())]);
629        };
630
631        // build headers
632        let mut headers = HeaderMap::new();
633        headers.insert("Accept", HeaderValue::from_static("application/json"));
634
635        // build user agent
636        match HeaderValue::from_str(local_configuration.user_agent.as_str()) {
637            Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent),
638            Err(e) => {
639                log::warn!("Failed to parse user agent header: {e}, falling back to default");
640                headers.insert(
641                    reqwest::header::USER_AGENT,
642                    HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()),
643                )
644            }
645        };
646
647        // build auth
648        if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") {
649            headers.insert(
650                "DD-API-KEY",
651                HeaderValue::from_str(local_key.key.as_str())
652                    .expect("failed to parse DD-API-KEY header"),
653            );
654        };
655        if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") {
656            headers.insert(
657                "DD-APPLICATION-KEY",
658                HeaderValue::from_str(local_key.key.as_str())
659                    .expect("failed to parse DD-APPLICATION-KEY header"),
660            );
661        };
662
663        local_req_builder = local_req_builder.headers(headers);
664        let local_req = local_req_builder.build()?;
665        log::debug!("request content: {:?}", local_req.body());
666        let local_resp = local_client.execute(local_req).await?;
667
668        let local_status = local_resp.status();
669        let local_content = local_resp.text().await?;
670        log::debug!("response content: {}", local_content);
671
672        if !local_status.is_client_error() && !local_status.is_server_error() {
673            match serde_json::from_str::<crate::datadogV2::model::SpansListResponse>(&local_content)
674            {
675                Ok(e) => {
676                    return Ok(datadog::ResponseContent {
677                        status: local_status,
678                        content: local_content,
679                        entity: Some(e),
680                    })
681                }
682                Err(e) => return Err(datadog::Error::Serde(e)),
683            };
684        } else {
685            let local_entity: Option<ListSpansGetError> = serde_json::from_str(&local_content).ok();
686            let local_error = datadog::ResponseContent {
687                status: local_status,
688                content: local_content,
689                entity: local_entity,
690            };
691            Err(datadog::Error::ResponseError(local_error))
692        }
693    }
694}