From 7bc62cb12d5a116e0591234455865f2843288eae Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 4 Feb 2023 11:40:59 +0100 Subject: [PATCH 01/21] Replaced time_tz by chrono + rewrite logging system to buffer all formatting --- Cargo.toml | 5 +-- src/backend.rs | 6 +-- src/internal.rs | 62 +++++++++++----------------- src/lib.rs | 22 +++------- src/log_msg.rs | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 141 insertions(+), 61 deletions(-) create mode 100644 src/log_msg.rs diff --git a/Cargo.toml b/Cargo.toml index d1a5d95..050614a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bp3d-logger" -version = "1.1.0" +version = "2.0.0-pre.1.0.0" authors = ["Yuri Edward "] edition = "2021" description = "A flexible Log implementation intended to be used with BP3D software." @@ -17,8 +17,7 @@ log = "0.4.14" bp3d-fs = "1.0.0" crossbeam-channel = "0.5.2" once_cell = "1.10.0" -time = { version = "0.3.7", features = ["formatting", "macros"] } -time-tz = { version = "0.3.1", features = ["system"] } +chrono = "0.4.23" termcolor = "1.1.3" atty = "0.2.14" diff --git a/src/backend.rs b/src/backend.rs index b3d7331..e690b37 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2021, BlockProject 3D +// Copyright (c) 2023, BlockProject 3D // // All rights reserved. // @@ -72,8 +72,8 @@ fn write_msg(stream: StandardStream, target: &str, msg: &str, level: Level) { .color(color(level)) .write(level) .reset() - .write(']') - .write(format!(" {}", msg)) + .write("] ") + .write(msg) .lf(); } diff --git a/src/internal.rs b/src/internal.rs index 9f8c091..54c83ed 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2021, BlockProject 3D +// Copyright (c) 2023, BlockProject 3D // // All rights reserved. // @@ -32,16 +32,15 @@ use crossbeam_channel::{bounded, Receiver, Sender}; use log::{Level, Log, Metadata, Record}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; -use time::macros::format_description; -use time::OffsetDateTime; -use time_tz::OffsetDateTimeExt; +use chrono::Local; +use std::fmt::Write; -const BUF_SIZE: usize = 128; // The maximum count of log messages in the channel. +const BUF_SIZE: usize = 16; // The maximum count of log messages in the channel. enum Command { Flush, Log(LogMsg), - Terminate, + Terminate } fn log( @@ -73,8 +72,11 @@ fn exec_commad(cmd: Command, logger: &mut Logger) -> bool { } false } - Command::Log(LogMsg { target, msg, level }) => { - if let Err(e) = log(logger.file.as_mut(), &target, &msg, level) { + Command::Log(buffer) => { + let target = buffer.target(); + let msg = buffer.msg(); + let level = buffer.level(); + if let Err(e) = log(logger.file.as_mut(), target, msg, level) { let _ = log( logger.std.as_mut(), "bp3d-logger", @@ -82,20 +84,20 @@ fn exec_commad(cmd: Command, logger: &mut Logger) -> bool { Level::Error, ); } - let _ = log(logger.std.as_mut(), &target, &msg, level); + let _ = log(logger.std.as_mut(), target, msg, level); false } } } pub struct LoggerImpl { - thread: Mutex>>, send_ch: Sender, recv_ch: Receiver, + enabled: AtomicBool, log_buffer_send_ch: Sender, log_buffer_recv_ch: Receiver, enable_log_buffer: AtomicBool, - enabled: AtomicBool, + thread: Mutex>> } impl LoggerImpl { @@ -177,17 +179,13 @@ impl LoggerImpl { // This cannot panic as send_ch is owned by LoggerImpl which is intended // to be statically allocated. self.send_ch - .send(Command::Log(LogMsg { - level: Level::Error, - msg: "The logging thread has panicked!".into(), - target: "bp3d-logger".into(), - })) + .send(Command::Log(LogMsg::from_msg("bp3d-logger", Level::Error, "The logging thread has panicked!"))) .unwrap_unchecked(); } } } - pub fn low_level_log(&self, msg: LogMsg) { + pub fn low_level_log(&self, msg: &LogMsg) { if self.enable_log_buffer.load(Ordering::Acquire) { unsafe { // This cannot panic as both send_ch and log_buffer_send_ch are owned by LoggerImpl @@ -195,17 +193,18 @@ impl LoggerImpl { self.send_ch .send(Command::Log(msg.clone())) .unwrap_unchecked(); - self.log_buffer_send_ch.send(msg).unwrap_unchecked(); + self.log_buffer_send_ch.send(msg.clone()).unwrap_unchecked(); } } else { unsafe { // This cannot panic as send_ch is owned by LoggerImpl which is intended // to be statically allocated. - self.send_ch.send(Command::Log(msg)).unwrap_unchecked(); + self.send_ch.send(Command::Log(msg.clone())).unwrap_unchecked(); } } } + #[inline] pub fn is_enabled(&self) -> bool { self.enabled.load(Ordering::Acquire) } @@ -232,26 +231,11 @@ impl Log for LoggerImpl { return; } let (target, module) = extract_target_module(record); - //In the future attempt to not update all the time https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=17c218f318826f55ab64535bfcd28ec6 - let system_tz = - time_tz::system::get_timezone().unwrap_or(time_tz::timezones::db::us::CENTRAL); - let format = format_description!("[weekday repr:short] [month repr:short] [day] [hour repr:12]:[minute]:[second] [period case:upper]"); - // is very unlikely to occur (only possibility is a weird io error). - let formatted = OffsetDateTime::now_utc() - .to_timezone(system_tz) - .format(format) - .unwrap_or_else(|_| "".into()); - let msg = LogMsg { - msg: format!( - "({}) {}: {}", - formatted, - module.unwrap_or("main"), - record.args() - ), - target: target.into(), - level: record.level(), - }; - self.low_level_log(msg); + let time = Local::now(); + let formatted = time.format("%a %b %d %Y %I:%M:%S %P"); + let mut msg = LogMsg::new(target, record.level()); + let _ = write!(msg, "({}) {}: {}", formatted, module.unwrap_or("main"), record.args()); + self.low_level_log(&msg); } fn flush(&self) { diff --git a/src/lib.rs b/src/lib.rs index d4ff517..244931f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2021, BlockProject 3D +// Copyright (c) 2023, BlockProject 3D // // All rights reserved. // @@ -33,27 +33,17 @@ mod backend; mod easy_termcolor; mod internal; +mod log_msg; use crate::backend::ENABLE_STDOUT; use bp3d_fs::dirs::App; use crossbeam_channel::Receiver; -use log::{Level, Log}; +use log::Log; use once_cell::sync::Lazy; use std::path::PathBuf; use std::sync::atomic::Ordering; -/// Represents a log message in the [LogBuffer](crate::LogBuffer). -#[derive(Clone)] -pub struct LogMsg { - /// The message string. - pub msg: String, - - /// The crate name that issued this log. - pub target: String, - - /// The log level. - pub level: Level, -} +pub use log_msg::LogMsg; /// The log buffer type. pub type LogBuffer = Receiver; @@ -207,7 +197,7 @@ impl Logger { /// Enables file logging to the given application. /// - /// The application is given as a reference to [GetLogs](crate::GetLogs) to allow obtaining + /// The application is given as a reference to [GetLogs](GetLogs) to allow obtaining /// a log directory from various sources. /// /// If the log directory could not be found the function prints an error to stderr. @@ -299,7 +289,7 @@ pub fn get_log_buffer() -> LogBuffer { /// - For stdout/stderr backend the format is \[level\] msg /// - For file backend the format is \[level\] msg and the message is recorded in the file /// corresponding to the log target. -pub fn raw_log(msg: LogMsg) { +pub fn raw_log(msg: &LogMsg) { BP3D_LOGGER.low_level_log(msg) } diff --git a/src/log_msg.rs b/src/log_msg.rs new file mode 100644 index 0000000..52153f4 --- /dev/null +++ b/src/log_msg.rs @@ -0,0 +1,107 @@ +// Copyright (c) 2023, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::fmt::{Error, Write}; +use std::mem::MaybeUninit; +use log::Level; + +// Limit the size of the target string to 16 bytes. +const LOG_TARGET_SIZE: usize = 16; +// Size of the control fields of the log message structure: +// sizeof Level + 1 byte for target_len + sizeof msg_len +const LOG_CONTROL_SIZE: usize = std::mem::size_of::() + std::mem::size_of::() + 1; +// Limit the size of the log message string so that the size of the log structure is LOG_BUFFER_SIZE +const LOG_MSG_SIZE: usize = LOG_BUFFER_SIZE - LOG_TARGET_SIZE - LOG_CONTROL_SIZE; +const LOG_BUFFER_SIZE: usize = 1024; + +#[derive(Clone)] +pub struct LogMsg { + buffer: [MaybeUninit; LOG_BUFFER_SIZE], + level: Level, + msg_len: u32, + target_len: u8 +} + +impl LogMsg { + pub fn new(target: &str, level: Level) -> LogMsg { + let len = std::cmp::min(LOG_TARGET_SIZE, target.as_bytes().len()); + let mut buffer = LogMsg { + buffer: unsafe { MaybeUninit::uninit().assume_init() }, + target_len: len as _, + msg_len: len as _, + level + }; + unsafe { + std::ptr::copy_nonoverlapping(target.as_bytes().as_ptr(), std::mem::transmute(buffer.buffer.as_mut_ptr()), len); + } + buffer + } + + pub fn clear(&mut self) { + self.msg_len = self.target_len as _; + } + + pub fn from_msg(target: &str, level: Level, msg: &str) -> LogMsg { + let mut ads = Self::new(target, level); + unsafe { ads.write(msg.as_bytes()) }; + ads + } + + // SAFETY: BufLogMsg must always contain valid UTF-8 so ensure that buf only contains valid UTF-8 data. + pub unsafe fn write(&mut self, buf: &[u8]) -> usize { + let len = std::cmp::min(buf.len(), LOG_MSG_SIZE - self.msg_len as usize); + if len > 0 { + std::ptr::copy_nonoverlapping(buf.as_ptr(), std::mem::transmute(self.buffer.as_mut_ptr().offset(self.msg_len as _)), len); + self.msg_len += len as u32; //The length is always less than 2^32. + } + len + } + + pub fn target(&self) -> &str { + // SAFEY: This is always safe because BufLogMsg is always UTF-8. + unsafe { std::str::from_utf8_unchecked(std::mem::transmute(&self.buffer[..self.target_len as _])) } + } + + pub fn msg(&self) -> &str { + // SAFEY: This is always safe because BufLogMsg is always UTF-8. + unsafe { std::str::from_utf8_unchecked(std::mem::transmute(&self.buffer[self.target_len as _..self.msg_len as _])) } + } + + pub fn level(&self) -> Level { + self.level + } +} + +impl Write for LogMsg { + fn write_str(&mut self, s: &str) -> Result<(), Error> { + unsafe { + self.write(s.as_bytes()); + } + Ok(()) + } +} From d7f9e8666cc24c0784698a5950074bbac841996e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 4 Feb 2023 12:49:08 +0100 Subject: [PATCH 02/21] Updated version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 050614a..5503d90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bp3d-logger" -version = "2.0.0-pre.1.0.0" +version = "2.0.0-pre.2.0.0" authors = ["Yuri Edward "] edition = "2021" description = "A flexible Log implementation intended to be used with BP3D software." From 86350b657fc32f33dcfe8e74f9464a6ab8458b4d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 4 Feb 2023 11:52:48 +0000 Subject: [PATCH 03/21] Format Rust code using rustfmt --- src/internal.rs | 26 +++++++++++++++++++------- src/log_msg.rs | 28 +++++++++++++++++++++------- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/internal.rs b/src/internal.rs index 54c83ed..c9ca4cc 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -28,19 +28,19 @@ use crate::backend::Backend; use crate::{LogMsg, Logger}; +use chrono::Local; use crossbeam_channel::{bounded, Receiver, Sender}; use log::{Level, Log, Metadata, Record}; +use std::fmt::Write; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; -use chrono::Local; -use std::fmt::Write; const BUF_SIZE: usize = 16; // The maximum count of log messages in the channel. enum Command { Flush, Log(LogMsg), - Terminate + Terminate, } fn log( @@ -97,7 +97,7 @@ pub struct LoggerImpl { log_buffer_send_ch: Sender, log_buffer_recv_ch: Receiver, enable_log_buffer: AtomicBool, - thread: Mutex>> + thread: Mutex>>, } impl LoggerImpl { @@ -179,7 +179,11 @@ impl LoggerImpl { // This cannot panic as send_ch is owned by LoggerImpl which is intended // to be statically allocated. self.send_ch - .send(Command::Log(LogMsg::from_msg("bp3d-logger", Level::Error, "The logging thread has panicked!"))) + .send(Command::Log(LogMsg::from_msg( + "bp3d-logger", + Level::Error, + "The logging thread has panicked!", + ))) .unwrap_unchecked(); } } @@ -199,7 +203,9 @@ impl LoggerImpl { unsafe { // This cannot panic as send_ch is owned by LoggerImpl which is intended // to be statically allocated. - self.send_ch.send(Command::Log(msg.clone())).unwrap_unchecked(); + self.send_ch + .send(Command::Log(msg.clone())) + .unwrap_unchecked(); } } } @@ -234,7 +240,13 @@ impl Log for LoggerImpl { let time = Local::now(); let formatted = time.format("%a %b %d %Y %I:%M:%S %P"); let mut msg = LogMsg::new(target, record.level()); - let _ = write!(msg, "({}) {}: {}", formatted, module.unwrap_or("main"), record.args()); + let _ = write!( + msg, + "({}) {}: {}", + formatted, + module.unwrap_or("main"), + record.args() + ); self.low_level_log(&msg); } diff --git a/src/log_msg.rs b/src/log_msg.rs index 52153f4..0080e27 100644 --- a/src/log_msg.rs +++ b/src/log_msg.rs @@ -26,9 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use log::Level; use std::fmt::{Error, Write}; use std::mem::MaybeUninit; -use log::Level; // Limit the size of the target string to 16 bytes. const LOG_TARGET_SIZE: usize = 16; @@ -44,7 +44,7 @@ pub struct LogMsg { buffer: [MaybeUninit; LOG_BUFFER_SIZE], level: Level, msg_len: u32, - target_len: u8 + target_len: u8, } impl LogMsg { @@ -54,10 +54,14 @@ impl LogMsg { buffer: unsafe { MaybeUninit::uninit().assume_init() }, target_len: len as _, msg_len: len as _, - level + level, }; unsafe { - std::ptr::copy_nonoverlapping(target.as_bytes().as_ptr(), std::mem::transmute(buffer.buffer.as_mut_ptr()), len); + std::ptr::copy_nonoverlapping( + target.as_bytes().as_ptr(), + std::mem::transmute(buffer.buffer.as_mut_ptr()), + len, + ); } buffer } @@ -76,7 +80,11 @@ impl LogMsg { pub unsafe fn write(&mut self, buf: &[u8]) -> usize { let len = std::cmp::min(buf.len(), LOG_MSG_SIZE - self.msg_len as usize); if len > 0 { - std::ptr::copy_nonoverlapping(buf.as_ptr(), std::mem::transmute(self.buffer.as_mut_ptr().offset(self.msg_len as _)), len); + std::ptr::copy_nonoverlapping( + buf.as_ptr(), + std::mem::transmute(self.buffer.as_mut_ptr().offset(self.msg_len as _)), + len, + ); self.msg_len += len as u32; //The length is always less than 2^32. } len @@ -84,12 +92,18 @@ impl LogMsg { pub fn target(&self) -> &str { // SAFEY: This is always safe because BufLogMsg is always UTF-8. - unsafe { std::str::from_utf8_unchecked(std::mem::transmute(&self.buffer[..self.target_len as _])) } + unsafe { + std::str::from_utf8_unchecked(std::mem::transmute(&self.buffer[..self.target_len as _])) + } } pub fn msg(&self) -> &str { // SAFEY: This is always safe because BufLogMsg is always UTF-8. - unsafe { std::str::from_utf8_unchecked(std::mem::transmute(&self.buffer[self.target_len as _..self.msg_len as _])) } + unsafe { + std::str::from_utf8_unchecked(std::mem::transmute( + &self.buffer[self.target_len as _..self.msg_len as _], + )) + } } pub fn level(&self) -> Level { From b05170b621116fc0502763bc883600ef7f8104be Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 4 Feb 2023 21:01:48 +0100 Subject: [PATCH 04/21] Improved performance of the logging system by moving the log buffer to the async logging thread --- Cargo.toml | 3 +- src/internal.rs | 147 +++++++++++++++++++++++++++++------------------- src/lib.rs | 12 ++-- 3 files changed, 97 insertions(+), 65 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5503d90..fe540e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bp3d-logger" -version = "2.0.0-pre.2.0.0" +version = "2.0.0-pre.3.0.0" authors = ["Yuri Edward "] edition = "2021" description = "A flexible Log implementation intended to be used with BP3D software." @@ -16,6 +16,7 @@ categories = [] log = "0.4.14" bp3d-fs = "1.0.0" crossbeam-channel = "0.5.2" +crossbeam-queue = "0.3.8" once_cell = "1.10.0" chrono = "0.4.23" termcolor = "1.1.3" diff --git a/src/internal.rs b/src/internal.rs index 54c83ed..51f06bf 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -31,16 +31,19 @@ use crate::{LogMsg, Logger}; use crossbeam_channel::{bounded, Receiver, Sender}; use log::{Level, Log, Metadata, Record}; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Mutex; +use std::sync::{Arc, Mutex}; use chrono::Local; use std::fmt::Write; +use crossbeam_queue::ArrayQueue; const BUF_SIZE: usize = 16; // The maximum count of log messages in the channel. enum Command { Flush, Log(LogMsg), - Terminate + Terminate, + EnableLogBuffer, + DisableLogBuffer } fn log( @@ -56,61 +59,95 @@ fn log( } } -fn exec_commad(cmd: Command, logger: &mut Logger) -> bool { - match cmd { - Command::Terminate => true, - Command::Flush => { - if let Some(file) = &mut logger.file { - if let Err(e) = file.flush() { +struct Thread { + logger: Logger, + recv_ch: Receiver, + enable_log_buffer: bool, + log_buffer: Arc> +} + +impl Thread { + pub fn new(logger: Logger, recv_ch: Receiver, log_buffer: Arc>) -> Thread { + Thread { + logger, + recv_ch, + enable_log_buffer: false, + log_buffer + } + } + + fn exec_commad(&mut self, cmd: Command) -> bool { + match cmd { + Command::Terminate => true, + Command::Flush => { + if let Some(file) = &mut self.logger.file { + if let Err(e) = file.flush() { + let _ = log( + self.logger.std.as_mut(), + "bp3d-logger", + &format!("Could not flush file backend: {}", e), + Level::Error, + ); + } + } + false + } + Command::Log(buffer) => { + let target = buffer.target(); + let msg = buffer.msg(); + let level = buffer.level(); + if let Err(e) = log(self.logger.file.as_mut(), target, msg, level) { let _ = log( - logger.std.as_mut(), + self.logger.std.as_mut(), "bp3d-logger", - &format!("Could not flush file backend: {}", e), + &format!("Could not write to file backend: {}", e), Level::Error, ); } + let _ = log(self.logger.std.as_mut(), target, msg, level); + if self.enable_log_buffer { + self.log_buffer.force_push(buffer); + } + false + } + Command::EnableLogBuffer => { + self.enable_log_buffer = true; + false + }, + Command::DisableLogBuffer => { + self.enable_log_buffer = false; + false } - false } - Command::Log(buffer) => { - let target = buffer.target(); - let msg = buffer.msg(); - let level = buffer.level(); - if let Err(e) = log(logger.file.as_mut(), target, msg, level) { - let _ = log( - logger.std.as_mut(), - "bp3d-logger", - &format!("Could not write to file backend: {}", e), - Level::Error, - ); + } + + pub fn run(mut self) { + while let Ok(v) = self.recv_ch.recv() { + let flag = self.exec_commad(v); + if flag { + // The thread has requested to exit itself; drop out of the main loop. + break; } - let _ = log(logger.std.as_mut(), target, msg, level); - false } } } pub struct LoggerImpl { send_ch: Sender, - recv_ch: Receiver, enabled: AtomicBool, - log_buffer_send_ch: Sender, - log_buffer_recv_ch: Receiver, - enable_log_buffer: AtomicBool, + recv_ch: Receiver, + log_buffer: Arc>, thread: Mutex>> } impl LoggerImpl { pub fn new() -> LoggerImpl { let (send_ch, recv_ch) = bounded(BUF_SIZE); - let (log_buffer_send_ch, log_buffer_recv_ch) = bounded(BUF_SIZE); LoggerImpl { thread: Mutex::new(None), send_ch, recv_ch, - log_buffer_send_ch, - log_buffer_recv_ch, - enable_log_buffer: AtomicBool::new(false), + log_buffer: Arc::new(ArrayQueue::new(BUF_SIZE)), enabled: AtomicBool::new(false), } } @@ -120,15 +157,23 @@ impl LoggerImpl { } pub fn enable_log_buffer(&self, flag: bool) { - self.enable_log_buffer.store(flag, Ordering::Release); + unsafe { + if flag { + self.send_ch.send(Command::EnableLogBuffer).unwrap_unchecked(); + } else { + self.send_ch.send(Command::DisableLogBuffer).unwrap_unchecked(); + } + } } + #[inline] pub fn clear_log_buffer(&self) { - while self.log_buffer_recv_ch.try_recv().is_ok() {} //Clear the entire log buffer. + while self.log_buffer.pop().is_some() {} //Clear the entire log buffer. } - pub fn get_log_buffer(&self) -> Receiver { - self.log_buffer_recv_ch.clone() + #[inline] + pub fn read_log(&self) -> Option { + self.log_buffer.pop() } pub fn terminate(&self) { @@ -162,15 +207,10 @@ impl LoggerImpl { } } let recv_ch = self.recv_ch.clone(); + let log_buffer = self.log_buffer.clone(); *thread = Some(std::thread::spawn(move || { - let mut logger = logger; - while let Ok(v) = recv_ch.recv() { - let flag = exec_commad(v, &mut logger); - if flag { - // The thread has requested to exit itself; drop out of the main loop. - break; - } - } + let thread = Thread::new(logger, recv_ch, log_buffer); + thread.run(); })); } if flag { @@ -186,21 +226,10 @@ impl LoggerImpl { } pub fn low_level_log(&self, msg: &LogMsg) { - if self.enable_log_buffer.load(Ordering::Acquire) { - unsafe { - // This cannot panic as both send_ch and log_buffer_send_ch are owned by LoggerImpl - // which is intended to be statically allocated. - self.send_ch - .send(Command::Log(msg.clone())) - .unwrap_unchecked(); - self.log_buffer_send_ch.send(msg.clone()).unwrap_unchecked(); - } - } else { - unsafe { - // This cannot panic as send_ch is owned by LoggerImpl which is intended - // to be statically allocated. - self.send_ch.send(Command::Log(msg.clone())).unwrap_unchecked(); - } + unsafe { + // This cannot panic as send_ch is owned by LoggerImpl which is intended + // to be statically allocated. + self.send_ch.send(Command::Log(msg.clone())).unwrap_unchecked(); } } diff --git a/src/lib.rs b/src/lib.rs index 244931f..7461dad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,8 +143,10 @@ impl Default for Colors { /// bp3d_logger::enable_log_buffer(); // Enable log redirect pump into application channel. /// //... application code with log redirect pump. /// info!("Example message"); -/// let l = bp3d_logger::get_log_buffer().recv().unwrap();// Capture the last log message. -/// println!("Last log message: {}", l.msg); +/// bp3d_logger::flush(); +/// let l = bp3d_logger::read_log().unwrap();// Capture the last log message. +/// //We can't test for equality because log messages contains a timestamp... +/// assert!(l.msg().ends_with("Example message")); /// bp3d_logger::disable_log_buffer(); /// //... application code without log redirect pump. /// } @@ -278,9 +280,9 @@ pub fn disable_stdout() { ENABLE_STDOUT.store(false, Ordering::Release); } -/// Returns the buffer from the log redirect pump. -pub fn get_log_buffer() -> LogBuffer { - BP3D_LOGGER.get_log_buffer() +/// Attempts to extract one log message from the buffer. +pub fn read_log() -> Option { + BP3D_LOGGER.read_log() } /// Low-level log function. This injects log messages directly into the logging thread channel. From e706d9ddf6b4da4f9fe50087b819df0684e6b13c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 4 Feb 2023 20:13:13 +0000 Subject: [PATCH 05/21] Format Rust code using rustfmt --- src/internal.rs | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/internal.rs b/src/internal.rs index 9481b47..9b0a20d 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -28,13 +28,13 @@ use crate::backend::Backend; use crate::{LogMsg, Logger}; +use chrono::Local; use crossbeam_channel::{bounded, Receiver, Sender}; +use crossbeam_queue::ArrayQueue; use log::{Level, Log, Metadata, Record}; +use std::fmt::Write; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; -use chrono::Local; -use std::fmt::Write; -use crossbeam_queue::ArrayQueue; const BUF_SIZE: usize = 16; // The maximum count of log messages in the channel. @@ -43,7 +43,7 @@ enum Command { Log(LogMsg), Terminate, EnableLogBuffer, - DisableLogBuffer + DisableLogBuffer, } fn log( @@ -63,16 +63,20 @@ struct Thread { logger: Logger, recv_ch: Receiver, enable_log_buffer: bool, - log_buffer: Arc> + log_buffer: Arc>, } impl Thread { - pub fn new(logger: Logger, recv_ch: Receiver, log_buffer: Arc>) -> Thread { + pub fn new( + logger: Logger, + recv_ch: Receiver, + log_buffer: Arc>, + ) -> Thread { Thread { logger, recv_ch, enable_log_buffer: false, - log_buffer + log_buffer, } } @@ -113,7 +117,7 @@ impl Thread { Command::EnableLogBuffer => { self.enable_log_buffer = true; false - }, + } Command::DisableLogBuffer => { self.enable_log_buffer = false; false @@ -137,7 +141,7 @@ pub struct LoggerImpl { enabled: AtomicBool, recv_ch: Receiver, log_buffer: Arc>, - thread: Mutex>> + thread: Mutex>>, } impl LoggerImpl { @@ -159,9 +163,13 @@ impl LoggerImpl { pub fn enable_log_buffer(&self, flag: bool) { unsafe { if flag { - self.send_ch.send(Command::EnableLogBuffer).unwrap_unchecked(); + self.send_ch + .send(Command::EnableLogBuffer) + .unwrap_unchecked(); } else { - self.send_ch.send(Command::DisableLogBuffer).unwrap_unchecked(); + self.send_ch + .send(Command::DisableLogBuffer) + .unwrap_unchecked(); } } } @@ -233,7 +241,9 @@ impl LoggerImpl { unsafe { // This cannot panic as send_ch is owned by LoggerImpl which is intended // to be statically allocated. - self.send_ch.send(Command::Log(msg.clone())).unwrap_unchecked(); + self.send_ch + .send(Command::Log(msg.clone())) + .unwrap_unchecked(); } } @@ -267,7 +277,13 @@ impl Log for LoggerImpl { let time = Local::now(); let formatted = time.format("%a %b %d %Y %I:%M:%S %P"); let mut msg = LogMsg::new(target, record.level()); - let _ = write!(msg, "({}) {}: {}", formatted, module.unwrap_or("main"), record.args()); + let _ = write!( + msg, + "({}) {}: {}", + formatted, + module.unwrap_or("main"), + record.args() + ); self.low_level_log(&msg); } From a8f122592bc7f9429a3df83ad6b49138cdf8bf6e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 5 Feb 2023 09:53:47 +0100 Subject: [PATCH 06/21] Updated version information --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fe540e5..486676e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bp3d-logger" -version = "2.0.0-pre.3.0.0" +version = "2.0.0-rc.1.0.0" authors = ["Yuri Edward "] edition = "2021" description = "A flexible Log implementation intended to be used with BP3D software." From 22be54703bfcd4954fc18913a12ecdfe2e19431c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 5 Feb 2023 10:36:45 +0100 Subject: [PATCH 07/21] Improved order and size of the LogMsg structure fields --- src/log_msg.rs | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/log_msg.rs b/src/log_msg.rs index 0080e27..6b588f2 100644 --- a/src/log_msg.rs +++ b/src/log_msg.rs @@ -33,18 +33,43 @@ use std::mem::MaybeUninit; // Limit the size of the target string to 16 bytes. const LOG_TARGET_SIZE: usize = 16; // Size of the control fields of the log message structure: -// sizeof Level + 1 byte for target_len + sizeof msg_len -const LOG_CONTROL_SIZE: usize = std::mem::size_of::() + std::mem::size_of::() + 1; +// sizeof msg_len + 1 byte for target_len + 1 byte for level +const LOG_CONTROL_SIZE: usize = std::mem::size_of::() + 2; // Limit the size of the log message string so that the size of the log structure is LOG_BUFFER_SIZE const LOG_MSG_SIZE: usize = LOG_BUFFER_SIZE - LOG_TARGET_SIZE - LOG_CONTROL_SIZE; const LOG_BUFFER_SIZE: usize = 1024; +#[inline] +fn log_to_u8(level: Level) -> u8 { + match level { + Level::Error => 0, + Level::Warn => 1, + Level::Info => 2, + Level::Debug => 3, + Level::Trace => 4 + } +} + +#[inline] +fn u8_to_log(l: u8) -> Level { + match l { + 0 => Level::Error, + 1 => Level::Warn, + 3 => Level::Debug, + 4 => Level::Trace, + _ => Level::Info + } +} + #[derive(Clone)] +// This repr is to force msg_len, level and target_len to appear first so that we don't +// have to mov something after the 1K buffer. +#[repr(C)] pub struct LogMsg { - buffer: [MaybeUninit; LOG_BUFFER_SIZE], - level: Level, msg_len: u32, + level: u8, target_len: u8, + buffer: [MaybeUninit; LOG_MSG_SIZE + LOG_TARGET_SIZE] } impl LogMsg { @@ -54,7 +79,7 @@ impl LogMsg { buffer: unsafe { MaybeUninit::uninit().assume_init() }, target_len: len as _, msg_len: len as _, - level, + level: log_to_u8(level), }; unsafe { std::ptr::copy_nonoverlapping( @@ -66,6 +91,7 @@ impl LogMsg { buffer } + #[inline] pub fn clear(&mut self) { self.msg_len = self.target_len as _; } @@ -90,6 +116,7 @@ impl LogMsg { len } + #[inline] pub fn target(&self) -> &str { // SAFEY: This is always safe because BufLogMsg is always UTF-8. unsafe { @@ -97,6 +124,7 @@ impl LogMsg { } } + #[inline] pub fn msg(&self) -> &str { // SAFEY: This is always safe because BufLogMsg is always UTF-8. unsafe { @@ -106,8 +134,9 @@ impl LogMsg { } } + #[inline] pub fn level(&self) -> Level { - self.level + u8_to_log(self.level) } } From 5b7362c0d0d9e7b6bea4d23425a8d9f9142121da Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 5 Feb 2023 11:05:01 +0100 Subject: [PATCH 08/21] Added missing docs --- src/lib.rs | 4 +++ src/log_msg.rs | 93 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7461dad..92c7076 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,10 @@ // some context to not make it confusing. #![allow(clippy::needless_doctest_main)] +#![warn(missing_docs)] + +//! An async flexible Log implementation intended to be used with BP3D software. + mod backend; mod easy_termcolor; mod internal; diff --git a/src/log_msg.rs b/src/log_msg.rs index 6b588f2..641800b 100644 --- a/src/log_msg.rs +++ b/src/log_msg.rs @@ -61,9 +61,25 @@ fn u8_to_log(l: u8) -> Level { } } +/// A log message. +/// +/// This structure uses a large 1K buffer which stores the entire log message to improve +/// performance. +/// +/// The repr(C) is used to force the control fields (msg_len, level and target_len) to be before +/// the message buffer and avoid large movs when setting control fields. +/// +/// # Examples +/// +/// ``` +/// use log::Level; +/// use bp3d_logger::LogMsg; +/// use std::fmt::Write; +/// let mut msg = LogMsg::new("test", Level::Info); +/// let _ = write!(msg, "This is a formatted message {}", 42); +/// assert_eq!(msg.msg(), "This is a formatted message 42"); +/// ``` #[derive(Clone)] -// This repr is to force msg_len, level and target_len to appear first so that we don't -// have to mov something after the 1K buffer. #[repr(C)] pub struct LogMsg { msg_len: u32, @@ -73,6 +89,24 @@ pub struct LogMsg { } impl LogMsg { + /// Creates a new instance of log message buffer. + /// + /// # Arguments + /// + /// * `target`: the target name this log comes from. + /// * `level`: the [Level](Level) of the log message. + /// + /// returns: LogMsg + /// + /// # Examples + /// + /// ``` + /// use log::Level; + /// use bp3d_logger::LogMsg; + /// let msg = LogMsg::new("test", Level::Info); + /// assert_eq!(msg.target(), "test"); + /// assert_eq!(msg.level(), Level::Info); + /// ``` pub fn new(target: &str, level: Level) -> LogMsg { let len = std::cmp::min(LOG_TARGET_SIZE, target.as_bytes().len()); let mut buffer = LogMsg { @@ -91,18 +125,68 @@ impl LogMsg { buffer } + /// Clears the log message but keep the target and the level. + /// + /// # Examples + /// + /// ``` + /// use log::Level; + /// use bp3d_logger::LogMsg; + /// let mut msg = LogMsg::from_msg("test", Level::Info, "this is a test"); + /// msg.clear(); + /// assert_eq!(msg.msg(), ""); + /// assert_eq!(msg.target(), "test"); + /// assert_eq!(msg.level(), Level::Info); + /// ``` #[inline] pub fn clear(&mut self) { self.msg_len = self.target_len as _; } + /// Auto-creates a new log message with a pre-defined string message. + /// + /// This function is the same as calling [write](LogMsg::write) after [new](LogMsg::new). + /// + /// # Arguments + /// + /// * `target`: the target name this log comes from. + /// * `level`: the [Level](Level) of the log message. + /// * `msg`: the message string. + /// + /// returns: LogMsg + /// + /// # Examples + /// + /// ``` + /// use log::Level; + /// use bp3d_logger::LogMsg; + /// let mut msg = LogMsg::from_msg("test", Level::Info, "this is a test"); + /// assert_eq!(msg.target(), "test"); + /// assert_eq!(msg.level(), Level::Info); + /// assert_eq!(msg.msg(), "this is a test"); + /// ``` pub fn from_msg(target: &str, level: Level, msg: &str) -> LogMsg { let mut ads = Self::new(target, level); unsafe { ads.write(msg.as_bytes()) }; ads } - // SAFETY: BufLogMsg must always contain valid UTF-8 so ensure that buf only contains valid UTF-8 data. + /// Appends a raw byte buffer at the end of the message buffer. + /// + /// Returns the number of bytes written. + /// + /// # Arguments + /// + /// * `buf`: the raw byte buffer to append. + /// + /// returns: usize + /// + /// # Safety + /// + /// * [LogMsg](LogMsg) contains only valid UTF-8 strings so buf must contain only valid UTF-8 + /// bytes. + /// * If buf contains invalid UTF-8 bytes, further operations on the log message buffer may + /// result in UB. pub unsafe fn write(&mut self, buf: &[u8]) -> usize { let len = std::cmp::min(buf.len(), LOG_MSG_SIZE - self.msg_len as usize); if len > 0 { @@ -116,6 +200,7 @@ impl LogMsg { len } + /// Returns the target name this log comes from. #[inline] pub fn target(&self) -> &str { // SAFEY: This is always safe because BufLogMsg is always UTF-8. @@ -124,6 +209,7 @@ impl LogMsg { } } + /// Returns the log message as a string. #[inline] pub fn msg(&self) -> &str { // SAFEY: This is always safe because BufLogMsg is always UTF-8. @@ -134,6 +220,7 @@ impl LogMsg { } } + /// Returns the level of this log message. #[inline] pub fn level(&self) -> Level { u8_to_log(self.level) From 37606251dea4fd63403eba5d9612611f4e137f77 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 5 Feb 2023 10:11:16 +0000 Subject: [PATCH 09/21] Format Rust code using rustfmt --- src/lib.rs | 1 - src/log_msg.rs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 92c7076..84706e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,6 @@ // The reason why this is needed is because the 3 examples of usage of the Logger struct requires // some context to not make it confusing. #![allow(clippy::needless_doctest_main)] - #![warn(missing_docs)] //! An async flexible Log implementation intended to be used with BP3D software. diff --git a/src/log_msg.rs b/src/log_msg.rs index 641800b..f13b6b1 100644 --- a/src/log_msg.rs +++ b/src/log_msg.rs @@ -46,7 +46,7 @@ fn log_to_u8(level: Level) -> u8 { Level::Warn => 1, Level::Info => 2, Level::Debug => 3, - Level::Trace => 4 + Level::Trace => 4, } } @@ -57,7 +57,7 @@ fn u8_to_log(l: u8) -> Level { 1 => Level::Warn, 3 => Level::Debug, 4 => Level::Trace, - _ => Level::Info + _ => Level::Info, } } @@ -85,7 +85,7 @@ pub struct LogMsg { msg_len: u32, level: u8, target_len: u8, - buffer: [MaybeUninit; LOG_MSG_SIZE + LOG_TARGET_SIZE] + buffer: [MaybeUninit; LOG_MSG_SIZE + LOG_TARGET_SIZE], } impl LogMsg { From 8433b04d84593aa4326473fc9aae6caf50b4056f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 1 Aug 2023 20:42:52 +0200 Subject: [PATCH 10/21] Replaced bp3d-fs by bp3d-os --- Cargo.toml | 2 +- src/lib.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 486676e..061ae40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ categories = [] [dependencies] log = "0.4.14" -bp3d-fs = "1.0.0" +bp3d-os = { version = "1.0.0-rc.1.0.0", features=["dirs"] } crossbeam-channel = "0.5.2" crossbeam-queue = "0.3.8" once_cell = "1.10.0" diff --git a/src/lib.rs b/src/lib.rs index 84706e0..79db955 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,7 +39,7 @@ mod internal; mod log_msg; use crate::backend::ENABLE_STDOUT; -use bp3d_fs::dirs::App; +use bp3d_os::dirs::App; use crossbeam_channel::Receiver; use log::Log; use once_cell::sync::Lazy; @@ -51,7 +51,7 @@ pub use log_msg::LogMsg; /// The log buffer type. pub type LogBuffer = Receiver; -/// Trait to allow getting a log directory from either a bp3d_fs::dirs::App or a String. +/// Trait to allow getting a log directory from either a bp3d_os::dirs::App or a String. pub trait GetLogs { /// Gets the log directory as a PathBuf. /// @@ -67,14 +67,14 @@ impl<'a> GetLogs for &'a String { impl<'a, 'b> GetLogs for &'a App<'b> { fn get_logs(self) -> Option { - self.get_logs().map(|v| v.into()).ok() + self.get_logs().map(|v| v.as_ref().into()) } } impl<'a> GetLogs for &'a str { fn get_logs(self) -> Option { let app = App::new(self); - app.get_logs().map(|v| v.into()).ok() + app.get_logs().map(|v| v.as_ref().into()) } } From b8cdcb632a2e5475c2986fb4fc5ff193d000a6c8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 1 Aug 2023 23:55:33 +0200 Subject: [PATCH 11/21] Added support for auto-creating log directory --- Cargo.toml | 2 +- src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 061ae40..92d932c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ categories = [] [dependencies] log = "0.4.14" -bp3d-os = { version = "1.0.0-rc.1.0.0", features=["dirs"] } +bp3d-os = { version = "1.0.0-rc.1.1.0", features=["dirs"] } crossbeam-channel = "0.5.2" crossbeam-queue = "0.3.8" once_cell = "1.10.0" diff --git a/src/lib.rs b/src/lib.rs index 79db955..deef19f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,14 +67,14 @@ impl<'a> GetLogs for &'a String { impl<'a, 'b> GetLogs for &'a App<'b> { fn get_logs(self) -> Option { - self.get_logs().map(|v| v.as_ref().into()) + self.get_logs().map(|v| v.create())?.ok().map(|v| v.into()) } } impl<'a> GetLogs for &'a str { fn get_logs(self) -> Option { let app = App::new(self); - app.get_logs().map(|v| v.as_ref().into()) + app.get_logs().map(|v| v.create())?.ok().map(|v| v.into()) } } From 15b098f7989440ae837aa2db19bbff16c1a17f4c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 8 Aug 2023 14:18:15 +0200 Subject: [PATCH 12/21] Fixed doc --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index deef19f..a704bcd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -291,7 +291,7 @@ pub fn read_log() -> Option { /// Low-level log function. This injects log messages directly into the logging thread channel. /// /// This function applies basic formatting depending on the backend: -/// - For stdout/stderr backend the format is \[level\] msg +/// - For stdout/stderr backend the format is \ \[level\] msg /// - For file backend the format is \[level\] msg and the message is recorded in the file /// corresponding to the log target. pub fn raw_log(msg: &LogMsg) { From 2dadee9a73a546ed7ac463fc5f5ace9167c77ba7 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 9 Aug 2023 14:26:38 +0200 Subject: [PATCH 13/21] Reverted chrono back to time thanks to new bp3d-os time feature --- Cargo.toml | 4 ++-- src/internal.rs | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 92d932c..b4af5fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,11 +14,11 @@ categories = [] [dependencies] log = "0.4.14" -bp3d-os = { version = "1.0.0-rc.1.1.0", features=["dirs"] } +bp3d-os = { version = "1.0.0-rc.1.2.0", features=["dirs", "time"] } crossbeam-channel = "0.5.2" crossbeam-queue = "0.3.8" once_cell = "1.10.0" -chrono = "0.4.23" +time = { version = "0.3.0", features = ["formatting", "macros"] } termcolor = "1.1.3" atty = "0.2.14" diff --git a/src/internal.rs b/src/internal.rs index 9b0a20d..5851ce9 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -28,10 +28,12 @@ use crate::backend::Backend; use crate::{LogMsg, Logger}; -use chrono::Local; +use bp3d_os::time::LocalOffsetDateTime; use crossbeam_channel::{bounded, Receiver, Sender}; use crossbeam_queue::ArrayQueue; use log::{Level, Log, Metadata, Record}; +use time::OffsetDateTime; +use time::macros::format_description; use std::fmt::Write; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; @@ -274,8 +276,9 @@ impl Log for LoggerImpl { return; } let (target, module) = extract_target_module(record); - let time = Local::now(); - let formatted = time.format("%a %b %d %Y %I:%M:%S %P"); + let time = OffsetDateTime::now_local(); + let format = format_description!("[weekday repr:short] [month repr:short] [day] [hour repr:12]:[minute]:[second] [period case:upper]"); + let formatted = time.unwrap_or_else(OffsetDateTime::now_utc).format(format).unwrap_or_default(); let mut msg = LogMsg::new(target, record.level()); let _ = write!( msg, From 25f4a87953e6f43756b6b8c20b1c9ad18f4cf415 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Aug 2023 12:30:12 +0000 Subject: [PATCH 14/21] Format Rust code using rustfmt --- src/internal.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/internal.rs b/src/internal.rs index 5851ce9..548cfb7 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -32,11 +32,11 @@ use bp3d_os::time::LocalOffsetDateTime; use crossbeam_channel::{bounded, Receiver, Sender}; use crossbeam_queue::ArrayQueue; use log::{Level, Log, Metadata, Record}; -use time::OffsetDateTime; -use time::macros::format_description; use std::fmt::Write; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; +use time::macros::format_description; +use time::OffsetDateTime; const BUF_SIZE: usize = 16; // The maximum count of log messages in the channel. @@ -278,7 +278,10 @@ impl Log for LoggerImpl { let (target, module) = extract_target_module(record); let time = OffsetDateTime::now_local(); let format = format_description!("[weekday repr:short] [month repr:short] [day] [hour repr:12]:[minute]:[second] [period case:upper]"); - let formatted = time.unwrap_or_else(OffsetDateTime::now_utc).format(format).unwrap_or_default(); + let formatted = time + .unwrap_or_else(OffsetDateTime::now_utc) + .format(format) + .unwrap_or_default(); let mut msg = LogMsg::new(target, record.level()); let _ = write!( msg, From 19659754dfdab9e58a9a40717f8befb5ee3972f8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 9 Aug 2023 14:55:24 +0200 Subject: [PATCH 15/21] Attempt at fixing weird illegal instruction --- src/internal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal.rs b/src/internal.rs index 5851ce9..2d48cf3 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -276,7 +276,7 @@ impl Log for LoggerImpl { return; } let (target, module) = extract_target_module(record); - let time = OffsetDateTime::now_local(); + let time = Some(OffsetDateTime::now_utc()); let format = format_description!("[weekday repr:short] [month repr:short] [day] [hour repr:12]:[minute]:[second] [period case:upper]"); let formatted = time.unwrap_or_else(OffsetDateTime::now_utc).format(format).unwrap_or_default(); let mut msg = LogMsg::new(target, record.level()); From e717322f789ab160571258cb0d2f7cf72001fdfe Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 9 Aug 2023 15:01:03 +0200 Subject: [PATCH 16/21] Making sure GetTimeZoneInformation causes illegal instruction on win 2019 --- src/internal.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/internal.rs b/src/internal.rs index 7d7ef19..5851ce9 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -32,11 +32,11 @@ use bp3d_os::time::LocalOffsetDateTime; use crossbeam_channel::{bounded, Receiver, Sender}; use crossbeam_queue::ArrayQueue; use log::{Level, Log, Metadata, Record}; +use time::OffsetDateTime; +use time::macros::format_description; use std::fmt::Write; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; -use time::macros::format_description; -use time::OffsetDateTime; const BUF_SIZE: usize = 16; // The maximum count of log messages in the channel. @@ -276,12 +276,9 @@ impl Log for LoggerImpl { return; } let (target, module) = extract_target_module(record); - let time = Some(OffsetDateTime::now_utc()); + let time = OffsetDateTime::now_local(); let format = format_description!("[weekday repr:short] [month repr:short] [day] [hour repr:12]:[minute]:[second] [period case:upper]"); - let formatted = time - .unwrap_or_else(OffsetDateTime::now_utc) - .format(format) - .unwrap_or_default(); + let formatted = time.unwrap_or_else(OffsetDateTime::now_utc).format(format).unwrap_or_default(); let mut msg = LogMsg::new(target, record.level()); let _ = write!( msg, From 22268f5dee2b2538bc94b984015d1c1e0c61c7ce Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Aug 2023 13:05:06 +0000 Subject: [PATCH 17/21] Format Rust code using rustfmt --- src/internal.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/internal.rs b/src/internal.rs index 5851ce9..548cfb7 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -32,11 +32,11 @@ use bp3d_os::time::LocalOffsetDateTime; use crossbeam_channel::{bounded, Receiver, Sender}; use crossbeam_queue::ArrayQueue; use log::{Level, Log, Metadata, Record}; -use time::OffsetDateTime; -use time::macros::format_description; use std::fmt::Write; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; +use time::macros::format_description; +use time::OffsetDateTime; const BUF_SIZE: usize = 16; // The maximum count of log messages in the channel. @@ -278,7 +278,10 @@ impl Log for LoggerImpl { let (target, module) = extract_target_module(record); let time = OffsetDateTime::now_local(); let format = format_description!("[weekday repr:short] [month repr:short] [day] [hour repr:12]:[minute]:[second] [period case:upper]"); - let formatted = time.unwrap_or_else(OffsetDateTime::now_utc).format(format).unwrap_or_default(); + let formatted = time + .unwrap_or_else(OffsetDateTime::now_utc) + .format(format) + .unwrap_or_default(); let mut msg = LogMsg::new(target, record.level()); let _ = write!( msg, From 9ab1a8bf3dda4e2f2c14495f2fc66fbbae28e3b2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 9 Aug 2023 21:48:15 +0200 Subject: [PATCH 18/21] Updated to new patched rc 1.2.1 of bp3d-os --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b4af5fe..2b1a99e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ categories = [] [dependencies] log = "0.4.14" -bp3d-os = { version = "1.0.0-rc.1.2.0", features=["dirs", "time"] } +bp3d-os = { version = "1.0.0-rc.1.2.1", features=["dirs", "time"] } crossbeam-channel = "0.5.2" crossbeam-queue = "0.3.8" once_cell = "1.10.0" From 7d8330096624fde98d750c6b6a67599c0cffa5a0 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 9 Aug 2023 22:12:08 +0200 Subject: [PATCH 19/21] Removed atty, now replaced by std::io::IsTerminal --- Cargo.toml | 1 - src/backend.rs | 19 ++++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2b1a99e..54cb708 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,5 @@ crossbeam-queue = "0.3.8" once_cell = "1.10.0" time = { version = "0.3.0", features = ["formatting", "macros"] } termcolor = "1.1.3" -atty = "0.2.14" [features] diff --git a/src/backend.rs b/src/backend.rs index e690b37..0070ff0 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -28,13 +28,12 @@ use crate::easy_termcolor::{color, EasyTermColor}; use crate::Colors; -use atty::Stream; use log::Level; use std::collections::HashMap; use std::fmt::Display; use std::fmt::Formatter; use std::fs::{File, OpenOptions}; -use std::io::{BufWriter, Write}; +use std::io::{BufWriter, Write, IsTerminal}; use std::path::PathBuf; use std::sync::atomic::{AtomicBool, Ordering}; use termcolor::{ColorChoice, ColorSpec, StandardStream}; @@ -77,6 +76,20 @@ fn write_msg(stream: StandardStream, target: &str, msg: &str, level: Level) { .lf(); } +enum Stream { + Stdout, + Stderr +} + +impl Stream { + pub fn isatty(&self) -> bool { + match self { + Stream::Stdout => std::io::stdout().is_terminal(), + Stream::Stderr => std::io::stderr().is_terminal(), + } + } +} + impl StdBackend { pub fn new(smart_stderr: bool, colors: Colors) -> StdBackend { StdBackend { @@ -108,7 +121,7 @@ impl Backend for StdBackend { let use_termcolor = match self.colors { Colors::Disabled => false, Colors::Enabled => true, - Colors::Auto => atty::is(stream), + Colors::Auto => stream.isatty(), }; match use_termcolor { true => { From a4c996cbaa17749122f102a93a081a5466d48594 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 9 Aug 2023 22:15:09 +0200 Subject: [PATCH 20/21] Added disable for large_enum_variant --- src/internal.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/internal.rs b/src/internal.rs index 548cfb7..1bf22b2 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -40,6 +40,10 @@ use time::OffsetDateTime; const BUF_SIZE: usize = 16; // The maximum count of log messages in the channel. +//Disable large_enum_variant as using a Box will inevitably cause a small allocation on a critical path, +//allocating in a critical code path will most likely result in degraded performance. +//And yes, logging is a critical path when using bp3d-tracing. +#[allow(clippy::large_enum_variant)] enum Command { Flush, Log(LogMsg), From c79ce6e2c53bee81cdf7c98032a4dd54b77c4abe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Aug 2023 20:19:29 +0000 Subject: [PATCH 21/21] Format Rust code using rustfmt --- src/backend.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend.rs b/src/backend.rs index 0070ff0..196465b 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -33,7 +33,7 @@ use std::collections::HashMap; use std::fmt::Display; use std::fmt::Formatter; use std::fs::{File, OpenOptions}; -use std::io::{BufWriter, Write, IsTerminal}; +use std::io::{BufWriter, IsTerminal, Write}; use std::path::PathBuf; use std::sync::atomic::{AtomicBool, Ordering}; use termcolor::{ColorChoice, ColorSpec, StandardStream}; @@ -78,7 +78,7 @@ fn write_msg(stream: StandardStream, target: &str, msg: &str, level: Level) { enum Stream { Stdout, - Stderr + Stderr, } impl Stream {