diff --git a/iceoryx2-bb/log/src/logger/file.rs b/iceoryx2-bb/log/src/logger/file.rs new file mode 100644 index 000000000..cd43a4733 --- /dev/null +++ b/iceoryx2-bb/log/src/logger/file.rs @@ -0,0 +1,146 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! # Example +//! +//! Using the file logger. +//! +//! ```no_run +//! use iceoryx2_bb_log::{info, set_logger, set_log_level, LogLevel, logger::file}; +//! use std::sync::LazyLock; +//! +//! const LOG_FILE: &str = "fuu.log"; +//! static FILE_LOGGER: LazyLock = LazyLock::new(|| file::Logger::new(LOG_FILE)); +//! set_logger(&*FILE_LOGGER); +//! set_log_level(LogLevel::Trace); +//! +//! // written into log file "fuu.log" +//! info!("hello world"); +//! ``` + +// TODO: [Reminder to my future self] +// In the long-term the file logger may be required to be based on the same +// iceoryx2_pal_posix platform. In this case, the logger needs to use the low-level calls directly +// to avoid a circular dependency with iceoryx2_bb_posix. +use std::{ + fmt::Debug, + fs::OpenOptions, + io::Write, + sync::{mpsc::Sender, Arc}, + thread::JoinHandle, + time::{Duration, Instant, SystemTime}, +}; + +use std::sync::mpsc::channel; + +use crate::{get_log_level, LogLevel}; + +enum Message { + Entry(Entry), + Stop, +} + +struct Entry { + timestamp: Duration, + elapsed_time: Duration, + log_level: LogLevel, + origin: String, + message: String, +} + +impl Debug for Entry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "timestamp: {:?}, elapsed_time: {:?}, log_level: {:?}, origin: {}, message: {}", + self.timestamp, self.elapsed_time, self.log_level, self.origin, self.message + ) + } +} + +/// A logger that logs all messages into a file. It implements an active object pattern. A +/// background thread waits on a queue of log messages and whenever a new message is added. +pub struct Logger { + sender: Arc>, + start_time: Instant, + _background_thread: Arc>>, +} + +impl Logger { + /// Creates a new file logger. + pub fn new(file_name: &str) -> Self { + let mut file = OpenOptions::new() + .append(true) + .create(true) + .open(file_name) + .expect("Open log file for writing."); + + let (sender, receiver) = channel(); + + let write_buffer_to_file = move || loop { + match receiver.recv() { + Ok(Message::Entry(entry)) => file + .write_all(format!("{:?}\n", entry).as_bytes()) + .expect("Writing log message into log file."), + Ok(Message::Stop) => break, + Err(e) => file + .write_all( + format!("[This should never happen!] File Logger got error: {:?}", e) + .as_bytes(), + ) + .expect("Write log message into log file."), + }; + file.sync_all().expect("Sync log file with disc."); + }; + + Self { + sender: Arc::new(sender), + _background_thread: Arc::new(Some(std::thread::spawn(write_buffer_to_file))), + start_time: Instant::now(), + } + } +} + +impl Drop for Logger { + fn drop(&mut self) { + self.sender + .send(Message::Stop) + .expect("Send stop notification to background thread."); + } +} + +impl crate::Logger for Logger { + fn log( + &self, + log_level: LogLevel, + origin: std::fmt::Arguments, + formatted_message: std::fmt::Arguments, + ) { + if get_log_level() > log_level as u8 { + return; + } + + self.sender + .send({ + Message::Entry(Entry { + log_level, + timestamp: SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("Acquire current system time."), + elapsed_time: self.start_time.elapsed(), + origin: origin.to_string(), + message: formatted_message.to_string(), + }) + }) + .expect("Send log message to log thread."); + } +} diff --git a/iceoryx2-bb/log/src/logger/mod.rs b/iceoryx2-bb/log/src/logger/mod.rs index f64dba9e6..1a8ba43fe 100644 --- a/iceoryx2-bb/log/src/logger/mod.rs +++ b/iceoryx2-bb/log/src/logger/mod.rs @@ -15,6 +15,7 @@ pub mod buffer; pub mod console; +pub mod file; #[cfg(feature = "logger_log")] pub mod log; #[cfg(feature = "logger_tracing")]