70 lines
1.9 KiB
Rust
70 lines
1.9 KiB
Rust
#![warn(clippy::all, clippy::pedantic)]
|
|
|
|
use chrono::{DateTime, Utc};
|
|
use serde::Serialize;
|
|
use std::fs::OpenOptions;
|
|
use std::io::{BufWriter, Write};
|
|
use std::path::Path;
|
|
use std::sync::{LazyLock, Mutex};
|
|
use url::Url;
|
|
|
|
#[derive(Serialize)]
|
|
pub struct ErrorEntry {
|
|
pub timestamp: DateTime<Utc>,
|
|
pub url: String,
|
|
pub error_type: String,
|
|
pub message: String,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub status_code: Option<u16>,
|
|
}
|
|
|
|
static LOG_WRITER: LazyLock<Mutex<Option<BufWriter<std::fs::File>>>> =
|
|
LazyLock::new(|| Mutex::new(None));
|
|
|
|
pub fn log_error(
|
|
log_path: &Path,
|
|
url: &Url,
|
|
error_type: &str,
|
|
message: &str,
|
|
status_code: Option<u16>,
|
|
) {
|
|
let entry = ErrorEntry {
|
|
timestamp: Utc::now(),
|
|
url: url.to_string(),
|
|
error_type: error_type.to_string(),
|
|
message: message.to_string(),
|
|
status_code,
|
|
};
|
|
|
|
let line = match serde_json::to_string(&entry) {
|
|
Ok(json) => json + "\n",
|
|
Err(e) => {
|
|
log::error!("Failed to serialize error entry: {e}");
|
|
return;
|
|
}
|
|
};
|
|
|
|
{
|
|
let mut guard = LOG_WRITER.lock().unwrap();
|
|
match guard.as_mut() {
|
|
Some(writer) => {
|
|
if let Err(e) = writer.write_all(line.as_bytes()) {
|
|
log::error!("Failed to write error log: {e}");
|
|
}
|
|
}
|
|
None => {
|
|
if let Ok(file) = OpenOptions::new().append(true).create(true).open(log_path) {
|
|
let mut writer = BufWriter::new(file);
|
|
let _ = writer.write_all(line.as_bytes());
|
|
let _ = writer.flush();
|
|
} else {
|
|
log::error!("Failed to open error log file: {}", log_path.display());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
log::info!("LOGGED ERROR [{error_type}]: {message}");
|
|
}
|