Files
ratatrix/src/log.rs

107 lines
3.3 KiB
Rust

/*
* Copyright (C) 2023 Valentin Lorentz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use std::collections::VecDeque;
use std::sync::RwLock;
use color_eyre::eyre::Result;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use tracing_error::ErrorLayer;
use tracing_subscriber::prelude::*;
use crate::utils::{get_data_dir, LOG_ENV, LOG_FILE};
pub struct LogWriter {
sender: UnboundedSender<String>,
last_line: Vec<u8>,
}
impl LogWriter {
fn new(sender: UnboundedSender<String>) -> Self {
LogWriter {
sender,
last_line: Vec::new(),
}
}
}
impl std::io::Write for LogWriter {
fn write(&mut self, bytes: &[u8]) -> Result<usize, std::io::Error> {
self.last_line.extend(bytes);
if bytes.contains(&b'\n') {
// Need to do this because tracing_subscriber never ever call flush() itself?
self.flush()?;
}
Ok(bytes.len())
}
fn flush(&mut self) -> Result<(), std::io::Error> {
let mut new_lines: Vec<&[u8]> = self.last_line.split(|&c| c == b'\n').collect();
if new_lines.len() == 1 {
// No new line, nothing to do
} else {
let last_line = new_lines.pop().expect("Split returned empty vec").to_vec();
for new_line in new_lines.into_iter() {
let new_line = String::from_utf8_lossy(new_line).to_string();
self
.sender
.send(new_line)
.unwrap_or_else(|e| panic!("Could not push log line: {:?}", e));
}
self.last_line.clear();
self.last_line.extend(&last_line[..]);
}
Ok(())
}
}
pub fn initialize_logging() -> Result<UnboundedReceiver<String>> {
let directory = get_data_dir();
std::fs::create_dir_all(directory.clone())?;
let log_path = directory.join(LOG_FILE.clone());
let log_file = std::fs::File::create(log_path)?;
std::env::set_var(
"RUST_LOG",
std::env::var("RUST_LOG")
.or_else(|_| std::env::var(LOG_ENV.clone()))
.unwrap_or_else(|_| format!("{}=info", env!("CARGO_CRATE_NAME"))),
);
let file_subscriber = tracing_subscriber::fmt::layer()
.with_file(true)
.with_line_number(true)
.with_writer(log_file)
.with_target(false)
.with_ansi(false)
.with_filter(tracing_subscriber::filter::EnvFilter::from_default_env());
// We keep the log until the application stops, so we might as well return it as &'static
let (sender, receiver) = unbounded_channel();
let mem_subscriber = tracing_subscriber::fmt::layer()
.with_ansi(true)
.with_file(true)
.with_line_number(true)
.with_target(false)
.with_writer(move || LogWriter::new(sender.clone()))
.with_filter(tracing_subscriber::filter::EnvFilter::from_default_env());
tracing_subscriber::registry()
.with(mem_subscriber)
.with(file_subscriber)
.with(ErrorLayer::default())
.init();
Ok(receiver)
}