1use crate::growable_array::GrowableArray;
2#[cfg(feature = "trace")]
3use opentelemetry::trace::SpanContext;
4use opentelemetry::{
5 logs::{AnyValue, Severity},
6 Key, SpanId, TraceFlags, TraceId,
7};
8use std::{borrow::Cow, time::SystemTime};
9
10const PREALLOCATED_ATTRIBUTE_CAPACITY: usize = 5;
13
14pub(crate) type LogRecordAttributes =
20 GrowableArray<Option<(Key, AnyValue)>, PREALLOCATED_ATTRIBUTE_CAPACITY>;
21
22#[derive(Debug, Clone, PartialEq)]
23#[non_exhaustive]
24pub struct SdkLogRecord {
27 pub(crate) event_name: Option<&'static str>,
29
30 pub(crate) target: Option<Cow<'static, str>>,
32
33 pub(crate) timestamp: Option<SystemTime>,
35
36 pub(crate) observed_timestamp: Option<SystemTime>,
38
39 pub(crate) trace_context: Option<TraceContext>,
41
42 pub(crate) severity_text: Option<&'static str>,
44
45 pub(crate) severity_number: Option<Severity>,
47
48 pub(crate) body: Option<AnyValue>,
50
51 pub(crate) attributes: LogRecordAttributes,
53}
54
55impl opentelemetry::logs::LogRecord for SdkLogRecord {
56 fn set_event_name(&mut self, name: &'static str) {
57 self.event_name = Some(name);
58 }
59
60 fn set_target<T>(&mut self, _target: T)
62 where
63 T: Into<Cow<'static, str>>,
64 {
65 self.target = Some(_target.into());
66 }
67
68 fn set_timestamp(&mut self, timestamp: SystemTime) {
69 self.timestamp = Some(timestamp);
70 }
71
72 fn set_observed_timestamp(&mut self, timestamp: SystemTime) {
73 self.observed_timestamp = Some(timestamp);
74 }
75
76 fn set_severity_text(&mut self, severity_text: &'static str) {
77 self.severity_text = Some(severity_text);
78 }
79
80 fn set_severity_number(&mut self, severity_number: Severity) {
81 self.severity_number = Some(severity_number);
82 }
83
84 fn set_body(&mut self, body: AnyValue) {
85 self.body = Some(body);
86 }
87
88 fn add_attributes<I, K, V>(&mut self, attributes: I)
89 where
90 I: IntoIterator<Item = (K, V)>,
91 K: Into<Key>,
92 V: Into<AnyValue>,
93 {
94 for (key, value) in attributes.into_iter() {
95 self.add_attribute(key, value);
96 }
97 }
98
99 fn add_attribute<K, V>(&mut self, key: K, value: V)
100 where
101 K: Into<Key>,
102 V: Into<AnyValue>,
103 {
104 self.attributes.push(Some((key.into(), value.into())));
105 }
106
107 fn set_trace_context(
108 &mut self,
109 trace_id: TraceId,
110 span_id: SpanId,
111 trace_flags: Option<TraceFlags>,
112 ) {
113 self.trace_context = Some(TraceContext {
114 trace_id,
115 span_id,
116 trace_flags,
117 });
118 }
119}
120
121impl SdkLogRecord {
122 pub(crate) fn new() -> Self {
124 SdkLogRecord {
125 event_name: None,
126 target: None,
127 timestamp: None,
128 observed_timestamp: None,
129 trace_context: None,
130 severity_text: None,
131 severity_number: None,
132 body: None,
133 attributes: LogRecordAttributes::default(),
134 }
135 }
136
137 #[inline]
139 pub fn event_name(&self) -> Option<&'static str> {
140 self.event_name
141 }
142
143 #[inline]
145 pub fn target(&self) -> Option<&Cow<'static, str>> {
146 self.target.as_ref()
147 }
148
149 #[inline]
151 pub fn timestamp(&self) -> Option<SystemTime> {
152 self.timestamp
153 }
154
155 #[inline]
157 pub fn observed_timestamp(&self) -> Option<SystemTime> {
158 self.observed_timestamp
159 }
160
161 #[inline]
163 pub fn trace_context(&self) -> Option<&TraceContext> {
164 self.trace_context.as_ref()
165 }
166
167 #[inline]
169 pub fn severity_text(&self) -> Option<&'static str> {
170 self.severity_text
171 }
172
173 #[inline]
175 pub fn severity_number(&self) -> Option<Severity> {
176 self.severity_number
177 }
178
179 #[inline]
181 pub fn body(&self) -> Option<&AnyValue> {
182 self.body.as_ref()
183 }
184
185 #[inline]
187 pub fn attributes_iter(&self) -> impl Iterator<Item = &(Key, AnyValue)> {
188 self.attributes.iter().filter_map(|opt| opt.as_ref())
189 }
190
191 #[allow(dead_code)]
192 pub(crate) fn attributes_len(&self) -> usize {
194 self.attributes.len()
195 }
196
197 #[allow(dead_code)]
198 pub(crate) fn attributes_contains(&self, key: &Key, value: &AnyValue) -> bool {
200 self.attributes
201 .iter()
202 .flatten()
203 .any(|(k, v)| k == key && v == value)
204 }
205}
206
207#[derive(Debug, Clone, PartialEq)]
210#[non_exhaustive]
211pub struct TraceContext {
212 pub trace_id: TraceId,
214 pub span_id: SpanId,
216 pub trace_flags: Option<TraceFlags>,
218}
219
220#[cfg(feature = "trace")]
221impl From<&SpanContext> for TraceContext {
222 fn from(span_context: &SpanContext) -> Self {
223 TraceContext {
224 trace_id: span_context.trace_id(),
225 span_id: span_context.span_id(),
226 trace_flags: Some(span_context.trace_flags()),
227 }
228 }
229}
230
231#[cfg(all(test, feature = "testing"))]
232mod tests {
233 use super::*;
234 use opentelemetry::logs::{AnyValue, LogRecord as _, Severity};
235 use opentelemetry::time::now;
236 use std::borrow::Cow;
237
238 #[test]
239 fn test_set_eventname() {
240 let mut log_record = SdkLogRecord::new();
241 log_record.set_event_name("test_event");
242 assert_eq!(log_record.event_name, Some("test_event"));
243 }
244
245 #[test]
246 fn test_set_target() {
247 let mut log_record = SdkLogRecord::new();
248 log_record.set_target("foo::bar");
249 assert_eq!(log_record.target, Some(Cow::Borrowed("foo::bar")));
250 }
251
252 #[test]
253 fn test_set_timestamp() {
254 let mut log_record = SdkLogRecord::new();
255 let now = now();
256 log_record.set_timestamp(now);
257 assert_eq!(log_record.timestamp, Some(now));
258 }
259
260 #[test]
261 fn test_set_observed_timestamp() {
262 let mut log_record = SdkLogRecord::new();
263 let now = now();
264 log_record.set_observed_timestamp(now);
265 assert_eq!(log_record.observed_timestamp, Some(now));
266 }
267
268 #[test]
269 fn test_set_severity_text() {
270 let mut log_record = SdkLogRecord::new();
271 log_record.set_severity_text("ERROR");
272 assert_eq!(log_record.severity_text, Some("ERROR"));
273 }
274
275 #[test]
276 fn test_set_severity_number() {
277 let mut log_record = SdkLogRecord::new();
278 let severity_number = Severity::Error;
279 log_record.set_severity_number(severity_number);
280 assert_eq!(log_record.severity_number, Some(Severity::Error));
281 }
282
283 #[test]
284 fn test_set_body() {
285 let mut log_record = SdkLogRecord::new();
286 let body = AnyValue::String("Test body".into());
287 log_record.set_body(body.clone());
288 assert_eq!(log_record.body, Some(body));
289 }
290
291 #[test]
292 fn test_set_attributes() {
293 let mut log_record = SdkLogRecord::new();
294 let attributes = vec![(Key::new("key"), AnyValue::String("value".into()))];
295 log_record.add_attributes(attributes.clone());
296 for (key, value) in attributes {
297 assert!(log_record.attributes_contains(&key, &value));
298 }
299 }
300
301 #[test]
302 fn test_set_attribute() {
303 let mut log_record = SdkLogRecord::new();
304 log_record.add_attribute("key", "value");
305 let key = Key::new("key");
306 let value = AnyValue::String("value".into());
307 assert!(log_record.attributes_contains(&key, &value));
308 }
309
310 #[test]
311 fn compare_trace_context() {
312 let trace_context = TraceContext {
313 trace_id: TraceId::from(1),
314 span_id: SpanId::from(1),
315 trace_flags: Some(TraceFlags::default()),
316 };
317
318 let trace_context_cloned = trace_context.clone();
319
320 assert_eq!(trace_context, trace_context_cloned);
321
322 let trace_context_different = TraceContext {
323 trace_id: TraceId::from(2),
324 span_id: SpanId::from(2),
325 trace_flags: Some(TraceFlags::default()),
326 };
327
328 assert_ne!(trace_context, trace_context_different);
329 }
330
331 #[test]
332 fn compare_log_record() {
333 let mut log_record = SdkLogRecord {
334 event_name: Some("test_event"),
335 target: Some(Cow::Borrowed("foo::bar")),
336 timestamp: Some(now()),
337 observed_timestamp: Some(now()),
338 severity_text: Some("ERROR"),
339 severity_number: Some(Severity::Error),
340 body: Some(AnyValue::String("Test body".into())),
341 attributes: LogRecordAttributes::new(),
342 trace_context: Some(TraceContext {
343 trace_id: TraceId::from(1),
344 span_id: SpanId::from(1),
345 trace_flags: Some(TraceFlags::default()),
346 }),
347 };
348 log_record.add_attribute(Key::new("key"), AnyValue::String("value".into()));
349
350 let log_record_cloned = log_record.clone();
351
352 assert_eq!(log_record, log_record_cloned);
353
354 let mut log_record_different = log_record.clone();
355 log_record_different.event_name = Some("different_event");
356
357 assert_ne!(log_record, log_record_different);
358 }
359
360 #[test]
361 fn compare_log_record_target_borrowed_eq_owned() {
362 let log_record_borrowed = SdkLogRecord {
363 event_name: Some("test_event"),
364 ..SdkLogRecord::new()
365 };
366
367 let log_record_owned = SdkLogRecord {
368 event_name: Some("test_event"),
369 ..SdkLogRecord::new()
370 };
371
372 assert_eq!(log_record_borrowed, log_record_owned);
373 }
374}