diff options
author | metamuffin <metamuffin@disroot.org> | 2025-02-03 17:05:17 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-02-03 17:05:17 +0100 |
commit | 8d22e7fa56cfbceb3c829c6f22dc99234fd20b8d (patch) | |
tree | d84f23de63bd691ead670c9f19385b720579c216 /server/src | |
parent | 1d0d8aa6fd6f788ff8b2852673f616608966ebd5 (diff) | |
download | jellything-8d22e7fa56cfbceb3c829c6f22dc99234fd20b8d.tar jellything-8d22e7fa56cfbceb3c829c6f22dc99234fd20b8d.tar.bz2 jellything-8d22e7fa56cfbceb3c829c6f22dc99234fd20b8d.tar.zst |
live log view
Diffstat (limited to 'server/src')
-rw-r--r-- | server/src/routes/mod.rs | 3 | ||||
-rw-r--r-- | server/src/routes/ui/admin/log.rs | 66 |
2 files changed, 56 insertions, 13 deletions
diff --git a/server/src/routes/mod.rs b/server/src/routes/mod.rs index b7d63da..e0b955a 100644 --- a/server/src/routes/mod.rs +++ b/server/src/routes/mod.rs @@ -30,7 +30,7 @@ use ui::{ settings::{r_account_settings, r_account_settings_post}, }, admin::{ - log::r_admin_log, + log::{r_admin_log, r_admin_log_stream}, r_admin_dashboard, r_admin_delete_cache, r_admin_import, r_admin_invite, r_admin_remove_invite, r_admin_transcode_posters, r_admin_update_search, user::{r_admin_remove_user, r_admin_user, r_admin_user_permission, r_admin_users}, @@ -135,6 +135,7 @@ pub fn build_rocket(database: Database, federation: Federation) -> Rocket<Build> r_admin_delete_cache, r_admin_transcode_posters, r_admin_log, + r_admin_log_stream, r_admin_import, r_admin_update_search, r_account_settings, diff --git a/server/src/routes/ui/admin/log.rs b/server/src/routes/ui/admin/log.rs index cc34dbc..f962138 100644 --- a/server/src/routes/ui/admin/log.rs +++ b/server/src/routes/ui/admin/log.rs @@ -13,12 +13,16 @@ use crate::{ }; use chrono::{DateTime, Utc}; use log::Level; +use markup::Render; use rocket::get; +use rocket_ws::{Message, Stream, WebSocket}; +use serde_json::json; use std::{ collections::VecDeque, fmt::Write, - sync::{LazyLock, RwLock}, + sync::{Arc, LazyLock, RwLock}, }; +use tokio::sync::broadcast; const MAX_LOG_LEN: usize = 4096; @@ -31,7 +35,11 @@ pub fn enable_logging() { pub struct Log { inner: env_logger::Logger, - log: RwLock<(VecDeque<LogLine>, VecDeque<LogLine>)>, + stream: ( + broadcast::Sender<Arc<LogLine>>, + broadcast::Sender<Arc<LogLine>>, + ), + log: RwLock<(VecDeque<Arc<LogLine>>, VecDeque<Arc<LogLine>>)>, } pub struct LogLine { @@ -41,14 +49,15 @@ pub struct LogLine { message: String, } -#[get("/admin/log?<warnonly>")] +#[get("/admin/log?<warnonly>", rank = 2)] pub fn r_admin_log<'a>(_session: AdminSession, warnonly: bool) -> MyResult<DynLayoutPage<'a>> { Ok(LayoutPage { title: "Log".into(), + class: Some("admin_log"), content: markup::new! { h1 { "Server Log" } a[href=uri!(r_admin_log(!warnonly))] { @if warnonly { "Show everything" } else { "Show only warnings" }} - code.log { + code.log[id="log"] { @let g = LOGGER.log.read().unwrap(); table { @for e in if warnonly { g.1.iter() } else { g.0.iter() } { tr[class=format!("level-{:?}", e.level).to_ascii_lowercase()] { @@ -63,6 +72,32 @@ pub fn r_admin_log<'a>(_session: AdminSession, warnonly: bool) -> MyResult<DynLa ..Default::default() }) } + +#[get("/admin/log?stream&<warnonly>", rank = 1)] +pub fn r_admin_log_stream( + _session: AdminSession, + ws: WebSocket, + warnonly: bool, +) -> Stream!['static] { + let mut stream = if warnonly { + LOGGER.stream.1.subscribe() + } else { + LOGGER.stream.0.subscribe() + }; + Stream! { ws => + let _ = ws; + while let Ok(line) = stream.recv().await { + yield Message::Text(json!({ + "time": line.time, + "level_class": format!("level-{:?}", line.level).to_ascii_lowercase(), + "level_html": format_level_string(line.level), + "module": line.module, + "message": vt100_to_html(&line.message), + }).to_string()); + } + } +} + impl Default for Log { fn default() -> Self { Self { @@ -70,6 +105,10 @@ impl Default for Log { .filter_level(log::LevelFilter::Warn) .parse_env("LOG") .build(), + stream: ( + tokio::sync::broadcast::channel(1024).0, + tokio::sync::broadcast::channel(1024).0, + ), log: Default::default(), } } @@ -85,24 +124,22 @@ impl Log { } } fn do_log(&self, record: &log::Record) { - let mut w = self.log.write().unwrap(); let time = Utc::now(); - w.0.push_back(LogLine { + let line = Arc::new(LogLine { time, module: record.module_path_static(), level: record.level(), message: record.args().to_string(), }); + let mut w = self.log.write().unwrap(); + w.0.push_back(line.clone()); + let _ = self.stream.0.send(line.clone()); while w.0.len() > MAX_LOG_LEN { w.0.pop_front(); } if record.level() <= Level::Warn { - w.1.push_back(LogLine { - time, - module: record.module_path_static(), - level: record.level(), - message: record.args().to_string(), - }); + let _ = self.stream.1.send(line.clone()); + w.1.push_back(line); while w.1.len() > MAX_LOG_LEN { w.1.pop_front(); } @@ -150,6 +187,11 @@ fn format_level(level: Level) -> impl markup::Render { }; markup::new! { span[style=format!("color:{c}")] {@s} } } +fn format_level_string(level: Level) -> String { + let mut w = String::new(); + format_level(level).render(&mut w).unwrap(); + w +} #[derive(Default)] pub struct HtmlOut { |