GstPipelineStudio/src/logger.rs
Stéphane Cerveau 1f200f4f30 app: update to gtk 0.8.0 and gst 0.22.2
In the change, the glib::channel has been dropped to use
async_io which achieves the same to receive message
for the logger and display it in the treeview.
Another message is received when a new gtkpaintablesink
has been instanciated.
2024-03-14 16:34:55 +01:00

234 lines
5.9 KiB
Rust

// logger.rs
//
// Copyright 2022 Stéphane Cerveau <scerveau@collabora.com>
//
// This file is part of GstPipelineStudio
//
// SPDX-License-Identifier: GPL-3.0-only
use log::{debug, error, info, trace, warn};
use simplelog::*;
use std::fmt;
use std::io;
use std::fs::File;
use chrono::Local;
use std::sync::Mutex;
lazy_static::lazy_static! {
static ref MSG_LOGGER: Mutex<Option<MessageLogger>> = Mutex::new(None);
}
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum LogLevel {
Off,
Error,
Warning,
Info,
Debug,
Trace,
}
#[derive(Debug, Clone, PartialEq)]
pub enum LogType {
App,
Gst,
Message,
}
impl LogLevel {
pub fn from_u32(value: u32) -> LogLevel {
match value {
0 => LogLevel::Off,
1 => LogLevel::Error,
2 => LogLevel::Warning,
3 => LogLevel::Info,
4 => LogLevel::Debug,
5 => LogLevel::Trace,
_ => panic!("Unknown value: {}", value),
}
}
}
impl fmt::Display for LogLevel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self:?}")
}
}
#[macro_export]
macro_rules! GPS_ERROR (
() => ($crate::print!("\n"));
($($arg:tt)*) => ({
logger::print_log(logger::LogLevel::Error, format!("{}\t{}",glib::function_name!(),format_args!($($arg)*).to_string()));
})
);
#[macro_export]
macro_rules! GPS_WARN (
() => ($crate::print!("\n"));
($($arg:tt)*) => ({
logger::print_log(logger::LogLevel::Warning, format!("{}\t{}",glib::function_name!(),format_args!($($arg)*).to_string()));
})
);
#[macro_export]
macro_rules! GPS_INFO (
() => ($crate::print!("\n"));
($($arg:tt)*) => ({
logger::print_log(logger::LogLevel::Info, format!("{}\t{}",glib::function_name!(),format_args!($($arg)*).to_string()));
})
);
#[macro_export]
macro_rules! GPS_DEBUG (
() => ($crate::print!("\n"));
($($arg:tt)*) => ({
logger::print_log(logger::LogLevel::Debug, format!("{}\t{}",glib::function_name!(),format_args!($($arg)*).to_string()));
})
);
#[macro_export]
macro_rules! GPS_MSG_LOG (
() => ($crate::print!("\n"));
($($arg:tt)*) => ({
logger::print_msg_logger(logger::LogType::Message, format_args!($($arg)*).to_string());
})
);
#[macro_export]
macro_rules! GPS_GST_LOG (
() => ($crate::print!("\n"));
($($arg:tt)*) => ({
logger::print_msg_logger(logger::LogType::Gst, format_args!($($arg)*).to_string());
})
);
#[macro_export]
macro_rules! GPS_TRACE (
() => ($crate::print!("\n"));
($($arg:tt)*) => ({
logger::print_log(logger::LogLevel::Trace, format_args!($($arg)*).to_string());
})
);
struct WriteAdapter {
sender: async_channel::Sender<(LogType, String)>,
buffer: String,
}
impl io::Write for WriteAdapter {
// On write we forward each u8 of the buffer to the sender and return the length of the buffer
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buffer
.push_str(&String::from_utf8(buf.to_vec()).unwrap());
if self.buffer.ends_with('\n') {
self.buffer.pop();
let _ = self.sender.try_send((LogType::App, self.buffer.clone()));
self.buffer.clear();
}
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
fn translate_to_simple_logger(log_level: LogLevel) -> LevelFilter {
match log_level {
LogLevel::Off => LevelFilter::Off,
LogLevel::Error => LevelFilter::Error,
LogLevel::Warning => LevelFilter::Warn,
LogLevel::Info => LevelFilter::Info,
LogLevel::Debug => LevelFilter::Debug,
LogLevel::Trace => LevelFilter::Trace,
}
}
pub fn init_logger(sender: async_channel::Sender<(LogType, String)>, log_file: &str) {
simplelog::CombinedLogger::init(vec![
WriteLogger::new(
translate_to_simple_logger(LogLevel::Trace),
Config::default(),
File::create(log_file).unwrap_or_else(|_| panic!("Unable to create log {}", log_file)),
),
WriteLogger::new(
translate_to_simple_logger(LogLevel::Debug),
Config::default(),
WriteAdapter {
sender,
buffer: String::from(""),
},
),
TermLogger::new(
LevelFilter::Info,
Config::default(),
TerminalMode::Mixed,
ColorChoice::Auto,
),
])
.unwrap();
}
pub fn set_log_level(level: LogLevel) {
log::set_max_level(translate_to_simple_logger(level));
}
pub fn print_log(log_level: LogLevel, msg: String) {
match log_level {
LogLevel::Error => {
error!("{}", msg);
}
LogLevel::Warning => {
warn!("{}", msg);
}
LogLevel::Info => {
info!("{}", msg);
}
LogLevel::Debug => {
debug!("{}", msg);
}
LogLevel::Trace => {
trace!("{}", msg);
}
_ => {}
};
}
#[derive(Debug, Clone)]
pub struct MessageLogger {
sender: async_channel::Sender<(LogType, String)>,
}
impl MessageLogger {
pub fn new(sender: async_channel::Sender<(LogType, String)>) -> Self {
Self { sender }
}
pub fn print_log(&self, log_type: LogType, msg: String) {
let to_send = format!("{}\t{}", Local::now().format("%H:%M:%S"), msg);
self.sender
.try_send((log_type, to_send))
.expect("Unable to send the log");
}
}
pub fn init_msg_logger(sender: async_channel::Sender<(LogType, String)>) {
let mut msg_logger = MSG_LOGGER.lock().unwrap();
if msg_logger.is_none() {
// Initialize the variable
*msg_logger = Some(MessageLogger::new(sender));
}
}
pub fn print_msg_logger(log_type: LogType, msg: String) {
let msg_logger = MSG_LOGGER.lock().unwrap();
if let Some(logger) = msg_logger.as_ref() {
logger.print_log(log_type, msg);
}
}