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}