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