iceoryx2_bb_log/
lib.rs

1// Copyright (c) 2023 Contributors to the Eclipse Foundation
2//
3// See the NOTICE file(s) distributed with this work for additional
4// information regarding copyright ownership.
5//
6// This program and the accompanying materials are made available under the
7// terms of the Apache Software License 2.0 which is available at
8// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
9// which is available at https://opensource.org/licenses/MIT.
10//
11// SPDX-License-Identifier: Apache-2.0 OR MIT
12
13#![warn(clippy::alloc_instead_of_core)]
14#![warn(clippy::std_instead_of_alloc)]
15#![warn(clippy::std_instead_of_core)]
16
17//! Simplistic logger. It has 6 [`LogLevel`]s which can be set via [`set_log_level()`] and read via
18//! [`get_log_level()`].
19//!
20//! The logger provides convinience macros to combine error/panic handling directly with the
21//! logger.
22//! The [`fail!`] macro can return when the function which was called return an error containing
23//! result.
24//! The [`fatal_panic!`] macro calls [`panic!`].
25//!
26//! # Example
27//!
28//! ## Logging
29//! ```
30//! use iceoryx2_bb_log::{debug, error, info, trace, warn};
31//!
32//! #[derive(Debug)]
33//! struct MyDataType {
34//!     value: u64
35//! }
36//!
37//! impl MyDataType {
38//!     fn log_stuff(&self) {
39//!         trace!("trace message");
40//!         trace!(from self, "trace message");
41//!         trace!(from "Custom::Origin", "trace message");
42//!
43//!         debug!("hello {} {}", 123, 456);
44//!         debug!(from self, "hello {}", 123);
45//!         debug!(from "Another::Origin", "hello {}", 123);
46//!
47//!         info!("world");
48//!         info!(from self, "world");
49//!         info!(from "hello", "world");
50//!
51//!         warn!("warn message");
52//!         warn!(from self, "warning");
53//!         warn!(from "Somewhere::Else", "warning!");
54//!
55//!         error!("bla {}", 1);
56//!         error!(from self, "bla {}", 1);
57//!         error!(from "error origin", "bla {}", 1);
58//!     }
59//!}
60//! ```
61//!
62//! ## Error Handling
63//! ```
64//! use iceoryx2_bb_log::fail;
65//!
66//! #[derive(Debug)]
67//! struct MyDataType {
68//!     value: u64
69//! }
70//!
71//! impl MyDataType {
72//!     fn doStuff(&self, value: u64) -> Result<(), ()> {
73//!         if value == 0 { Err(()) } else { Ok(()) }
74//!     }
75//!
76//!     fn doMoreStuff(&self) -> Result<(), u64> {
77//!         // fail when doStuff.is_err() and return the error 1234
78//!         fail!(from self, when self.doStuff(0),
79//!                 with 1234, "Failed while calling doStuff");
80//!         Ok(())
81//!     }
82//!
83//!     fn doMore(&self) -> Result<(), u64> {
84//!         if self.value == 0 {
85//!             // without condition, return error 4567
86//!             fail!(from self, with 4567, "Value is zero");
87//!         }
88//!
89//!         Ok(())
90//!     }
91//!
92//!     fn evenMore(&self) -> Result<(), u64> {
93//!         // forward error when it is compatible or convertable
94//!         fail!(from self, when self.doMore(), "doMore failed");
95//!         Ok(())
96//!     }
97//! }
98//! ```
99//!
100//! ## Panic Handling
101//! ```
102//! use iceoryx2_bb_log::fatal_panic;
103//!
104//! #[derive(Debug)]
105//! struct MyDataType {
106//!     value: u64
107//! }
108//!
109//! impl MyDataType {
110//!     fn doStuff(&self, value: u64) {
111//!         if value == 0 {
112//!             fatal_panic!(from self, "value is {}", value);
113//!         }
114//!     }
115//!
116//!     fn moreStuff(&self) -> Result<(), ()> {
117//!         if self.value == 0 { Err(()) } else { Ok(()) }
118//!     }
119//!
120//!     fn doIt(&self) {
121//!         fatal_panic!(from self, when self.moreStuff(), "moreStuff failed");
122//!     }
123//! }
124//! ```
125//! ## Setting custom logger on application startup
126//!
127//! In this example we use the [`crate::logger::buffer::Logger`], that stores every log
128//! message in an internal buffer, and use it as the default logger.
129//!
130//! ```
131//! use iceoryx2_bb_log::{set_logger, info};
132//!
133//! static LOGGER: iceoryx2_bb_log::logger::buffer::Logger =
134//!     iceoryx2_bb_log::logger::buffer::Logger::new();
135//!
136//! assert!(set_logger(&LOGGER));
137//! info!("hello world");
138//! let log_content = LOGGER.content();
139//!
140//! for entry in log_content {
141//!     println!("{:?} {} {}", entry.log_level, entry.origin, entry.message);
142//! }
143//! ```
144
145extern crate alloc;
146
147#[macro_use]
148pub mod log;
149#[macro_use]
150pub mod fail;
151pub mod logger;
152
153use iceoryx2_pal_concurrency_sync::iox_atomic::IoxAtomicU8;
154
155use core::{fmt::Arguments, sync::atomic::Ordering};
156use std::sync::Once;
157
158use std::env;
159
160#[cfg(feature = "logger_tracing")]
161static DEFAULT_LOGGER: logger::tracing::Logger = logger::tracing::Logger::new();
162
163#[cfg(feature = "logger_log")]
164static DEFAULT_LOGGER: logger::log::Logger = logger::log::Logger::new();
165
166#[cfg(not(any(feature = "logger_log", feature = "logger_tracing")))]
167static DEFAULT_LOGGER: logger::console::Logger = logger::console::Logger::new();
168
169const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Info;
170
171static mut LOGGER: Option<&'static dyn Log> = None;
172static LOG_LEVEL: IoxAtomicU8 = IoxAtomicU8::new(DEFAULT_LOG_LEVEL as u8);
173static INIT: Once = Once::new();
174
175pub trait Log: Send + Sync {
176    /// logs a message
177    fn log(&self, log_level: LogLevel, origin: Arguments, formatted_message: Arguments);
178}
179
180/// Describes the log level.
181#[repr(u8)]
182#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
183pub enum LogLevel {
184    Trace = 0,
185    Debug = 1,
186    Info = 2,
187    Warn = 3,
188    Error = 4,
189    Fatal = 5,
190}
191
192impl LogLevel {
193    fn from_str_fuzzy(log_level_string: &str, log_level_fallback: LogLevel) -> LogLevel {
194        match log_level_string.to_lowercase().as_str() {
195            "trace" => LogLevel::Trace,
196            "debug" => LogLevel::Debug,
197            "info" => LogLevel::Info,
198            "warn" => LogLevel::Warn,
199            "error" => LogLevel::Error,
200            "fatal" => LogLevel::Fatal,
201            _ => {
202                eprintln!(
203                    "Invalid value for 'IOX2_LOG_LEVEL' environment variable!\
204                \nFound: {log_level_string:?}\
205                \nAllowed is one of: fatal, error, warn, info, debug, trace\
206                \nSetting log level as : {log_level_fallback:?}"
207                );
208                log_level_fallback
209            }
210        }
211    }
212}
213
214/// Sets the log level by reading environment variable "IOX2_LOG_LEVEL" or default it with LogLevel::INFO
215pub fn set_log_level_from_env_or_default() {
216    set_log_level_from_env_or(DEFAULT_LOG_LEVEL);
217}
218
219/// Sets the log level by reading environment variable "IOX2_LOG_LEVEL", and if the environment variable
220/// doesn't exit it sets it with a user-defined logging level
221pub fn set_log_level_from_env_or(v: LogLevel) {
222    let log_level = env::var("IOX2_LOG_LEVEL")
223        .ok()
224        .map(|s| LogLevel::from_str_fuzzy(&s, v))
225        .unwrap_or(v);
226    set_log_level(log_level);
227}
228
229/// Sets the current log level. This is ignored for external frameworks like `log` or `tracing`.
230/// Here you have to use the log-level settings of that framework.
231pub fn set_log_level(v: LogLevel) {
232    LOG_LEVEL.store(v as u8, Ordering::Relaxed);
233}
234
235/// Returns the current log level
236pub fn get_log_level() -> u8 {
237    LOG_LEVEL.load(Ordering::Relaxed)
238}
239
240/// Sets the [`Log`]ger. Can be only called once at the beginning of the program. If the
241/// [`Log`]ger is already set it returns false and does not update it.
242pub fn set_logger<T: Log + 'static>(value: &'static T) -> bool {
243    let mut set_logger_success = false;
244    INIT.call_once(|| {
245        unsafe { LOGGER = Some(value) };
246        set_logger_success = true;
247    });
248    set_logger_success
249}
250
251/// Returns a reference to the [`Log`]ger.
252pub fn get_logger() -> &'static dyn Log {
253    INIT.call_once(|| {
254        unsafe { LOGGER = Some(&DEFAULT_LOGGER) };
255    });
256
257    // # From The Compiler
258    //
259    // shared references to mutable statics are dangerous; it's undefined behavior
260    //   1. if the static is mutated or
261    //   2. if a mutable reference is created for it while the shared reference lives
262    //
263    // # Safety
264    //
265    // 1. The logger is always an immutable threadsafe object with only interior mutability.
266    // 2. [`std::sync::Once`] is used to ensure it can only mutated on initialization and the
267    //    lifetime is `'static`.
268    #[allow(static_mut_refs)]
269    unsafe {
270        *LOGGER.as_ref().unwrap()
271    }
272}
273
274#[doc(hidden)]
275pub fn __internal_print_log_msg(log_level: LogLevel, origin: Arguments, args: Arguments) {
276    if get_log_level() <= log_level as u8 {
277        get_logger().log(log_level, origin, args)
278    }
279}