opentelemetry_api/logs/
record.rs

1use crate::{
2    trace::{SpanContext, SpanId, TraceContextExt, TraceFlags, TraceId},
3    Array, Key, OrderMap, StringValue, Value,
4};
5use std::{borrow::Cow, time::SystemTime};
6
7#[derive(Debug, Clone, Default)]
8#[non_exhaustive]
9/// LogRecord represents all data carried by a log record, and
10/// is provided to `LogExporter`s as input.
11pub struct LogRecord {
12    /// Record timestamp
13    pub timestamp: Option<SystemTime>,
14
15    /// Timestamp for when the record was observed by OpenTelemetry
16    pub observed_timestamp: Option<SystemTime>,
17
18    /// Trace context for logs associated with spans
19    pub trace_context: Option<TraceContext>,
20
21    /// The original severity string from the source
22    pub severity_text: Option<Cow<'static, str>>,
23    /// The corresponding severity value, normalized
24    pub severity_number: Option<Severity>,
25
26    /// Record body
27    pub body: Option<AnyValue>,
28
29    /// Additional attributes associated with this record
30    pub attributes: Option<Vec<(Key, AnyValue)>>,
31}
32
33impl LogRecord {
34    /// Create a [`LogRecordBuilder`] to create a new Log Record
35    pub fn builder() -> LogRecordBuilder {
36        LogRecordBuilder::new()
37    }
38}
39
40/// TraceContext stores the trace data for logs that have an associated
41/// span.
42#[derive(Debug, Clone)]
43#[non_exhaustive]
44pub struct TraceContext {
45    /// Trace id
46    pub trace_id: TraceId,
47    /// Span Id
48    pub span_id: SpanId,
49    /// Trace flags
50    pub trace_flags: Option<TraceFlags>,
51}
52
53impl From<&SpanContext> for TraceContext {
54    fn from(span_context: &SpanContext) -> Self {
55        TraceContext {
56            trace_id: span_context.trace_id(),
57            span_id: span_context.span_id(),
58            trace_flags: Some(span_context.trace_flags()),
59        }
60    }
61}
62
63/// Value types for representing arbitrary values in a log record.
64#[derive(Debug, Clone)]
65pub enum AnyValue {
66    /// An integer value
67    Int(i64),
68    /// A double value
69    Double(f64),
70    /// A string value
71    String(StringValue),
72    /// A boolean value
73    Boolean(bool),
74    /// A byte array
75    Bytes(Vec<u8>),
76    /// An array of `Any` values
77    ListAny(Vec<AnyValue>),
78    /// A map of string keys to `Any` values, arbitrarily nested.
79    Map(OrderMap<Key, AnyValue>),
80}
81
82macro_rules! impl_trivial_from {
83    ($t:ty, $variant:path) => {
84        impl From<$t> for AnyValue {
85            fn from(val: $t) -> AnyValue {
86                $variant(val.into())
87            }
88        }
89    };
90}
91
92impl_trivial_from!(i8, AnyValue::Int);
93impl_trivial_from!(i16, AnyValue::Int);
94impl_trivial_from!(i32, AnyValue::Int);
95impl_trivial_from!(i64, AnyValue::Int);
96
97impl_trivial_from!(u8, AnyValue::Int);
98impl_trivial_from!(u16, AnyValue::Int);
99impl_trivial_from!(u32, AnyValue::Int);
100
101impl_trivial_from!(f64, AnyValue::Double);
102impl_trivial_from!(f32, AnyValue::Double);
103
104impl_trivial_from!(String, AnyValue::String);
105impl_trivial_from!(Cow<'static, str>, AnyValue::String);
106impl_trivial_from!(&'static str, AnyValue::String);
107impl_trivial_from!(StringValue, AnyValue::String);
108
109impl_trivial_from!(bool, AnyValue::Boolean);
110
111impl<T: Into<AnyValue>> FromIterator<T> for AnyValue {
112    /// Creates an [`AnyValue::ListAny`] value from a sequence of `Into<AnyValue>` values.
113    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
114        AnyValue::ListAny(iter.into_iter().map(Into::into).collect())
115    }
116}
117
118impl<K: Into<Key>, V: Into<AnyValue>> FromIterator<(K, V)> for AnyValue {
119    /// Creates an [`AnyValue::Map`] value from a sequence of key-value pairs
120    /// that can be converted into a `Key` and `AnyValue` respectively.
121    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
122        AnyValue::Map(OrderMap::from_iter(
123            iter.into_iter().map(|(k, v)| (k.into(), v.into())),
124        ))
125    }
126}
127
128impl From<Value> for AnyValue {
129    fn from(value: Value) -> Self {
130        match value {
131            Value::Bool(b) => b.into(),
132            Value::I64(i) => i.into(),
133            Value::F64(f) => f.into(),
134            Value::String(s) => s.into(),
135            Value::Array(a) => match a {
136                Array::Bool(b) => AnyValue::from_iter(b),
137                Array::F64(f) => AnyValue::from_iter(f),
138                Array::I64(i) => AnyValue::from_iter(i),
139                Array::String(s) => AnyValue::from_iter(s),
140            },
141        }
142    }
143}
144
145/// A normalized severity value.
146#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
147pub enum Severity {
148    /// TRACE
149    Trace = 1,
150    /// TRACE2
151    Trace2 = 2,
152    /// TRACE3
153    Trace3 = 3,
154    /// TRACE4
155    Trace4 = 4,
156    /// DEBUG
157    Debug = 5,
158    /// DEBUG2
159    Debug2 = 6,
160    /// DEBUG3
161    Debug3 = 7,
162    /// DEBUG4
163    Debug4 = 8,
164    /// INFO
165    Info = 9,
166    /// INFO2
167    Info2 = 10,
168    /// INFO3
169    Info3 = 11,
170    /// INFO4
171    Info4 = 12,
172    /// WARN
173    Warn = 13,
174    /// WARN2
175    Warn2 = 14,
176    /// WARN3
177    Warn3 = 15,
178    /// WARN4
179    Warn4 = 16,
180    /// ERROR
181    Error = 17,
182    /// ERROR2
183    Error2 = 18,
184    /// ERROR3
185    Error3 = 19,
186    /// ERROR4
187    Error4 = 20,
188    /// FATAL
189    Fatal = 21,
190    /// FATAL2
191    Fatal2 = 22,
192    /// FATAL3
193    Fatal3 = 23,
194    /// FATAL4
195    Fatal4 = 24,
196}
197
198impl Severity {
199    /// Return the string representing the short name for the `Severity`
200    /// value as specified by the OpenTelemetry logs data model.
201    pub const fn name(&self) -> &'static str {
202        match &self {
203            Severity::Trace => "TRACE",
204            Severity::Trace2 => "TRACE2",
205            Severity::Trace3 => "TRACE3",
206            Severity::Trace4 => "TRACE4",
207
208            Severity::Debug => "DEBUG",
209            Severity::Debug2 => "DEBUG2",
210            Severity::Debug3 => "DEBUG3",
211            Severity::Debug4 => "DEBUG4",
212
213            Severity::Info => "INFO",
214            Severity::Info2 => "INFO2",
215            Severity::Info3 => "INFO3",
216            Severity::Info4 => "INFO4",
217
218            Severity::Warn => "WARN",
219            Severity::Warn2 => "WARN2",
220            Severity::Warn3 => "WARN3",
221            Severity::Warn4 => "WARN4",
222
223            Severity::Error => "ERROR",
224            Severity::Error2 => "ERROR2",
225            Severity::Error3 => "ERROR3",
226            Severity::Error4 => "ERROR4",
227
228            Severity::Fatal => "FATAL",
229            Severity::Fatal2 => "FATAL2",
230            Severity::Fatal3 => "FATAL3",
231            Severity::Fatal4 => "FATAL4",
232        }
233    }
234}
235
236/// A builder for [`LogRecord`] values.
237#[derive(Debug, Clone)]
238pub struct LogRecordBuilder {
239    record: LogRecord,
240}
241
242impl LogRecordBuilder {
243    /// Create a new LogRecordBuilder
244    pub fn new() -> Self {
245        Self {
246            record: Default::default(),
247        }
248    }
249
250    /// Assign timestamp
251    pub fn with_timestamp(self, timestamp: SystemTime) -> Self {
252        Self {
253            record: LogRecord {
254                timestamp: Some(timestamp),
255                ..self.record
256            },
257        }
258    }
259
260    /// Assign observed timestamp
261    pub fn with_observed_timestamp(self, timestamp: SystemTime) -> Self {
262        Self {
263            record: LogRecord {
264                observed_timestamp: Some(timestamp),
265                ..self.record
266            },
267        }
268    }
269
270    /// Assign the record's [`TraceContext`]
271    pub fn with_span_context(self, span_context: &SpanContext) -> Self {
272        Self {
273            record: LogRecord {
274                trace_context: Some(TraceContext {
275                    span_id: span_context.span_id(),
276                    trace_id: span_context.trace_id(),
277                    trace_flags: Some(span_context.trace_flags()),
278                }),
279                ..self.record
280            },
281        }
282    }
283
284    /// Assign the record's [`TraceContext`] from a `TraceContextExt` trait
285    pub fn with_context<T>(self, context: &T) -> Self
286    where
287        T: TraceContextExt,
288    {
289        if context.has_active_span() {
290            self.with_span_context(context.span().span_context())
291        } else {
292            self
293        }
294    }
295
296    /// Assign severity text
297    pub fn with_severity_text<T>(self, severity: T) -> Self
298    where
299        T: Into<Cow<'static, str>>,
300    {
301        Self {
302            record: LogRecord {
303                severity_text: Some(severity.into()),
304                ..self.record
305            },
306        }
307    }
308
309    /// Assign severity number
310    pub fn with_severity_number(self, severity: Severity) -> Self {
311        Self {
312            record: LogRecord {
313                severity_number: Some(severity),
314                ..self.record
315            },
316        }
317    }
318
319    /// Assign body
320    pub fn with_body(self, body: AnyValue) -> Self {
321        Self {
322            record: LogRecord {
323                body: Some(body),
324                ..self.record
325            },
326        }
327    }
328
329    /// Assign attributes.
330    /// The SDK doesn't carry on any deduplication on these attributes.
331    pub fn with_attributes(self, attributes: Vec<(Key, AnyValue)>) -> Self {
332        Self {
333            record: LogRecord {
334                attributes: Some(attributes),
335                ..self.record
336            },
337        }
338    }
339
340    /// Set a single attribute for this record.
341    /// The SDK doesn't carry on any deduplication on these attributes.
342    pub fn with_attribute<K, V>(mut self, key: K, value: V) -> Self
343    where
344        K: Into<Key>,
345        V: Into<AnyValue>,
346    {
347        if let Some(ref mut vec) = self.record.attributes {
348            vec.push((key.into(), value.into()));
349        } else {
350            let vec = vec![(key.into(), value.into())];
351            self.record.attributes = Some(vec);
352        }
353
354        self
355    }
356
357    /// Build the record, consuming the Builder
358    pub fn build(self) -> LogRecord {
359        self.record
360    }
361}
362
363impl Default for LogRecordBuilder {
364    fn default() -> Self {
365        Self::new()
366    }
367}