opentelemetry_otlp/
logs.rs

1//! OTLP - Log Exporter
2//!
3//! Defines a [LogExporter] to send logs via the OpenTelemetry Protocol (OTLP)
4
5#[cfg(feature = "grpc-tonic")]
6use crate::exporter::tonic::TonicExporterBuilder;
7
8#[cfg(feature = "http-proto")]
9use crate::exporter::http::HttpExporterBuilder;
10
11use crate::{NoExporterConfig, OtlpPipeline};
12use async_trait::async_trait;
13use std::fmt::Debug;
14
15use opentelemetry::logs::LogError;
16
17use opentelemetry_sdk::{export::logs::LogData, runtime::RuntimeChannel, Resource};
18
19/// Compression algorithm to use, defaults to none.
20pub const OTEL_EXPORTER_OTLP_LOGS_COMPRESSION: &str = "OTEL_EXPORTER_OTLP_LOGS_COMPRESSION";
21
22/// Target to which the exporter is going to send logs
23pub const OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: &str = "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT";
24
25/// Maximum time the OTLP exporter will wait for each batch logs export.
26pub const OTEL_EXPORTER_OTLP_LOGS_TIMEOUT: &str = "OTEL_EXPORTER_OTLP_LOGS_TIMEOUT";
27
28/// Key-value pairs to be used as headers associated with gRPC or HTTP requests
29/// for sending logs.
30/// Example: `k1=v1,k2=v2`
31/// Note: this is only supported for HTTP.
32pub const OTEL_EXPORTER_OTLP_LOGS_HEADERS: &str = "OTEL_EXPORTER_OTLP_LOGS_HEADERS";
33
34impl OtlpPipeline {
35    /// Create a OTLP logging pipeline.
36    pub fn logging(self) -> OtlpLogPipeline<NoExporterConfig> {
37        OtlpLogPipeline {
38            resource: None,
39            exporter_builder: NoExporterConfig(()),
40            batch_config: None,
41        }
42    }
43}
44
45/// OTLP log exporter builder
46#[derive(Debug)]
47#[allow(clippy::large_enum_variant)]
48#[non_exhaustive]
49pub enum LogExporterBuilder {
50    /// Tonic log exporter builder
51    #[cfg(feature = "grpc-tonic")]
52    Tonic(TonicExporterBuilder),
53    /// Http log exporter builder
54    #[cfg(feature = "http-proto")]
55    Http(HttpExporterBuilder),
56}
57
58impl LogExporterBuilder {
59    /// Build a OTLP log exporter using the given configuration.
60    pub fn build_log_exporter(self) -> Result<LogExporter, LogError> {
61        match self {
62            #[cfg(feature = "grpc-tonic")]
63            LogExporterBuilder::Tonic(builder) => builder.build_log_exporter(),
64            #[cfg(feature = "http-proto")]
65            LogExporterBuilder::Http(builder) => builder.build_log_exporter(),
66        }
67    }
68}
69
70#[cfg(feature = "grpc-tonic")]
71impl From<TonicExporterBuilder> for LogExporterBuilder {
72    fn from(exporter: TonicExporterBuilder) -> Self {
73        LogExporterBuilder::Tonic(exporter)
74    }
75}
76
77#[cfg(feature = "http-proto")]
78impl From<HttpExporterBuilder> for LogExporterBuilder {
79    fn from(exporter: HttpExporterBuilder) -> Self {
80        LogExporterBuilder::Http(exporter)
81    }
82}
83
84/// OTLP exporter that sends log data
85#[derive(Debug)]
86pub struct LogExporter {
87    client: Box<dyn opentelemetry_sdk::export::logs::LogExporter>,
88}
89
90impl LogExporter {
91    /// Create a new log exporter
92    pub fn new(client: impl opentelemetry_sdk::export::logs::LogExporter + 'static) -> Self {
93        LogExporter {
94            client: Box::new(client),
95        }
96    }
97}
98
99#[async_trait]
100impl opentelemetry_sdk::export::logs::LogExporter for LogExporter {
101    async fn export<'a>(
102        &mut self,
103        batch: Vec<std::borrow::Cow<'a, LogData>>,
104    ) -> opentelemetry::logs::LogResult<()> {
105        self.client.export(batch).await
106    }
107
108    fn set_resource(&mut self, resource: &opentelemetry_sdk::Resource) {
109        self.client.set_resource(resource);
110    }
111}
112
113/// Recommended configuration for an OTLP exporter pipeline.
114#[derive(Debug)]
115pub struct OtlpLogPipeline<EB> {
116    exporter_builder: EB,
117    resource: Option<Resource>,
118    batch_config: Option<opentelemetry_sdk::logs::BatchConfig>,
119}
120
121impl<EB> OtlpLogPipeline<EB> {
122    /// Set the Resource associated with log provider.
123    pub fn with_resource(self, resource: Resource) -> Self {
124        OtlpLogPipeline {
125            resource: Some(resource),
126            ..self
127        }
128    }
129
130    /// Set the batch log processor configuration, and it will override the env vars.
131    pub fn with_batch_config(mut self, batch_config: opentelemetry_sdk::logs::BatchConfig) -> Self {
132        self.batch_config = Some(batch_config);
133        self
134    }
135}
136
137impl OtlpLogPipeline<NoExporterConfig> {
138    /// Set the OTLP log exporter builder.
139    pub fn with_exporter<B: Into<LogExporterBuilder>>(
140        self,
141        pipeline: B,
142    ) -> OtlpLogPipeline<LogExporterBuilder> {
143        OtlpLogPipeline {
144            exporter_builder: pipeline.into(),
145            resource: self.resource,
146            batch_config: self.batch_config,
147        }
148    }
149}
150
151impl OtlpLogPipeline<LogExporterBuilder> {
152    /// Install the configured log exporter.
153    ///
154    /// Returns a [`LoggerProvider`].
155    ///
156    /// [`LoggerProvider`]: opentelemetry_sdk::logs::LoggerProvider
157    pub fn install_simple(self) -> Result<opentelemetry_sdk::logs::LoggerProvider, LogError> {
158        Ok(build_simple_with_exporter(
159            self.exporter_builder.build_log_exporter()?,
160            self.resource,
161        ))
162    }
163
164    /// Install the configured log exporter and a batch log processor using the
165    /// specified runtime.
166    ///
167    /// Returns a [`LoggerProvider`].
168    ///
169    /// [`LoggerProvider`]: opentelemetry_sdk::logs::LoggerProvider
170    pub fn install_batch<R: RuntimeChannel>(
171        self,
172        runtime: R,
173    ) -> Result<opentelemetry_sdk::logs::LoggerProvider, LogError> {
174        Ok(build_batch_with_exporter(
175            self.exporter_builder.build_log_exporter()?,
176            self.resource,
177            runtime,
178            self.batch_config,
179        ))
180    }
181}
182
183fn build_simple_with_exporter(
184    exporter: LogExporter,
185    resource: Option<Resource>,
186) -> opentelemetry_sdk::logs::LoggerProvider {
187    let mut provider_builder =
188        opentelemetry_sdk::logs::LoggerProvider::builder().with_simple_exporter(exporter);
189    if let Some(resource) = resource {
190        provider_builder = provider_builder.with_resource(resource);
191    }
192    // logger would be created in the appenders like
193    // opentelemetry-appender-tracing, opentelemetry-appender-log etc.
194    provider_builder.build()
195}
196
197fn build_batch_with_exporter<R: RuntimeChannel>(
198    exporter: LogExporter,
199    resource: Option<Resource>,
200    runtime: R,
201    batch_config: Option<opentelemetry_sdk::logs::BatchConfig>,
202) -> opentelemetry_sdk::logs::LoggerProvider {
203    let mut provider_builder = opentelemetry_sdk::logs::LoggerProvider::builder();
204    let batch_processor = opentelemetry_sdk::logs::BatchLogProcessor::builder(exporter, runtime)
205        .with_batch_config(batch_config.unwrap_or_default())
206        .build();
207    provider_builder = provider_builder.with_log_processor(batch_processor);
208
209    if let Some(resource) = resource {
210        provider_builder = provider_builder.with_resource(resource);
211    }
212    // logger would be created in the tracing appender
213    provider_builder.build()
214}