Skip to content

Commit 6f20ebd

Browse files
authored
Merge pull request #465 from elfenpiff/iox2-464-file-logger
[#464] file logger
2 parents e5b44a9 + fa4a525 commit 6f20ebd

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed

iceoryx2-bb/log/src/logger/file.rs

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// Copyright (c) 2024 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+
//! # Example
14+
//!
15+
//! Using the file logger.
16+
//!
17+
//! ```no_run
18+
//! use iceoryx2_bb_log::{info, set_logger, set_log_level, LogLevel, logger::file};
19+
//! use std::sync::LazyLock;
20+
//!
21+
//! const LOG_FILE: &str = "fuu.log";
22+
//! static FILE_LOGGER: LazyLock<file::Logger> = LazyLock::new(|| file::Logger::new(LOG_FILE));
23+
//! set_logger(&*FILE_LOGGER);
24+
//! set_log_level(LogLevel::Trace);
25+
//!
26+
//! // written into log file "fuu.log"
27+
//! info!("hello world");
28+
//! ```
29+
30+
// TODO: [Reminder to my future self]
31+
// In the long-term the file logger may be required to be based on the same
32+
// iceoryx2_pal_posix platform. In this case, the logger needs to use the low-level calls directly
33+
// to avoid a circular dependency with iceoryx2_bb_posix.
34+
use std::{
35+
fmt::Debug,
36+
fs::OpenOptions,
37+
io::Write,
38+
sync::{mpsc::Sender, Arc},
39+
thread::JoinHandle,
40+
time::{Duration, Instant, SystemTime},
41+
};
42+
43+
use std::sync::mpsc::channel;
44+
45+
use crate::{get_log_level, LogLevel};
46+
47+
enum Message {
48+
Entry(Entry),
49+
Stop,
50+
}
51+
52+
struct Entry {
53+
timestamp: Duration,
54+
elapsed_time: Duration,
55+
log_level: LogLevel,
56+
origin: String,
57+
message: String,
58+
}
59+
60+
impl Debug for Entry {
61+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62+
write!(
63+
f,
64+
"timestamp: {:?}, elapsed_time: {:?}, log_level: {:?}, origin: {}, message: {}",
65+
self.timestamp, self.elapsed_time, self.log_level, self.origin, self.message
66+
)
67+
}
68+
}
69+
70+
/// A logger that logs all messages into a file. It implements an active object pattern. A
71+
/// background thread waits on a queue of log messages and whenever a new message is added.
72+
pub struct Logger {
73+
sender: Arc<Sender<Message>>,
74+
start_time: Instant,
75+
_background_thread: Arc<Option<JoinHandle<()>>>,
76+
}
77+
78+
impl Logger {
79+
/// Creates a new file logger.
80+
pub fn new(file_name: &str) -> Self {
81+
let mut file = OpenOptions::new()
82+
.append(true)
83+
.create(true)
84+
.open(file_name)
85+
.expect("Open log file for writing.");
86+
87+
let (sender, receiver) = channel();
88+
89+
let write_buffer_to_file = move || loop {
90+
match receiver.recv() {
91+
Ok(Message::Entry(entry)) => file
92+
.write_all(format!("{:?}\n", entry).as_bytes())
93+
.expect("Writing log message into log file."),
94+
Ok(Message::Stop) => break,
95+
Err(e) => file
96+
.write_all(
97+
format!("[This should never happen!] File Logger got error: {:?}", e)
98+
.as_bytes(),
99+
)
100+
.expect("Write log message into log file."),
101+
};
102+
file.sync_all().expect("Sync log file with disc.");
103+
};
104+
105+
Self {
106+
sender: Arc::new(sender),
107+
_background_thread: Arc::new(Some(std::thread::spawn(write_buffer_to_file))),
108+
start_time: Instant::now(),
109+
}
110+
}
111+
}
112+
113+
impl Drop for Logger {
114+
fn drop(&mut self) {
115+
self.sender
116+
.send(Message::Stop)
117+
.expect("Send stop notification to background thread.");
118+
}
119+
}
120+
121+
impl crate::Logger for Logger {
122+
fn log(
123+
&self,
124+
log_level: LogLevel,
125+
origin: std::fmt::Arguments,
126+
formatted_message: std::fmt::Arguments,
127+
) {
128+
if get_log_level() > log_level as u8 {
129+
return;
130+
}
131+
132+
self.sender
133+
.send({
134+
Message::Entry(Entry {
135+
log_level,
136+
timestamp: SystemTime::now()
137+
.duration_since(SystemTime::UNIX_EPOCH)
138+
.expect("Acquire current system time."),
139+
elapsed_time: self.start_time.elapsed(),
140+
origin: origin.to_string(),
141+
message: formatted_message.to_string(),
142+
})
143+
})
144+
.expect("Send log message to log thread.");
145+
}
146+
}

iceoryx2-bb/log/src/logger/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
1616
pub mod buffer;
1717
pub mod console;
18+
pub mod file;
1819
#[cfg(feature = "logger_log")]
1920
pub mod log;
2021
#[cfg(feature = "logger_tracing")]

0 commit comments

Comments
 (0)