You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
3.5 KiB
168 lines
3.5 KiB
//! Logging hooks for logfile output
|
|
|
|
use super::*;
|
|
use std::{
|
|
path::{
|
|
PathBuf,
|
|
Path,
|
|
},
|
|
sync::Arc,
|
|
io::{
|
|
self,
|
|
ErrorKind,
|
|
},
|
|
};
|
|
use tokio::{
|
|
prelude::*,
|
|
sync::{
|
|
mpsc,
|
|
},
|
|
task,
|
|
fs::{OpenOptions,File,},
|
|
};
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub struct Internal
|
|
{
|
|
stdout: PathBuf,
|
|
stderr: PathBuf,
|
|
|
|
use_local_time: bool,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Command
|
|
{
|
|
level: Level,
|
|
|
|
trace: Trace,
|
|
timestamp: Date,
|
|
message: String,
|
|
|
|
is_err: bool,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
/// Logging hook that appends to logfiles
|
|
pub struct LogFileHook
|
|
{
|
|
mask: Level,
|
|
|
|
internal: Arc<Internal>,
|
|
|
|
sender: Option<mpsc::Sender<Command>>,
|
|
}
|
|
|
|
impl Internal
|
|
{
|
|
//funcs we might both want to use
|
|
|
|
fn valid_perms(&self) -> readable_perms::Permissions
|
|
{
|
|
//TODO: Add field for this
|
|
readable_perms::Permissions::new()
|
|
.add_mask(readable_perms::User::Owner, readable_perms::Bit::Read | readable_perms::Bit::Write)
|
|
.add_mask(readable_perms::User::Group, readable_perms::Bit::Read)
|
|
.add_mask(readable_perms::User::Owner, readable_perms::Bit::None)
|
|
}
|
|
|
|
/// Fix permissions on this file to what we want.
|
|
fn fix_perms(&self, file: &mut tokio::fs::File)
|
|
{
|
|
use readable_perms::*;
|
|
if let Err(e) = file.chmod(self.valid_perms()) {
|
|
crate::warn!("Error fixing logfile permissions: {}", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl LogFileHook
|
|
{
|
|
/// Create a new log file hook
|
|
pub fn new<L,P,E>(mask: L, path: P, error: E, use_local_time: bool) -> Self
|
|
where L: Into<Level>,
|
|
P: AsRef<Path>,
|
|
E: AsRef<Path>
|
|
{
|
|
Self {
|
|
mask: mask.into(),
|
|
internal: Arc::new(Internal{
|
|
stderr: error.as_ref().to_owned(),
|
|
stdout: path.as_ref().to_owned(),
|
|
use_local_time,
|
|
}),
|
|
sender: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn write_log(file: &mut File, com: Command) -> std::io::Result<()>
|
|
{
|
|
let output = format!("{} <{}> [{}]: {}\n", com.timestamp, com.trace, com.level, com.message);
|
|
file.write(output.as_bytes()).await?;
|
|
Ok(())
|
|
}
|
|
|
|
const BACKLOG: usize = 100;
|
|
|
|
impl Hook for LogFileHook
|
|
{
|
|
fn initialise(&mut self, _: &Index)
|
|
{
|
|
let (tx, mut rx) = mpsc::channel(BACKLOG);
|
|
|
|
let internal = Arc::clone(&self.internal);
|
|
|
|
self.sender = Some(tx);
|
|
task::spawn(async move {
|
|
let (stdout, stderr) = (Path::new(&internal.stdout), Path::new(&internal.stderr));
|
|
|
|
while let Some(command) = rx.recv().await {
|
|
let path = if command.is_err {stderr} else {stdout};
|
|
|
|
let mut file = match OpenOptions::new()
|
|
.append(true)
|
|
.create(true)
|
|
.write(true)
|
|
.open(path).await {
|
|
Ok(file) => file,
|
|
Err(_err) => {
|
|
//crate::warn!("Could not open logfile {:?} for writing: {}", path, err);
|
|
continue;
|
|
},
|
|
};
|
|
|
|
internal.fix_perms(&mut file);
|
|
|
|
if let Err(_err) = write_log(&mut file, command).await {
|
|
//crate::warn!("Failed writing to logfile {:?}: {}", path, err);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
fn finalise(&mut self, _: Index)
|
|
{
|
|
|
|
}
|
|
|
|
fn println(&mut self, level: &dyn AsLevel, what: &dyn Display, trace: &Trace) -> io::Result<()>
|
|
{
|
|
let com = Command {
|
|
timestamp: if self.internal.use_local_time {Date::now_local()} else {Date::now_utc()},
|
|
is_err: level.is_err(),
|
|
trace: trace.clone(),
|
|
message: what.to_string(),
|
|
level: level.borrow().clone(),
|
|
};
|
|
|
|
if let Some(sender) = &mut self.sender {
|
|
if let Err(e) = sender.try_send(com) {
|
|
return Err(io::Error::new(ErrorKind::BrokenPipe, format!("Failed to pass to worker: {}", e)));
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|