Files
snitch/snitch-lib/src/lib.rs
2024-09-21 14:08:24 +02:00

79 lines
1.9 KiB
Rust

use std::process::Command;
use log::{Level, Log, Metadata, Record};
use reqwest::blocking;
mod message;
pub use message::{LogMsg, Severity};
pub struct SnitchLogger<L: Log> {
wrapped: L,
url: String,
service_name: String,
hostname: String,
}
impl<L: Log> SnitchLogger<L> {
pub fn new(wrapped: L, url: impl Into<String>, service_name: impl Into<String>) -> Self {
SnitchLogger {
wrapped,
url: url.into(),
service_name: service_name.into(),
hostname: probe_hostname(),
}
}
}
impl<L: Log> Log for SnitchLogger<L> {
fn enabled(&self, metadata: &Metadata) -> bool {
self.wrapped.enabled(metadata) || metadata.level() <= Level::Warn
}
fn log(&self, record: &Record) {
self.wrapped.log(record);
let severity = match record.metadata().level() {
Level::Error => Severity::Error,
Level::Warn => Severity::Warning,
_ => return,
};
let record = LogMsg {
severity,
file: record.file().map(String::from),
line: record.line(),
..LogMsg::new(
self.service_name.clone(),
record.args().to_string(),
self.hostname.clone(),
)
};
let client = blocking::Client::new();
if let Err(e) = client.post(&self.url).json(&record).send() {
// TODO: log error (without sending it)
eprintln!("failed to send log record: {e:?}");
}
}
fn flush(&self) {
self.wrapped.flush();
}
}
pub fn probe_hostname() -> String {
let output = Command::new("uname").arg("-n").output().ok();
output
.as_ref()
.and_then(|output| std::str::from_utf8(&output.stdout).ok())
.unwrap_or("<unknown_hostname>")
.to_string()
}
impl<L: Log> Drop for SnitchLogger<L> {
fn drop(&mut self) {
self.flush();
}
}