#![cfg(unix)]
use std::io;
use std::os::unix::net::UnixDatagram;
use std::sync::Arc;
use tracing_subscriber::fmt::MakeWriter;
const FACILITY_USER: u8 = 1;
#[derive(Clone)]
pub struct SyslogMakeWriter {
pub socket: Arc<UnixDatagram>,
}
impl<'a> MakeWriter<'a> for SyslogMakeWriter {
type Writer = SyslogWriter;
fn make_writer(&'a self) -> Self::Writer {
self.writer_for_severity(6) }
fn make_writer_for(&'a self, meta: &tracing::Metadata<'_>) -> Self::Writer {
self.writer_for_severity(severity_for_level(*meta.level()))
}
}
pub fn severity_for_level(level: tracing::Level) -> u8 {
match level {
tracing::Level::ERROR => 3,
tracing::Level::WARN => 4,
tracing::Level::INFO => 6,
tracing::Level::DEBUG | tracing::Level::TRACE => 7,
}
}
impl SyslogMakeWriter {
pub fn writer_for_severity(&self, severity: u8) -> SyslogWriter {
SyslogWriter {
socket: self.socket.clone(),
severity,
buf: Vec::new(),
}
}
}
pub struct SyslogWriter {
socket: Arc<UnixDatagram>,
severity: u8,
buf: Vec<u8>,
}
impl io::Write for SyslogWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buf.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
if self.buf.is_empty() {
return Ok(());
}
let pri = FACILITY_USER * 8 + self.severity;
let mut datagram = format!("<{pri}>yorishiro-server: ").into_bytes();
datagram.extend_from_slice(&self.buf);
self.socket.send(&datagram)?;
self.buf.clear();
Ok(())
}
}
impl Drop for SyslogWriter {
fn drop(&mut self) {
let _ = io::Write::flush(self);
}
}
#[cfg(test)]
#[path = "../../tests/logging/syslog.rs"]
mod tests;