opentelemetry_otlp/exporter/http/
trace.rs

1use std::sync::Arc;
2
3use super::OtlpHttpClient;
4use http::{header::CONTENT_TYPE, Method};
5use opentelemetry::otel_debug;
6use opentelemetry_sdk::{
7    error::{OTelSdkError, OTelSdkResult},
8    trace::{SpanData, SpanExporter},
9};
10
11impl SpanExporter for OtlpHttpClient {
12    async fn export(&self, batch: Vec<SpanData>) -> OTelSdkResult {
13        let client = match self
14            .client
15            .lock()
16            .map_err(|e| OTelSdkError::InternalFailure(format!("Mutex lock failed: {e}")))
17            .and_then(|g| match &*g {
18                Some(client) => Ok(Arc::clone(client)),
19                _ => Err(OTelSdkError::AlreadyShutdown),
20            }) {
21            Ok(client) => client,
22            Err(err) => return Err(err),
23        };
24
25        let (body, content_type, content_encoding) = match self.build_trace_export_body(batch) {
26            Ok(result) => result,
27            Err(e) => return Err(OTelSdkError::InternalFailure(e.to_string())),
28        };
29
30        let mut request_builder = http::Request::builder()
31            .method(Method::POST)
32            .uri(&self.collector_endpoint)
33            .header(CONTENT_TYPE, content_type);
34
35        if let Some(encoding) = content_encoding {
36            request_builder = request_builder.header("Content-Encoding", encoding);
37        }
38
39        let mut request = match request_builder.body(body.into()) {
40            Ok(req) => req,
41            Err(e) => return Err(OTelSdkError::InternalFailure(e.to_string())),
42        };
43
44        for (k, v) in &self.headers {
45            request.headers_mut().insert(k.clone(), v.clone());
46        }
47
48        let request_uri = request.uri().to_string();
49        otel_debug!(name: "HttpTracesClient.ExportStarted");
50        let response = client
51            .send_bytes(request)
52            .await
53            .map_err(|e| OTelSdkError::InternalFailure(format!("{e:?}")))?;
54
55        if !response.status().is_success() {
56            let error = format!(
57                "OpenTelemetry trace export failed. Url: {}, Status Code: {}, Response: {:?}",
58                request_uri,
59                response.status().as_u16(),
60                response.body()
61            );
62            otel_debug!(name: "HttpTracesClient.ExportFailed", error = &error);
63            return Err(OTelSdkError::InternalFailure(error));
64        }
65
66        otel_debug!(name: "HttpTracesClient.ExportSucceeded");
67        Ok(())
68    }
69
70    fn shutdown(&mut self) -> OTelSdkResult {
71        let mut client_guard = self.client.lock().map_err(|e| {
72            OTelSdkError::InternalFailure(format!("Failed to acquire client lock: {e}"))
73        })?;
74
75        if client_guard.take().is_none() {
76            return Err(OTelSdkError::AlreadyShutdown);
77        }
78
79        Ok(())
80    }
81
82    fn set_resource(&mut self, resource: &opentelemetry_sdk::Resource) {
83        self.resource = resource.into();
84    }
85}