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?;