From 55370dd87a29f5750b002a2217e09e49d0fcc2b7 Mon Sep 17 00:00:00 2001 From: ForgQi <34411314+ForgQi@users.noreply.github.com> Date: Thu, 27 Nov 2025 03:41:09 +0800 Subject: [PATCH] Add dynamic log level reload support to server Introduces tracing_subscriber::reload to enable dynamic log level changes at runtime. Updates the main server initialization and ServiceRegister to accept and store a log handle. Refactors related code to support this feature and removes disabled state from developer UI log level selectors. --- Cargo.lock | 4 +- Cargo.toml | 2 +- app/ui/plugins/developer.tsx | 2 - crates/biliup-cli/src/lib.rs | 11 +++- crates/biliup-cli/src/main.rs | 10 ++- crates/biliup-cli/src/server/api/endpoints.rs | 62 +++++++++---------- .../biliup-cli/src/server/common/download.rs | 7 +-- crates/biliup-cli/src/server/common/upload.rs | 7 ++- crates/biliup-cli/src/server/config.rs | 6 +- crates/biliup-cli/src/server/core/monitor.rs | 4 +- .../infrastructure/models/live_streamer.rs | 2 +- .../server/infrastructure/service_register.rs | 9 ++- crates/stream-gears/src/server.rs | 50 +++++++++------ 13 files changed, 101 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3b6348..b9d50f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -362,7 +362,7 @@ dependencies = [ [[package]] name = "biliup-cli" -version = "1.1.24" +version = "1.1.25" dependencies = [ "async-channel", "async-trait", @@ -3645,7 +3645,7 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stream-gears" -version = "1.1.24" +version = "1.1.25" dependencies = [ "async-channel", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 54ce84b..cb8fec1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = [ resolver = "3" [workspace.package] -version = "1.1.24" +version = "1.1.25" [workspace.dependencies] serde = { version = "1.0", features = ["derive"] } diff --git a/app/ui/plugins/developer.tsx b/app/ui/plugins/developer.tsx index 848a6f6..d750932 100644 --- a/app/ui/plugins/developer.tsx +++ b/app/ui/plugins/developer.tsx @@ -52,7 +52,6 @@ const Developer: React.FC = () => { formApi.setValue('LOGGING.root.handlers', ['console']) }} showClear={true} - disabled > DEBUG INFO @@ -73,7 +72,6 @@ const Developer: React.FC = () => { formApi.setValue('LOGGING.loggers.biliup.handlers', ['file']) }} showClear={true} - disabled > DEBUG INFO diff --git a/crates/biliup-cli/src/lib.rs b/crates/biliup-cli/src/lib.rs index 9530046..033a894 100644 --- a/crates/biliup-cli/src/lib.rs +++ b/crates/biliup-cli/src/lib.rs @@ -14,8 +14,14 @@ use clap::ValueEnum; use error_stack::ResultExt; use std::net::ToSocketAddrs; use std::sync::{Arc, RwLock}; +use tracing_subscriber::{EnvFilter, Registry, reload}; -pub async fn run(addr: (&str, u16), auth: bool) -> AppResult<()> { +// 定义 Handle 的类型别名,简化代码 +// EnvFilter: 我们使用的过滤器类型 +// Registry: 基础的 Subscriber 类型 +type LogHandle = reload::Handle; + +pub async fn run(addr: (&str, u16), auth: bool, log_handle: LogHandle) -> AppResult<()> { // let config = Arc::new(AppConfig::parse()); tracing::info!( @@ -31,7 +37,8 @@ pub async fn run(addr: (&str, u16), auth: bool) -> AppResult<()> { config.read().unwrap().pool2_size, conn_pool.clone(), ); - let service_register = ServiceRegister::new(conn_pool, config.clone(), download_manager); + let service_register = + ServiceRegister::new(conn_pool, config.clone(), download_manager, log_handle); tracing::info!("migrations successfully ran, initializing axum server..."); let addr = addr diff --git a/crates/biliup-cli/src/main.rs b/crates/biliup-cli/src/main.rs index 5f085ce..050fc96 100644 --- a/crates/biliup-cli/src/main.rs +++ b/crates/biliup-cli/src/main.rs @@ -9,6 +9,7 @@ use clap::Parser; use biliup_cli::server::errors::AppResult; use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::reload; use tracing_subscriber::util::SubscriberInitExt; #[tokio::main] @@ -33,8 +34,11 @@ async fn main() -> AppResult<()> { "[year]-[month]-[day] [hour]:[minute]:[second]" )); + let console_filter = tracing_subscriber::EnvFilter::new(&cli.rust_log); + // let (file_filter_layer, file_reload_handle) = reload::Layer::new(file_filter); + let (console_filter_layer, console_reload_handle) = reload::Layer::new(console_filter); tracing_subscriber::registry() - .with(tracing_subscriber::EnvFilter::new(&cli.rust_log)) + .with(console_filter_layer) .with(tracing_subscriber::fmt::layer().with_timer(timer)) .init(); @@ -97,7 +101,9 @@ async fn main() -> AppResult<()> { split_size, split_time, } => download(&url, output, split_size, split_time).await?, - Commands::Server { bind, port, auth } => biliup_cli::run((&bind, port), auth).await?, + Commands::Server { bind, port, auth } => { + biliup_cli::run((&bind, port), auth, console_reload_handle).await? + } Commands::List { is_pubing, pubed, diff --git a/crates/biliup-cli/src/server/api/endpoints.rs b/crates/biliup-cli/src/server/api/endpoints.rs index 20c0b5d..ba894d2 100644 --- a/crates/biliup-cli/src/server/api/endpoints.rs +++ b/crates/biliup-cli/src/server/api/endpoints.rs @@ -544,37 +544,37 @@ pub async fn post_uploads( (line, limit, submit_api) }; info!("通过页面开始上传"); - let (bilibili, videos) = upload( - upload_config - .user_cookie - .as_deref() - .unwrap_or("cookies.json"), - None, - line, - &json_data.files, - limit as usize, - ) - .await - .map_err(report_to_response)?; - if !videos.is_empty() { - let recorder = Recorder::new( - upload_config.title.clone(), - StreamerInfo::new( - &upload_config.template_name, - "stream_title", + tokio::spawn(async move { + let (bilibili, videos) = upload( + upload_config + .user_cookie + .as_deref() + .unwrap_or("cookies.json"), + None, + line, + &json_data.files, + limit as usize, + ) + .await?; + if !videos.is_empty() { + let recorder = Recorder::new( + upload_config.title.clone(), + StreamerInfo::new( + &upload_config.template_name, + "stream_title", + "", + Utc::now(), + "", + ), "", - Utc::now(), - "", - ), - "", - ); - let studio = build_studio(&upload_config, &bilibili, videos, recorder) - .await - .map_err(report_to_response)?; - let response_data = submit_to_bilibili(&bilibili, &studio, submit_api.as_deref()) - .await - .map_err(report_to_response)?; - info!("通过页面上传成功 {:?}", response_data); - } + ); + let studio = build_studio(&upload_config, &bilibili, videos, recorder).await?; + let response_data = + submit_to_bilibili(&bilibili, &studio, submit_api.as_deref()).await?; + info!("通过页面上传成功 {:?}", response_data); + } + Ok::<_, Report>(()) + }); + Ok(Json(serde_json::json!({}))) } diff --git a/crates/biliup-cli/src/server/common/download.rs b/crates/biliup-cli/src/server/common/download.rs index 4ee1ade..d3bb5d7 100644 --- a/crates/biliup-cli/src/server/common/download.rs +++ b/crates/biliup-cli/src/server/common/download.rs @@ -114,12 +114,7 @@ impl SegmentEventProcessor { channel: None, uploader, file_validator: FileValidator::new( - ctx.worker - .clone() - .get_config() - .filtering_threshold - * 1000 - * 1000, + ctx.worker.clone().get_config().filtering_threshold * 1000 * 1000, true, ), ctx, diff --git a/crates/biliup-cli/src/server/common/upload.rs b/crates/biliup-cli/src/server/common/upload.rs index 3112416..df31d17 100644 --- a/crates/biliup-cli/src/server/common/upload.rs +++ b/crates/biliup-cli/src/server/common/upload.rs @@ -239,7 +239,12 @@ pub(crate) async fn build_studio( .maybe_copyright(upload_config.copyright) .cover(upload_config.cover_path.clone().unwrap_or_default()) .dynamic(upload_config.dynamic.clone().unwrap_or_default()) - .source(upload_config.copyright_source.clone().unwrap_or_default()) + .source( + upload_config + .copyright_source + .clone() + .unwrap_or_else(|| recorder.streamer_info.url.clone()), + ) .tag(upload_config.tags.join(",")) .maybe_tid(upload_config.tid) .title(recorder.format_filename()) diff --git a/crates/biliup-cli/src/server/config.rs b/crates/biliup-cli/src/server/config.rs index 96eeb12..cd29bea 100644 --- a/crates/biliup-cli/src/server/config.rs +++ b/crates/biliup-cli/src/server/config.rs @@ -441,10 +441,8 @@ mod tests { assert_eq!( single, - Config::builder() - .streamers(Default::default()) - .build(), + Config::builder().streamers(Default::default()).build(), "普通Option正常包裹一层" ); } -} \ No newline at end of file +} diff --git a/crates/biliup-cli/src/server/core/monitor.rs b/crates/biliup-cli/src/server/core/monitor.rs index 4b97dcb..87a71b1 100644 --- a/crates/biliup-cli/src/server/core/monitor.rs +++ b/crates/biliup-cli/src/server/core/monitor.rs @@ -198,7 +198,9 @@ impl Monitor { // 没有必要检查两次失败 let _ = self.sender.send(msg).await; if let Some(worker) = recv.await.expect("Actor task has been killed") { - worker.change_status(Stage::Download, WorkerStatus::Idle).await; + worker + .change_status(Stage::Download, WorkerStatus::Idle) + .await; } } diff --git a/crates/biliup-cli/src/server/infrastructure/models/live_streamer.rs b/crates/biliup-cli/src/server/infrastructure/models/live_streamer.rs index fe7f881..28ecd56 100644 --- a/crates/biliup-cli/src/server/infrastructure/models/live_streamer.rs +++ b/crates/biliup-cli/src/server/infrastructure/models/live_streamer.rs @@ -1,8 +1,8 @@ +use crate::server::config::{Config, ConfigPatch}; use crate::server::infrastructure::models::hook_step::HookStep; use ormlite::{Insert, Model}; use serde::{Deserialize, Serialize}; use serde_json::Value; -use crate::server::config::{Config, ConfigPatch}; /// 直播主播模型 /// 存储直播主播的配置信息和录制参数 diff --git a/crates/biliup-cli/src/server/infrastructure/service_register.rs b/crates/biliup-cli/src/server/infrastructure/service_register.rs index 1202469..4916735 100644 --- a/crates/biliup-cli/src/server/infrastructure/service_register.rs +++ b/crates/biliup-cli/src/server/infrastructure/service_register.rs @@ -1,3 +1,4 @@ +use crate::LogHandle; use crate::server::config::Config; use crate::server::core::download_manager::DownloadManager; use crate::server::core::plugin::yy::YY; @@ -7,9 +8,9 @@ use crate::server::infrastructure::models::live_streamer::LiveStreamer; use crate::server::infrastructure::models::upload_streamer::UploadStreamer; use axum::extract::FromRef; use biliup::client::StatelessClient; -use std::sync::{Arc, RwLock}; -use error_stack::fmt::ColorMode; use error_stack::Report; +use error_stack::fmt::ColorMode; +use std::sync::{Arc, RwLock}; use tracing::info; /// 服务注册器 @@ -24,6 +25,8 @@ pub struct ServiceRegister { pub config: Arc>, /// HTTP客户端 pub client: StatelessClient, + #[from_ref(skip)] + log_handle: LogHandle, } /// 简单的服务容器,负责管理API端点通过axum扩展获取的各种服务 @@ -39,6 +42,7 @@ impl ServiceRegister { pool: ConnectionPool, config: Arc>, download_manager: DownloadManager, + log_handle: LogHandle, ) -> Self { Report::set_color_mode(ColorMode::None); info!("initializing utility services..."); @@ -56,6 +60,7 @@ impl ServiceRegister { managers: Arc::new(download_manager), config: config.clone(), client, + log_handle, } } diff --git a/crates/stream-gears/src/server.rs b/crates/stream-gears/src/server.rs index 3bb3f0a..b201b05 100644 --- a/crates/stream-gears/src/server.rs +++ b/crates/stream-gears/src/server.rs @@ -35,9 +35,9 @@ use std::sync::{Arc, LazyLock, RwLock}; use time::macros::format_description; use tracing::{debug, info, warn}; use tracing_appender::rolling::Rotation; -use tracing_subscriber::EnvFilter; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; +use tracing_subscriber::{EnvFilter, reload}; #[derive(Debug)] pub struct PyPlugin { @@ -339,6 +339,14 @@ pub(crate) async fn _main(args: &[String]) -> AppResult<()> { let local_time = tracing_subscriber::fmt::time::LocalTime::new(format_description!( "[year]-[month]-[day] [hour]:[minute]:[second]" )); + + let console_layer = tracing_subscriber::fmt::layer() + .with_target(false) + .with_timer(local_time.clone()) + .with_file(true) // 打印文件名 + .with_line_number(true) + .with_thread_ids(true); + // 按日期滚动,每天创建新文件 let file_appender = tracing_appender::rolling::RollingFileAppender::builder() .rotation(Rotation::DAILY) // rotate log files once every hour @@ -355,27 +363,25 @@ pub(crate) async fn _main(args: &[String]) -> AppResult<()> { let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender); + let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); + let (filter_layer, reload_handle) = reload::Layer::new(filter); + + let file_layer = tracing_subscriber::fmt::layer() + .with_writer(non_blocking) + .with_timer(local_time) + .with_target(true) + .with_thread_ids(true) + .with_file(true) + .with_line_number(true) + .with_ansi(false); + let subscriber = tracing_subscriber::registry() - .with(EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"))) + .with(filter_layer) // 这个是“总开关”,所有 layer 都会被它过滤 // 控制台输出 - .with( - tracing_subscriber::fmt::layer() - .with_target(false) - .with_timer(local_time.clone()) - .with_file(true) // 打印文件名 - .with_line_number(true) - .with_thread_ids(true), - ) + .with(console_layer) // 文件输出 .with( - tracing_subscriber::fmt::layer() - .with_writer(non_blocking) - .with_timer(local_time) - .with_target(true) - .with_thread_ids(true) - .with_file(true) - .with_line_number(true) - .with_ansi(false), // .json() // 可选:使用 JSON 格式便于解析 + file_layer, // .json() // 可选:使用 JSON 格式便于解析 ); subscriber.init(); @@ -461,8 +467,12 @@ pub(crate) async fn _main(args: &[String]) -> AppResult<()> { download_manager.add_plugin(Arc::new(v)); } - let service_register = - ServiceRegister::new(conn_pool.clone(), CONFIG.clone(), download_manager); + let service_register = ServiceRegister::new( + conn_pool.clone(), + CONFIG.clone(), + download_manager, + reload_handle, + ); let all_streamer = repositories::get_all_streamer(&conn_pool).await?;