Refactor statistics page and enhance logging
- Updated the layout and styling of the statistics page for better responsiveness and visual appeal. - Introduced a new error page for 404 errors with user-friendly messaging and navigation options. - Enhanced logging functionality to capture detailed events related to asset uploads, deletions, and HTTP requests. - Implemented an AssetTracker to manage assets in memory, allowing for efficient tracking and retrieval. - Improved the API for uploading and retrieving assets, ensuring better error handling and response formatting. - Added auto-refresh functionality to the statistics page to keep data up-to-date.
This commit is contained in:
123
src/logs.rs
123
src/logs.rs
@@ -1,68 +1,72 @@
|
||||
use std::{fs::OpenOptions, io::Write, time::Instant};
|
||||
use std::{fs::OpenOptions, io::Write};
|
||||
|
||||
use actix_web::HttpRequest;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::LOG_DIR;
|
||||
use crate::{LOG_DIR, data_mgt::Asset};
|
||||
|
||||
pub fn log_to_file(req: &HttpRequest, start: Instant) {
|
||||
let delta = start.elapsed().as_nanos();
|
||||
println!("Request processed in {} ns", delta);
|
||||
let duration_ms = delta as f64 / 1000_000.0;
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct LogHttpRequest {
|
||||
pub method: String,
|
||||
pub path: String,
|
||||
pub query_string: String,
|
||||
pub scheme: String,
|
||||
pub ip: String,
|
||||
pub real_ip: String,
|
||||
pub user_agent: String,
|
||||
}
|
||||
impl From<HttpRequest> for LogHttpRequest {
|
||||
fn from(req: HttpRequest) -> Self {
|
||||
let method = req.method().as_str().to_string();
|
||||
let uri = req.uri();
|
||||
let path = uri.path().to_string();
|
||||
let query_string = uri.query().unwrap_or("-").to_string();
|
||||
|
||||
let log_path = LOG_DIR.to_string() + "access.log";
|
||||
let connection_info = req.connection_info();
|
||||
let scheme = connection_info.scheme().to_string();
|
||||
let ip = connection_info.peer_addr().unwrap_or("-").to_string();
|
||||
let real_ip = connection_info.realip_remote_addr().unwrap_or("-").to_string();
|
||||
|
||||
// Ensure log directory exists
|
||||
if let Err(e) = std::fs::create_dir_all(LOG_DIR) {
|
||||
eprintln!("failed to create log dir: {}", e);
|
||||
return;
|
||||
let user_agent = req
|
||||
.headers()
|
||||
.get("user-agent")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("-")
|
||||
.to_string();
|
||||
|
||||
LogHttpRequest {
|
||||
method,
|
||||
path,
|
||||
query_string,
|
||||
scheme,
|
||||
ip,
|
||||
real_ip,
|
||||
user_agent,
|
||||
}
|
||||
}
|
||||
|
||||
let Ok(mut file) = OpenOptions::new().create(true).append(true).open(log_path) else {
|
||||
eprintln!("failed to open log file");
|
||||
return;
|
||||
};
|
||||
|
||||
let ts = chrono::Local::now().to_rfc3339();
|
||||
|
||||
let method = req.method();
|
||||
let uri = req.uri();
|
||||
let path = uri.path();
|
||||
let query = uri.query().unwrap_or("-");
|
||||
|
||||
let connection_info = req.connection_info();
|
||||
let scheme = connection_info.scheme();
|
||||
let ip = connection_info.peer_addr().unwrap_or("-");
|
||||
let real_ip = connection_info.realip_remote_addr().unwrap_or("-");
|
||||
|
||||
let ua = req
|
||||
.headers()
|
||||
.get("user-agent")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("-");
|
||||
|
||||
let line = format!(
|
||||
"{ts} scheme={scheme} ip={ip} real_ip={real_ip} method={method} path={path} qs={query} dur_ms={duration_ms} ua=\"{ua}\"\n"
|
||||
);
|
||||
|
||||
let _ = file.write_all(line.as_bytes());
|
||||
}
|
||||
|
||||
pub fn log_asset_event(
|
||||
action: &str,
|
||||
id: &str,
|
||||
mime: &str,
|
||||
size_bytes: usize,
|
||||
duration_min: u32,
|
||||
created_at_ms: i64,
|
||||
expires_at_ms: i64,
|
||||
uploader_ip: &str,
|
||||
) {
|
||||
// Ensure logging directory exists before writing
|
||||
if let Err(e) = std::fs::create_dir_all(LOG_DIR) {
|
||||
eprintln!("failed to create log dir for asset event: {}", e);
|
||||
return;
|
||||
}
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum LogEventType<'a> {
|
||||
AssetUploaded(&'a Asset),
|
||||
AssetDeleted(&'a Asset),
|
||||
HttpRequest(&'a LogHttpRequest),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct LogEvent<'a> {
|
||||
pub time: String,
|
||||
pub event: LogEventType<'a>,
|
||||
}
|
||||
|
||||
impl<'a> From<LogEventType<'a>> for LogEvent<'a> {
|
||||
fn from(event: LogEventType<'a>) -> Self {
|
||||
let time = chrono::Utc::now().to_rfc3339();
|
||||
LogEvent { time, event }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_event(event: LogEventType) {
|
||||
let log_path = LOG_DIR.to_string() + "access.log";
|
||||
|
||||
let Ok(mut file) = OpenOptions::new().create(true).append(true).open(log_path) else {
|
||||
@@ -70,11 +74,8 @@ pub fn log_asset_event(
|
||||
return;
|
||||
};
|
||||
|
||||
let ts = chrono::Local::now().to_rfc3339();
|
||||
let log_event: LogEvent = event.into();
|
||||
let line = serde_json::to_string(&log_event).unwrap_or_else(|e| e.to_string());
|
||||
|
||||
let line = format!(
|
||||
"{ts} event=asset action={action} id={id} mime={mime} size_bytes={size_bytes} duration_min={duration_min} created_at_ms={created_at_ms} expires_at_ms={expires_at_ms} uploader_ip={uploader_ip}\n"
|
||||
);
|
||||
|
||||
let _ = file.write_all(line.as_bytes());
|
||||
let _ = writeln!(file, "{}", line);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user