107 lines
3.3 KiB
Rust
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)
|
|
}
|