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}