opentelemetry_sdk/logs/
in_memory_exporter.rs

1use crate::error::{OTelSdkError, OTelSdkResult};
2use crate::logs::SdkLogRecord;
3use crate::logs::{LogBatch, LogExporter};
4use crate::InMemoryExporterError;
5use crate::Resource;
6use opentelemetry::InstrumentationScope;
7use std::borrow::Cow;
8use std::sync::atomic::AtomicBool;
9use std::sync::{Arc, Mutex};
10use std::time;
11
12/// An in-memory logs exporter that stores logs data in memory..
13///
14/// This exporter is useful for testing and debugging purposes.
15/// It stores logs in a `Vec<OwnedLogData>`. Logs can be retrieved using
16/// `get_emitted_logs` method.
17///
18/// # Example
19/// ```no_run
20///# use opentelemetry_sdk::logs::{BatchLogProcessor, SdkLoggerProvider};
21///# use opentelemetry_sdk::runtime;
22///# use opentelemetry_sdk::logs::InMemoryLogExporter;
23///
24///# #[tokio::main]
25///# async fn main() {
26///    // Create an InMemoryLogExporter
27///    let exporter: InMemoryLogExporter = InMemoryLogExporter::default();
28///    //Create a LoggerProvider and register the exporter
29///    let logger_provider = SdkLoggerProvider::builder()
30///        .with_log_processor(BatchLogProcessor::builder(exporter.clone()).build())
31///        .build();
32///    // Setup Log Appenders and emit logs. (Not shown here)
33///    logger_provider.force_flush();
34///    let emitted_logs = exporter.get_emitted_logs().unwrap();
35///    for log in emitted_logs {
36///        println!("{:?}", log);
37///    }
38///# }
39/// ```
40///
41#[derive(Clone, Debug)]
42pub struct InMemoryLogExporter {
43    logs: Arc<Mutex<Vec<OwnedLogData>>>,
44    resource: Arc<Mutex<Resource>>,
45    should_reset_on_shutdown: bool,
46    shutdown_called: Arc<AtomicBool>,
47}
48
49impl Default for InMemoryLogExporter {
50    fn default() -> Self {
51        InMemoryLogExporterBuilder::new().build()
52    }
53}
54
55/// `OwnedLogData` represents a single log event without resource context.
56#[derive(Debug, Clone)]
57pub struct OwnedLogData {
58    /// Log record, which can be borrowed or owned.
59    pub record: SdkLogRecord,
60    /// Instrumentation details for the emitter who produced this `LogEvent`.
61    pub instrumentation: InstrumentationScope,
62}
63
64/// `LogDataWithResource` associates a [`SdkLogRecord`] with a [`Resource`] and
65/// [`InstrumentationScope`].
66#[derive(Clone, Debug)]
67pub struct LogDataWithResource {
68    /// Log record
69    pub record: SdkLogRecord,
70    /// Instrumentation details for the emitter who produced this `LogRecord`.
71    pub instrumentation: InstrumentationScope,
72    /// Resource for the emitter who produced this `LogRecord`.
73    pub resource: Cow<'static, Resource>,
74}
75
76///Builder for ['InMemoryLogExporter'].
77/// # Example
78///
79/// ```no_run
80///# use opentelemetry_sdk::logs::{InMemoryLogExporter, InMemoryLogExporterBuilder};
81///# use opentelemetry_sdk::logs::{BatchLogProcessor, SdkLoggerProvider};
82///# use opentelemetry_sdk::runtime;
83///
84///# #[tokio::main]
85///# async fn main() {
86///    //Create an InMemoryLogExporter
87///    let exporter: InMemoryLogExporter = InMemoryLogExporterBuilder::default().build();
88///    //Create a LoggerProvider and register the exporter
89///    let logger_provider = SdkLoggerProvider::builder()
90///        .with_log_processor(BatchLogProcessor::builder(exporter.clone()).build())
91///        .build();
92///    // Setup Log Appenders and emit logs. (Not shown here)
93///    logger_provider.force_flush();
94///    let emitted_logs = exporter.get_emitted_logs().unwrap();
95///    for log in emitted_logs {
96///        println!("{:?}", log);
97///    }
98///# }
99///
100/// ```
101///
102#[derive(Debug, Clone)]
103pub struct InMemoryLogExporterBuilder {
104    reset_on_shutdown: bool,
105}
106
107impl Default for InMemoryLogExporterBuilder {
108    fn default() -> Self {
109        Self::new()
110    }
111}
112
113impl InMemoryLogExporterBuilder {
114    /// Creates a new instance of `InMemoryLogExporter`.
115    ///
116    pub fn new() -> Self {
117        Self {
118            reset_on_shutdown: true,
119        }
120    }
121
122    /// Creates a new instance of `InMemoryLogExporter`.
123    ///
124    pub fn build(&self) -> InMemoryLogExporter {
125        InMemoryLogExporter {
126            logs: Arc::new(Mutex::new(Vec::new())),
127            resource: Arc::new(Mutex::new(Resource::builder().build())),
128            should_reset_on_shutdown: self.reset_on_shutdown,
129            shutdown_called: Arc::new(AtomicBool::new(false)),
130        }
131    }
132
133    /// If set, the records will not be [`InMemoryLogExporter::reset`] on shutdown.
134    #[cfg(test)]
135    pub(crate) fn keep_records_on_shutdown(self) -> Self {
136        Self {
137            reset_on_shutdown: false,
138        }
139    }
140}
141
142impl InMemoryLogExporter {
143    /// Returns true if shutdown was called.
144    pub fn is_shutdown_called(&self) -> bool {
145        self.shutdown_called
146            .load(std::sync::atomic::Ordering::Relaxed)
147    }
148
149    /// Returns the logs emitted via Logger as a vector of `LogDataWithResource`.
150    ///
151    /// # Example
152    ///
153    /// ```
154    /// use opentelemetry_sdk::logs::{InMemoryLogExporter, InMemoryLogExporterBuilder};
155    ///
156    /// let exporter = InMemoryLogExporterBuilder::default().build();
157    /// let emitted_logs = exporter.get_emitted_logs().unwrap();
158    /// ```
159    ///
160    pub fn get_emitted_logs(&self) -> Result<Vec<LogDataWithResource>, InMemoryExporterError> {
161        let logs_guard = self.logs.lock().map_err(InMemoryExporterError::from)?;
162        let resource_guard = self.resource.lock().map_err(InMemoryExporterError::from)?;
163        let logs: Vec<LogDataWithResource> = logs_guard
164            .iter()
165            .map(|log_data| LogDataWithResource {
166                record: log_data.record.clone(),
167                resource: Cow::Owned(resource_guard.clone()),
168                instrumentation: log_data.instrumentation.clone(),
169            })
170            .collect();
171
172        Ok(logs)
173    }
174    /// Clears the internal (in-memory) storage of logs.
175    ///
176    /// # Example
177    ///
178    /// ```
179    /// use opentelemetry_sdk::logs::{InMemoryLogExporter, InMemoryLogExporterBuilder};
180    ///
181    /// let exporter = InMemoryLogExporterBuilder::default().build();
182    /// exporter.reset();
183    /// ```
184    ///
185    pub fn reset(&self) {
186        let _ = self
187            .logs
188            .lock()
189            .map(|mut logs_guard| logs_guard.clear())
190            .map_err(|e| OTelSdkError::InternalFailure(format!("Failed to reset logs: {}", e)));
191    }
192}
193
194impl LogExporter for InMemoryLogExporter {
195    async fn export(&self, batch: LogBatch<'_>) -> OTelSdkResult {
196        let mut logs_guard = self.logs.lock().map_err(|e| {
197            OTelSdkError::InternalFailure(format!("Failed to lock logs for export: {}", e))
198        })?;
199        for (log_record, instrumentation) in batch.iter() {
200            let owned_log = OwnedLogData {
201                record: (*log_record).clone(),
202                instrumentation: (*instrumentation).clone(),
203            };
204            logs_guard.push(owned_log);
205        }
206        Ok(())
207    }
208
209    fn shutdown_with_timeout(&self, _timeout: time::Duration) -> OTelSdkResult {
210        self.shutdown_called
211            .store(true, std::sync::atomic::Ordering::Relaxed);
212        if self.should_reset_on_shutdown {
213            self.reset();
214        }
215        Ok(())
216    }
217
218    fn set_resource(&mut self, resource: &Resource) {
219        let mut res_guard = self.resource.lock().expect("Resource lock poisoned");
220        *res_guard = resource.clone();
221    }
222}