opentelemetry_appender_log/
lib.rs

1//! Bridge `log` into OpenTelemetry.
2//!
3//! This library implements a log appender for the [`log`] crate using the [Logs Bridge API].
4//!
5//! *[Supported Rust Versions](#supported-rust-versions)*
6//!
7//! # Getting Started
8//!
9//! The bridge requires configuration on both the `log` and OpenTelemetry sides.
10//!
11//! For OpenTelemetry, configure a [`LoggerProvider`] with the desired exporter:
12//!
13//! ```
14//! # #[tokio::main] async fn main() {
15//! # use opentelemetry_sdk::logs::{BatchLogProcessor, SdkLoggerProvider};
16//! # use opentelemetry_sdk::runtime;
17//! let exporter = opentelemetry_stdout::LogExporter::default();
18//!
19//! let logger_provider = SdkLoggerProvider::builder()
20//!     .with_log_processor(BatchLogProcessor::builder(exporter).build())
21//!     .build();
22//! # }
23//! ```
24//!
25//! For `log`, set the global logger to an [`OpenTelemetryLogBridge`] instance using the `LoggerProvider`:
26//!
27//! ```
28//! # #[tokio::main] async fn main() {
29//! # use opentelemetry_sdk::logs::{BatchLogProcessor, SdkLoggerProvider};
30//! # use opentelemetry_sdk::runtime;
31//! # use opentelemetry_appender_log::OpenTelemetryLogBridge;
32//! # let exporter = opentelemetry_stdout::LogExporter::default();
33//! # let logger_provider = SdkLoggerProvider::builder()
34//! #     .with_log_processor(BatchLogProcessor::builder(exporter).build())
35//! #     .build();
36//! let otel_log_appender = OpenTelemetryLogBridge::new(&logger_provider);
37//!
38//! log::set_boxed_logger(Box::new(otel_log_appender)).unwrap();
39//! # }
40//! ```
41//!
42//! # Mapping Log Records
43//!
44//! This section outlines how log records produced by `log` are mapped into OpenTelemetry log records.
45//! Each subsection deals with a different property on `opentelemetry::logs::LogRecord`.
46//!
47//! ## Body
48//!
49//! The body is the stringified message ([`log::Record::args`]).
50//!
51//! ## Severity
52//!
53//! The severity number and text are mapped from the [`log::Level`] ([`log::Record::level`]):
54//!
55//! | `log::Level` | Severity Text | Severity Number |
56//! | ------------ | ------------- | --------------- |
57//! | `Error`      | Error         | 17              |
58//! | `Warn`       | Warn          | 13              |
59//! | `Info`       | Info          | 9               |
60//! | `Debug`      | Debug         | 5               |
61//! | `Trace`      | Trace         | 1               |
62//!
63//! # Attributes
64//!
65//! Any key-values ([`log::Record::key_values`]) are converted into attributes:
66//!
67//! | Type            | Result                | Notes                                                                                                                   |
68//! | --------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------- |
69//! | `i8`-`i128`     | [`AnyValue::Int`]     | If the value is too big then it will be stringified using [`std::fmt::Display`]                                         |
70//! | `u8`-`u128`     | [`AnyValue::Int`]     | If the value is too big then it will be stringified using [`std::fmt::Display`]                                         |
71//! | `f32`-`f64`     | [`AnyValue::Double`]  |                                                                                                                         |
72//! | `bool`          | [`AnyValue::Boolean`] |                                                                                                                         |
73//! | `str`           | [`AnyValue::String`]  |                                                                                                                         |
74//! | Bytes           | [`AnyValue::Bytes`]   | Requires the `with-serde` feature, otherwise it will be stringified using [`std::fmt::Debug`]                           |
75//! | `()`            | -                     | Unit values are discared                                                                                                |
76//! | `Some`          | Any                   | `Some` variants use their inner value                                                                                   |
77//! | `None`          | -                     | `None` variants are discared                                                                                            |
78//! | Unit struct     | [`AnyValue::String`]  | Uses the name of the struct                                                                                             |
79//! | Unit variant    | [`AnyValue::String`]  | Uses the name of the variant                                                                                            |
80//! | Newtype struct  | Any                   | Uses the inner value of the newtype                                                                                     |
81//! | Newtype variant | [`AnyValue::Map`]     | An internally-tagged map. Requires the `with-serde` feature, otherwise it will be stringified using [`std::fmt::Debug`] |
82//! | Sequence        | [`AnyValue::ListAny`] | Requires the `with-serde` feature, otherwise it will be stringified using [`std::fmt::Debug`]                           |
83//! | Tuple           | [`AnyValue::ListAny`] | Requires the `with-serde` feature, otherwise it will be stringified using [`std::fmt::Debug`]                           |
84//! | Tuple struct    | [`AnyValue::ListAny`] | Requires the `with-serde` feature, otherwise it will be stringified using [`std::fmt::Debug`]                           |
85//! | Tuple variant   | [`AnyValue::Map`]     | An internally-tagged map. Requires the `with-serde` feature, otherwise it will be stringified using [`std::fmt::Debug`] |
86//! | Map             | [`AnyValue::Map`]     | Requires the `with-serde` feature, otherwise it will be stringified using [`std::fmt::Debug`]                           |
87//! | Struct          | [`AnyValue::Map`]     | Requires the `with-serde` feature, otherwise it will be stringified using [`std::fmt::Debug`]                           |
88//! | Struct variant  | [`AnyValue::Map`]     | An internally-tagged map. Requires the `with-serde` feature, otherwise it will be stringified using [`std::fmt::Debug`] |
89//!
90//! # Feature Flags
91//!
92//! This library provides the following Cargo features:
93//!
94//! - `spec_unstable_logs_enabled`: Allow users to control the log level.
95//! - `with-serde`: Support complex values as attributes without stringifying them.
96//!
97//! [Logs Bridge API]: https://opentelemetry.io/docs/specs/otel/logs/bridge-api/
98//!
99//! ## Supported Rust Versions
100//!
101//! OpenTelemetry is built against the latest stable release. The minimum
102//! supported version is 1.70. The current OpenTelemetry version is not
103//! guaranteed to build on Rust versions earlier than the minimum supported
104//! version.
105//!
106//! The current stable Rust compiler and the three most recent minor versions
107//! before it will always be supported. For example, if the current stable
108//! compiler version is 1.49, the minimum supported version will not be
109//! increased past 1.46, three minor versions prior. Increasing the minimum
110//! supported compiler version is not considered a semver breaking change as
111//! long as doing so complies with this policy.
112
113use log::{Level, Metadata, Record};
114use opentelemetry::{
115    logs::{AnyValue, LogRecord, Logger, LoggerProvider, Severity},
116    Key,
117};
118#[cfg(feature = "experimental_metadata_attributes")]
119use opentelemetry_semantic_conventions::attribute::{
120    CODE_FILE_PATH, CODE_FUNCTION_NAME, CODE_LINE_NUMBER,
121};
122
123pub struct OpenTelemetryLogBridge<P, L>
124where
125    P: LoggerProvider<Logger = L> + Send + Sync,
126    L: Logger + Send + Sync,
127{
128    logger: L,
129    _phantom: std::marker::PhantomData<P>, // P is not used in this struct
130}
131
132impl<P, L> log::Log for OpenTelemetryLogBridge<P, L>
133where
134    P: LoggerProvider<Logger = L> + Send + Sync,
135    L: Logger + Send + Sync,
136{
137    fn enabled(&self, _metadata: &Metadata) -> bool {
138        #[cfg(feature = "spec_unstable_logs_enabled")]
139        return self.logger.event_enabled(
140            severity_of_level(_metadata.level()),
141            _metadata.target(),
142            None,
143        );
144        #[cfg(not(feature = "spec_unstable_logs_enabled"))]
145        true
146    }
147
148    fn log(&self, record: &Record) {
149        if self.enabled(record.metadata()) {
150            let mut log_record = self.logger.create_log_record();
151            log_record.set_severity_number(severity_of_level(record.level()));
152            log_record.set_severity_text(record.level().as_str());
153            log_record.set_body(AnyValue::from(record.args().to_string()));
154
155            #[cfg(feature = "experimental_metadata_attributes")]
156            {
157                if let Some(filepath) = record.file() {
158                    log_record.add_attribute(
159                        Key::new(CODE_FILE_PATH),
160                        AnyValue::from(filepath.to_string()),
161                    );
162                }
163
164                if let Some(line_no) = record.line() {
165                    log_record.add_attribute(Key::new(CODE_LINE_NUMBER), AnyValue::from(line_no));
166                }
167
168                if let Some(module) = record.module_path() {
169                    log_record.add_attribute(
170                        Key::new(CODE_FUNCTION_NAME),
171                        AnyValue::from(module.to_string()),
172                    );
173                }
174            }
175
176            log_record.add_attributes(log_attributes(record.key_values()));
177            log_record.set_target(record.metadata().target().to_string());
178
179            self.logger.emit(log_record);
180        }
181    }
182
183    fn flush(&self) {}
184}
185
186impl<P, L> OpenTelemetryLogBridge<P, L>
187where
188    P: LoggerProvider<Logger = L> + Send + Sync,
189    L: Logger + Send + Sync,
190{
191    pub fn new(provider: &P) -> Self {
192        Self {
193            // Using empty scope name.
194            // The name/version of this library itself can be added
195            // as a Scope attribute once a semantic convention is
196            // defined for the same.
197            // See https://github.com/open-telemetry/semantic-conventions/issues/1550
198            logger: provider.logger(""),
199            _phantom: Default::default(),
200        }
201    }
202}
203
204const fn severity_of_level(level: Level) -> Severity {
205    match level {
206        Level::Error => Severity::Error,
207        Level::Warn => Severity::Warn,
208        Level::Info => Severity::Info,
209        Level::Debug => Severity::Debug,
210        Level::Trace => Severity::Trace,
211    }
212}
213
214fn log_attributes(kvs: impl log::kv::Source) -> Vec<(Key, AnyValue)> {
215    struct AttributeVisitor(Vec<(Key, AnyValue)>);
216
217    impl<'kvs> log::kv::VisitSource<'kvs> for AttributeVisitor {
218        fn visit_pair(
219            &mut self,
220            key: log::kv::Key<'kvs>,
221            value: log::kv::Value<'kvs>,
222        ) -> Result<(), log::kv::Error> {
223            let key = Key::from(String::from(key.as_str()));
224
225            if let Some(value) = any_value::serialize(value) {
226                self.0.push((key, value));
227            }
228
229            Ok(())
230        }
231    }
232
233    let mut visitor = AttributeVisitor(Vec::new());
234
235    let _ = kvs.visit(&mut visitor);
236
237    visitor.0
238}
239
240#[cfg(not(feature = "with-serde"))]
241mod any_value {
242    use opentelemetry::{logs::AnyValue, StringValue};
243
244    pub(crate) fn serialize(value: log::kv::Value) -> Option<AnyValue> {
245        struct ValueVisitor(Option<AnyValue>);
246
247        impl log::kv::VisitValue<'_> for ValueVisitor {
248            fn visit_any(&mut self, value: log::kv::Value) -> Result<(), log::kv::Error> {
249                self.0 = Some(AnyValue::String(StringValue::from(value.to_string())));
250
251                Ok(())
252            }
253
254            fn visit_bool(&mut self, value: bool) -> Result<(), log::kv::Error> {
255                self.0 = Some(AnyValue::Boolean(value));
256
257                Ok(())
258            }
259
260            fn visit_str(&mut self, value: &str) -> Result<(), log::kv::Error> {
261                self.0 = Some(AnyValue::String(StringValue::from(value.to_owned())));
262
263                Ok(())
264            }
265
266            fn visit_i64(&mut self, value: i64) -> Result<(), log::kv::Error> {
267                self.0 = Some(AnyValue::Int(value));
268
269                Ok(())
270            }
271
272            fn visit_u64(&mut self, value: u64) -> Result<(), log::kv::Error> {
273                if let Ok(value) = value.try_into() {
274                    self.visit_i64(value)
275                } else {
276                    self.visit_any(log::kv::Value::from(value))
277                }
278            }
279
280            fn visit_i128(&mut self, value: i128) -> Result<(), log::kv::Error> {
281                if let Ok(value) = value.try_into() {
282                    self.visit_i64(value)
283                } else {
284                    self.visit_any(log::kv::Value::from(value))
285                }
286            }
287
288            fn visit_u128(&mut self, value: u128) -> Result<(), log::kv::Error> {
289                if let Ok(value) = value.try_into() {
290                    self.visit_i64(value)
291                } else {
292                    self.visit_any(log::kv::Value::from(value))
293                }
294            }
295
296            fn visit_f64(&mut self, value: f64) -> Result<(), log::kv::Error> {
297                self.0 = Some(AnyValue::Double(value));
298
299                Ok(())
300            }
301        }
302
303        let mut visitor = ValueVisitor(None);
304        value.visit(&mut visitor).unwrap();
305        visitor.0
306    }
307}
308
309// This could make a nice addition to the SDK itself for serializing into `AnyValue`s
310#[cfg(feature = "with-serde")]
311mod any_value {
312    use std::{collections::HashMap, fmt};
313
314    use opentelemetry::{logs::AnyValue, Key, StringValue};
315    use serde::ser::{
316        Error, Serialize, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant,
317        SerializeTuple, SerializeTupleStruct, SerializeTupleVariant, Serializer, StdError,
318    };
319
320    /// Serialize an arbitrary `serde::Serialize` into an `AnyValue`.
321    ///
322    /// This method performs the following translations when converting between `serde`'s data model and OpenTelemetry's:
323    ///
324    /// - Integers that don't fit in a `i64` are converted into strings.
325    /// - Unit types and nones are discarded (effectively treated as undefined).
326    /// - Struct and tuple variants are converted into an internally tagged map.
327    /// - Unit variants are converted into strings.
328    pub(crate) fn serialize(value: impl serde::Serialize) -> Option<AnyValue> {
329        value.serialize(ValueSerializer).ok()?
330    }
331
332    struct ValueSerializer;
333
334    struct ValueSerializeSeq {
335        value: Vec<AnyValue>,
336    }
337
338    struct ValueSerializeTuple {
339        value: Vec<AnyValue>,
340    }
341
342    struct ValueSerializeTupleStruct {
343        value: Vec<AnyValue>,
344    }
345
346    struct ValueSerializeMap {
347        key: Option<Key>,
348        value: HashMap<Key, AnyValue>,
349    }
350
351    struct ValueSerializeStruct {
352        value: HashMap<Key, AnyValue>,
353    }
354
355    struct ValueSerializeTupleVariant {
356        variant: &'static str,
357        value: Vec<AnyValue>,
358    }
359
360    struct ValueSerializeStructVariant {
361        variant: &'static str,
362        value: HashMap<Key, AnyValue>,
363    }
364
365    #[derive(Debug)]
366    struct ValueError(String);
367
368    impl fmt::Display for ValueError {
369        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
370            fmt::Display::fmt(&self.0, f)
371        }
372    }
373
374    impl Error for ValueError {
375        fn custom<T>(msg: T) -> Self
376        where
377            T: fmt::Display,
378        {
379            ValueError(msg.to_string())
380        }
381    }
382
383    impl StdError for ValueError {}
384
385    impl Serializer for ValueSerializer {
386        type Ok = Option<AnyValue>;
387
388        type Error = ValueError;
389
390        type SerializeSeq = ValueSerializeSeq;
391
392        type SerializeTuple = ValueSerializeTuple;
393
394        type SerializeTupleStruct = ValueSerializeTupleStruct;
395
396        type SerializeTupleVariant = ValueSerializeTupleVariant;
397
398        type SerializeMap = ValueSerializeMap;
399
400        type SerializeStruct = ValueSerializeStruct;
401
402        type SerializeStructVariant = ValueSerializeStructVariant;
403
404        fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
405            Ok(Some(AnyValue::Boolean(v)))
406        }
407
408        fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
409            self.serialize_i64(v as i64)
410        }
411
412        fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
413            self.serialize_i64(v as i64)
414        }
415
416        fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
417            self.serialize_i64(v as i64)
418        }
419
420        fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
421            Ok(Some(AnyValue::Int(v)))
422        }
423
424        fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> {
425            if let Ok(v) = v.try_into() {
426                self.serialize_i64(v)
427            } else {
428                self.collect_str(&v)
429            }
430        }
431
432        fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
433            self.serialize_i64(v as i64)
434        }
435
436        fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
437            self.serialize_i64(v as i64)
438        }
439
440        fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
441            self.serialize_i64(v as i64)
442        }
443
444        fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
445            if let Ok(v) = v.try_into() {
446                self.serialize_i64(v)
447            } else {
448                self.collect_str(&v)
449            }
450        }
451
452        fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> {
453            if let Ok(v) = v.try_into() {
454                self.serialize_i64(v)
455            } else {
456                self.collect_str(&v)
457            }
458        }
459
460        fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
461            self.serialize_f64(v as f64)
462        }
463
464        fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
465            Ok(Some(AnyValue::Double(v)))
466        }
467
468        fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
469            self.collect_str(&v)
470        }
471
472        fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
473            Ok(Some(AnyValue::String(StringValue::from(v.to_owned()))))
474        }
475
476        fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
477            Ok(Some(AnyValue::Bytes(Box::new(v.to_owned()))))
478        }
479
480        fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
481            Ok(None)
482        }
483
484        fn serialize_some<T: serde::Serialize + ?Sized>(
485            self,
486            value: &T,
487        ) -> Result<Self::Ok, Self::Error> {
488            value.serialize(self)
489        }
490
491        fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
492            Ok(None)
493        }
494
495        fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
496            name.serialize(self)
497        }
498
499        fn serialize_unit_variant(
500            self,
501            _: &'static str,
502            _: u32,
503            variant: &'static str,
504        ) -> Result<Self::Ok, Self::Error> {
505            variant.serialize(self)
506        }
507
508        fn serialize_newtype_struct<T: serde::Serialize + ?Sized>(
509            self,
510            _: &'static str,
511            value: &T,
512        ) -> Result<Self::Ok, Self::Error> {
513            value.serialize(self)
514        }
515
516        fn serialize_newtype_variant<T: serde::Serialize + ?Sized>(
517            self,
518            _: &'static str,
519            _: u32,
520            variant: &'static str,
521            value: &T,
522        ) -> Result<Self::Ok, Self::Error> {
523            let mut map = self.serialize_map(Some(1))?;
524            map.serialize_entry(variant, value)?;
525            map.end()
526        }
527
528        fn serialize_seq(self, _: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
529            Ok(ValueSerializeSeq { value: Vec::new() })
530        }
531
532        fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Self::Error> {
533            Ok(ValueSerializeTuple { value: Vec::new() })
534        }
535
536        fn serialize_tuple_struct(
537            self,
538            _: &'static str,
539            _: usize,
540        ) -> Result<Self::SerializeTupleStruct, Self::Error> {
541            Ok(ValueSerializeTupleStruct { value: Vec::new() })
542        }
543
544        fn serialize_tuple_variant(
545            self,
546            _: &'static str,
547            _: u32,
548            variant: &'static str,
549            _: usize,
550        ) -> Result<Self::SerializeTupleVariant, Self::Error> {
551            Ok(ValueSerializeTupleVariant {
552                variant,
553                value: Vec::new(),
554            })
555        }
556
557        fn serialize_map(self, _: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
558            Ok(ValueSerializeMap {
559                key: None,
560                value: HashMap::new(),
561            })
562        }
563
564        fn serialize_struct(
565            self,
566            _: &'static str,
567            _: usize,
568        ) -> Result<Self::SerializeStruct, Self::Error> {
569            Ok(ValueSerializeStruct {
570                value: HashMap::new(),
571            })
572        }
573
574        fn serialize_struct_variant(
575            self,
576            _: &'static str,
577            _: u32,
578            variant: &'static str,
579            _: usize,
580        ) -> Result<Self::SerializeStructVariant, Self::Error> {
581            Ok(ValueSerializeStructVariant {
582                variant,
583                value: HashMap::new(),
584            })
585        }
586    }
587
588    impl SerializeSeq for ValueSerializeSeq {
589        type Ok = Option<AnyValue>;
590
591        type Error = ValueError;
592
593        fn serialize_element<T: serde::Serialize + ?Sized>(
594            &mut self,
595            value: &T,
596        ) -> Result<(), Self::Error> {
597            if let Some(value) = value.serialize(ValueSerializer)? {
598                self.value.push(value);
599            }
600
601            Ok(())
602        }
603
604        fn end(self) -> Result<Self::Ok, Self::Error> {
605            Ok(Some(AnyValue::ListAny(Box::new(self.value))))
606        }
607    }
608
609    impl SerializeTuple for ValueSerializeTuple {
610        type Ok = Option<AnyValue>;
611
612        type Error = ValueError;
613
614        fn serialize_element<T: serde::Serialize + ?Sized>(
615            &mut self,
616            value: &T,
617        ) -> Result<(), Self::Error> {
618            if let Some(value) = value.serialize(ValueSerializer)? {
619                self.value.push(value);
620            }
621
622            Ok(())
623        }
624
625        fn end(self) -> Result<Self::Ok, Self::Error> {
626            Ok(Some(AnyValue::ListAny(Box::new(self.value))))
627        }
628    }
629
630    impl SerializeTupleStruct for ValueSerializeTupleStruct {
631        type Ok = Option<AnyValue>;
632
633        type Error = ValueError;
634
635        fn serialize_field<T: serde::Serialize + ?Sized>(
636            &mut self,
637            value: &T,
638        ) -> Result<(), Self::Error> {
639            if let Some(value) = value.serialize(ValueSerializer)? {
640                self.value.push(value);
641            }
642
643            Ok(())
644        }
645
646        fn end(self) -> Result<Self::Ok, Self::Error> {
647            Ok(Some(AnyValue::ListAny(Box::new(self.value))))
648        }
649    }
650
651    impl SerializeTupleVariant for ValueSerializeTupleVariant {
652        type Ok = Option<AnyValue>;
653
654        type Error = ValueError;
655
656        fn serialize_field<T: serde::Serialize + ?Sized>(
657            &mut self,
658            value: &T,
659        ) -> Result<(), Self::Error> {
660            if let Some(value) = value.serialize(ValueSerializer)? {
661                self.value.push(value);
662            }
663
664            Ok(())
665        }
666
667        fn end(self) -> Result<Self::Ok, Self::Error> {
668            Ok(Some(AnyValue::Map({
669                let mut variant = Box::<HashMap<Key, AnyValue>>::default();
670                variant.insert(
671                    Key::from(self.variant),
672                    AnyValue::ListAny(Box::new(self.value)),
673                );
674                variant
675            })))
676        }
677    }
678
679    impl SerializeMap for ValueSerializeMap {
680        type Ok = Option<AnyValue>;
681
682        type Error = ValueError;
683
684        fn serialize_key<T: serde::Serialize + ?Sized>(
685            &mut self,
686            key: &T,
687        ) -> Result<(), Self::Error> {
688            let key = match key.serialize(ValueSerializer)? {
689                Some(AnyValue::String(key)) => Key::from(String::from(key)),
690                key => Key::from(format!("{key:?}")),
691            };
692
693            self.key = Some(key);
694
695            Ok(())
696        }
697
698        fn serialize_value<T: serde::Serialize + ?Sized>(
699            &mut self,
700            value: &T,
701        ) -> Result<(), Self::Error> {
702            let key = self
703                .key
704                .take()
705                .ok_or_else(|| Self::Error::custom("missing key"))?;
706
707            if let Some(value) = value.serialize(ValueSerializer)? {
708                self.value.insert(key, value);
709            }
710
711            Ok(())
712        }
713
714        fn end(self) -> Result<Self::Ok, Self::Error> {
715            Ok(Some(AnyValue::Map(Box::new(self.value))))
716        }
717    }
718
719    impl SerializeStruct for ValueSerializeStruct {
720        type Ok = Option<AnyValue>;
721
722        type Error = ValueError;
723
724        fn serialize_field<T: serde::Serialize + ?Sized>(
725            &mut self,
726            key: &'static str,
727            value: &T,
728        ) -> Result<(), Self::Error> {
729            let key = Key::from(key);
730
731            if let Some(value) = value.serialize(ValueSerializer)? {
732                self.value.insert(key, value);
733            }
734
735            Ok(())
736        }
737
738        fn end(self) -> Result<Self::Ok, Self::Error> {
739            Ok(Some(AnyValue::Map(Box::new(self.value))))
740        }
741    }
742
743    impl SerializeStructVariant for ValueSerializeStructVariant {
744        type Ok = Option<AnyValue>;
745
746        type Error = ValueError;
747
748        fn serialize_field<T: serde::Serialize + ?Sized>(
749            &mut self,
750            key: &'static str,
751            value: &T,
752        ) -> Result<(), Self::Error> {
753            let key = Key::from(key);
754
755            if let Some(value) = value.serialize(ValueSerializer)? {
756                self.value.insert(key, value);
757            }
758
759            Ok(())
760        }
761
762        fn end(self) -> Result<Self::Ok, Self::Error> {
763            Ok(Some(AnyValue::Map({
764                let mut variant = Box::<HashMap<Key, AnyValue>>::default();
765                variant.insert(Key::from(self.variant), AnyValue::Map(Box::new(self.value)));
766                variant
767            })))
768        }
769    }
770}
771
772#[cfg(test)]
773mod tests {
774    use super::OpenTelemetryLogBridge;
775
776    use opentelemetry::{logs::AnyValue, StringValue};
777    use opentelemetry_sdk::{logs::InMemoryLogExporter, logs::SdkLoggerProvider};
778
779    use log::Log;
780
781    #[test]
782    fn logbridge_with_default_metadata_is_enabled() {
783        let exporter = InMemoryLogExporter::default();
784
785        let logger_provider = SdkLoggerProvider::builder()
786            .with_simple_exporter(exporter)
787            .build();
788
789        let otel_log_appender = OpenTelemetryLogBridge::new(&logger_provider);
790
791        // As a result of using `with_simple_exporter` while building the logger provider,
792        // the processor used is a `SimpleLogProcessor` which has an implementation of `event_enabled`
793        // that always returns true.
794        #[cfg(feature = "spec_unstable_logs_enabled")]
795        assert!(otel_log_appender.enabled(&log::Metadata::builder().build()));
796        #[cfg(not(feature = "spec_unstable_logs_enabled"))]
797        assert!(otel_log_appender.enabled(&log::Metadata::builder().build()));
798    }
799
800    #[test]
801    fn logbridge_with_record_can_log() {
802        let exporter = InMemoryLogExporter::default();
803
804        let logger_provider = SdkLoggerProvider::builder()
805            .with_simple_exporter(exporter.clone())
806            .build();
807
808        let otel_log_appender = OpenTelemetryLogBridge::new(&logger_provider);
809
810        // log::trace!("TRACE")
811        otel_log_appender.log(
812            &log::RecordBuilder::new()
813                .level(log::Level::Trace)
814                .args(format_args!("TRACE"))
815                .build(),
816        );
817
818        // log::trace!("DEBUG")
819        otel_log_appender.log(
820            &log::RecordBuilder::new()
821                .level(log::Level::Debug)
822                .args(format_args!("DEBUG"))
823                .build(),
824        );
825
826        // log::trace!("INFO")
827        otel_log_appender.log(
828            &log::RecordBuilder::new()
829                .level(log::Level::Info)
830                .args(format_args!("INFO"))
831                .build(),
832        );
833
834        // log::trace!("WARN")
835        otel_log_appender.log(
836            &log::RecordBuilder::new()
837                .level(log::Level::Warn)
838                .args(format_args!("WARN"))
839                .build(),
840        );
841
842        // log::trace!("ERROR")
843        otel_log_appender.log(
844            &log::RecordBuilder::new()
845                .level(log::Level::Error)
846                .args(format_args!("ERROR"))
847                .build(),
848        );
849
850        let logs = exporter.get_emitted_logs().unwrap();
851
852        assert_eq!(logs.len(), 5);
853        for log in logs {
854            let body: String = match log.record.body().unwrap() {
855                super::AnyValue::String(s) => s.to_string(),
856                _ => panic!("AnyValue::String expected"),
857            };
858            assert_eq!(body, log.record.severity_text().unwrap());
859        }
860    }
861
862    #[test]
863    fn logbridge_attributes() {
864        #[derive(serde::Serialize)]
865        struct Struct {
866            a: i32,
867            b: i32,
868            c: i32,
869        }
870
871        #[derive(serde::Serialize)]
872        struct Newtype(i32);
873
874        #[derive(serde::Serialize)]
875        enum Enum {
876            Unit,
877            Newtype(i32),
878            Struct { a: i32, b: i32, c: i32 },
879            Tuple(i32, i32, i32),
880        }
881
882        struct Bytes<B>(B);
883
884        impl<B: AsRef<[u8]>> serde::Serialize for Bytes<B> {
885            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
886            where
887                S: serde::Serializer,
888            {
889                serializer.serialize_bytes(self.0.as_ref())
890            }
891        }
892
893        struct Map {
894            a: i32,
895            b: i32,
896            c: i32,
897        }
898
899        impl serde::Serialize for Map {
900            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
901            where
902                S: serde::Serializer,
903            {
904                use serde::ser::SerializeMap;
905
906                let mut map = serializer.serialize_map(Some(3))?;
907
908                map.serialize_entry(&"a", &self.a)?;
909                map.serialize_entry(&"b", &self.b)?;
910                map.serialize_entry(&"c", &self.c)?;
911
912                map.end()
913            }
914        }
915
916        let exporter = InMemoryLogExporter::default();
917
918        let logger_provider = SdkLoggerProvider::builder()
919            .with_simple_exporter(exporter.clone())
920            .build();
921
922        let otel_log_appender = OpenTelemetryLogBridge::new(&logger_provider);
923
924        otel_log_appender.log(
925            &log::RecordBuilder::new()
926                .level(log::Level::Info)
927                .args(format_args!("body"))
928                .key_values(&[
929                    ("str_value", log::kv::Value::from("a string")),
930                    ("u8_value", log::kv::Value::from(1u8)),
931                    ("u16_value", log::kv::Value::from(2u16)),
932                    ("u32_value", log::kv::Value::from(42u32)),
933                    ("u64_value", log::kv::Value::from(2147483660u64)),
934                    ("u128_small_value", log::kv::Value::from(2147483660u128)),
935                    (
936                        "u128_big_value",
937                        log::kv::Value::from(9223372036854775820u128),
938                    ),
939                    ("i8_value", log::kv::Value::from(1i8)),
940                    ("i16_value", log::kv::Value::from(2i16)),
941                    ("i32_value", log::kv::Value::from(42i32)),
942                    ("i64_value", log::kv::Value::from(2147483660i64)),
943                    ("i128_small_value", log::kv::Value::from(2147483660i128)),
944                    (
945                        "i128_big_value",
946                        log::kv::Value::from(9223372036854775820i128),
947                    ),
948                    ("f64_value", log::kv::Value::from(4.2f64)),
949                    ("bool_value", log::kv::Value::from(true)),
950                    ("bytes_value", log::kv::Value::from_serde(&Bytes([1, 1, 1]))),
951                    ("unit_value", log::kv::Value::from_serde(&())),
952                    ("some_value", log::kv::Value::from_serde(&Some(42))),
953                    ("none_value", log::kv::Value::from_serde(&None::<i32>)),
954                    (
955                        "slice_value",
956                        log::kv::Value::from_serde(&(&[1, 1, 1] as &[i32])),
957                    ),
958                    (
959                        "map_value",
960                        log::kv::Value::from_serde(&Map { a: 1, b: 1, c: 1 }),
961                    ),
962                    (
963                        "struct_value",
964                        log::kv::Value::from_serde(&Struct { a: 1, b: 1, c: 1 }),
965                    ),
966                    ("tuple_value", log::kv::Value::from_serde(&(1, 1, 1))),
967                    ("newtype_value", log::kv::Value::from_serde(&Newtype(42))),
968                    (
969                        "unit_variant_value",
970                        log::kv::Value::from_serde(&Enum::Unit),
971                    ),
972                    (
973                        "newtype_variant_value",
974                        log::kv::Value::from_serde(&Enum::Newtype(42)),
975                    ),
976                    (
977                        "struct_variant_value",
978                        log::kv::Value::from_serde(&Enum::Struct { a: 1, b: 1, c: 1 }),
979                    ),
980                    (
981                        "tuple_variant_value",
982                        log::kv::Value::from_serde(&Enum::Tuple(1, 1, 1)),
983                    ),
984                ])
985                .build(),
986        );
987
988        let logs = exporter.get_emitted_logs().unwrap();
989        assert_eq!(logs.len(), 1);
990
991        let log = logs.first().unwrap();
992        assert_eq!(log.instrumentation.name(), "");
993
994        let get = |needle: &str| -> Option<AnyValue> {
995            log.record.attributes_iter().find_map(|(k, v)| {
996                if k.as_str() == needle {
997                    Some(v.clone())
998                } else {
999                    None
1000                }
1001            })
1002        };
1003
1004        assert_eq!(
1005            AnyValue::String(StringValue::from("a string")),
1006            get("str_value").unwrap()
1007        );
1008
1009        assert_eq!(AnyValue::Int(1), get("i8_value").unwrap());
1010        assert_eq!(AnyValue::Int(2), get("i16_value").unwrap());
1011        assert_eq!(AnyValue::Int(42), get("i32_value").unwrap());
1012        assert_eq!(AnyValue::Int(2147483660), get("i64_value").unwrap());
1013        assert_eq!(AnyValue::Int(2147483660), get("i128_small_value").unwrap());
1014        assert_eq!(
1015            AnyValue::String(StringValue::from("9223372036854775820")),
1016            get("i128_big_value").unwrap()
1017        );
1018
1019        assert_eq!(AnyValue::Double(4.2), get("f64_value").unwrap());
1020
1021        assert_eq!(AnyValue::Boolean(true), get("bool_value").unwrap());
1022
1023        #[cfg(not(feature = "with-serde"))]
1024        {
1025            assert_eq!(
1026                AnyValue::String(StringValue::from("[1, 1, 1]")),
1027                get("slice_value").unwrap()
1028            );
1029
1030            assert_eq!(
1031                AnyValue::String(StringValue::from("{\"a\": 1, \"b\": 1, \"c\": 1}")),
1032                get("map_value").unwrap()
1033            );
1034
1035            assert_eq!(
1036                AnyValue::String(StringValue::from("Struct { a: 1, b: 1, c: 1 }")),
1037                get("struct_value").unwrap()
1038            );
1039
1040            assert_eq!(
1041                AnyValue::String(StringValue::from("(1, 1, 1)")),
1042                get("tuple_value").unwrap()
1043            );
1044
1045            assert_eq!(
1046                AnyValue::String(StringValue::from("Newtype(42)")),
1047                get("newtype_value").unwrap()
1048            );
1049
1050            assert_eq!(
1051                AnyValue::String(StringValue::from("Unit")),
1052                get("unit_variant_value").unwrap()
1053            );
1054
1055            assert_eq!(
1056                AnyValue::String(StringValue::from("Newtype(42)")),
1057                get("newtype_variant_value").unwrap()
1058            );
1059
1060            assert_eq!(
1061                AnyValue::String(StringValue::from("Struct { a: 1, b: 1, c: 1 }")),
1062                get("struct_variant_value").unwrap()
1063            );
1064
1065            assert_eq!(
1066                AnyValue::String(StringValue::from("Tuple(1, 1, 1)")),
1067                get("tuple_variant_value").unwrap()
1068            );
1069        }
1070        #[cfg(feature = "with-serde")]
1071        {
1072            use opentelemetry::Key;
1073            use std::collections::HashMap;
1074
1075            assert_eq!(None, get("unit_value"));
1076            assert_eq!(None, get("none_value"));
1077            assert_eq!(AnyValue::Int(42), get("some_value").unwrap());
1078
1079            assert_eq!(
1080                AnyValue::ListAny(Box::new(vec![
1081                    AnyValue::Int(1),
1082                    AnyValue::Int(1),
1083                    AnyValue::Int(1)
1084                ])),
1085                get("slice_value").unwrap()
1086            );
1087
1088            assert_eq!(
1089                AnyValue::Map({
1090                    let mut map = Box::<HashMap<Key, AnyValue>>::default();
1091
1092                    map.insert(Key::from("a"), AnyValue::Int(1));
1093                    map.insert(Key::from("b"), AnyValue::Int(1));
1094                    map.insert(Key::from("c"), AnyValue::Int(1));
1095
1096                    map
1097                }),
1098                get("map_value").unwrap()
1099            );
1100
1101            assert_eq!(
1102                AnyValue::Map({
1103                    let mut map = Box::<HashMap<Key, AnyValue>>::default();
1104
1105                    map.insert(Key::from("a"), AnyValue::Int(1));
1106                    map.insert(Key::from("b"), AnyValue::Int(1));
1107                    map.insert(Key::from("c"), AnyValue::Int(1));
1108
1109                    map
1110                }),
1111                get("struct_value").unwrap()
1112            );
1113
1114            assert_eq!(
1115                AnyValue::ListAny(Box::new(vec![
1116                    AnyValue::Int(1),
1117                    AnyValue::Int(1),
1118                    AnyValue::Int(1)
1119                ])),
1120                get("tuple_value").unwrap()
1121            );
1122
1123            assert_eq!(
1124                AnyValue::String(StringValue::from("Unit")),
1125                get("unit_variant_value").unwrap()
1126            );
1127
1128            assert_eq!(
1129                AnyValue::Map({
1130                    let mut map = HashMap::new();
1131
1132                    map.insert(Key::from("Newtype"), AnyValue::Int(42));
1133
1134                    Box::new(map)
1135                }),
1136                get("newtype_variant_value").unwrap()
1137            );
1138
1139            assert_eq!(
1140                AnyValue::Map({
1141                    let mut map = HashMap::new();
1142
1143                    map.insert(
1144                        Key::from("Struct"),
1145                        AnyValue::Map(Box::new({
1146                            let mut map = HashMap::new();
1147                            map.insert(Key::from("a"), AnyValue::Int(1));
1148                            map.insert(Key::from("b"), AnyValue::Int(1));
1149                            map.insert(Key::from("c"), AnyValue::Int(1));
1150                            map
1151                        })),
1152                    );
1153
1154                    Box::new(map)
1155                }),
1156                get("struct_variant_value").unwrap()
1157            );
1158
1159            assert_eq!(
1160                AnyValue::Map({
1161                    let mut map = HashMap::new();
1162
1163                    map.insert(
1164                        Key::from("Tuple"),
1165                        AnyValue::ListAny(Box::new(vec![
1166                            AnyValue::Int(1),
1167                            AnyValue::Int(1),
1168                            AnyValue::Int(1),
1169                        ])),
1170                    );
1171
1172                    Box::new(map)
1173                }),
1174                get("tuple_variant_value").unwrap()
1175            );
1176        }
1177    }
1178
1179    #[cfg(feature = "experimental_metadata_attributes")]
1180    #[test]
1181    fn logbridge_code_attributes() {
1182        use opentelemetry_semantic_conventions::attribute::{
1183            CODE_FILE_PATH, CODE_FUNCTION_NAME, CODE_LINE_NUMBER,
1184        };
1185
1186        let exporter = InMemoryLogExporter::default();
1187
1188        let logger_provider = SdkLoggerProvider::builder()
1189            .with_simple_exporter(exporter.clone())
1190            .build();
1191
1192        let otel_log_appender = OpenTelemetryLogBridge::new(&logger_provider);
1193
1194        otel_log_appender.log(
1195            &log::RecordBuilder::new()
1196                .level(log::Level::Warn)
1197                .args(format_args!("WARN"))
1198                .file(Some("src/main.rs"))
1199                .module_path(Some("service"))
1200                .line(Some(101))
1201                .build(),
1202        );
1203
1204        let logs = exporter.get_emitted_logs().unwrap();
1205        assert_eq!(logs.len(), 1);
1206
1207        let log = logs.first().unwrap();
1208        assert_eq!(log.instrumentation.name(), "");
1209
1210        let get = |needle: &str| -> Option<AnyValue> {
1211            log.record.attributes_iter().find_map(|(k, v)| {
1212                if k.as_str() == needle {
1213                    Some(v.clone())
1214                } else {
1215                    None
1216                }
1217            })
1218        };
1219
1220        assert_eq!(
1221            Some(AnyValue::String(StringValue::from("src/main.rs"))),
1222            get(CODE_FILE_PATH)
1223        );
1224        assert_eq!(
1225            Some(AnyValue::String(StringValue::from("service"))),
1226            get(CODE_FUNCTION_NAME)
1227        );
1228        assert_eq!(Some(AnyValue::Int(101)), get(CODE_LINE_NUMBER));
1229    }
1230
1231    #[test]
1232    fn test_flush() {
1233        let exporter = InMemoryLogExporter::default();
1234
1235        let logger_provider = SdkLoggerProvider::builder()
1236            .with_simple_exporter(exporter)
1237            .build();
1238
1239        let otel_log_appender = OpenTelemetryLogBridge::new(&logger_provider);
1240        otel_log_appender.flush();
1241    }
1242
1243    #[test]
1244    fn check_level_severities() {
1245        assert_eq!(
1246            super::severity_of_level(log::Level::Error),
1247            opentelemetry::logs::Severity::Error
1248        );
1249        assert_eq!(
1250            super::severity_of_level(log::Level::Warn),
1251            opentelemetry::logs::Severity::Warn
1252        );
1253        assert_eq!(
1254            super::severity_of_level(log::Level::Info),
1255            opentelemetry::logs::Severity::Info
1256        );
1257        assert_eq!(
1258            super::severity_of_level(log::Level::Debug),
1259            opentelemetry::logs::Severity::Debug
1260        );
1261        assert_eq!(
1262            super::severity_of_level(log::Level::Trace),
1263            opentelemetry::logs::Severity::Trace
1264        );
1265    }
1266}