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.
This commit is contained in:
ForgQi committed 2025-11-27 03:41:09 +08:00
1 parent 3698bcf28a
commit 55370dd87a
13 files changed
+101 -75

No files matched your search

Generated
+2 -2
View File
@@ -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",
+1 -1
View File
@@ -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"] }
-2
View File
@@ -52,7 +52,6 @@ const Developer: React.FC = () => {
formApi.setValue('LOGGING.root.handlers', ['console'])
}}
showClear={true}
disabled
>
<Select.Option value="DEBUG">DEBUG</Select.Option>
<Select.Option value="INFO">INFO</Select.Option>
@@ -73,7 +72,6 @@ const Developer: React.FC = () => {
formApi.setValue('LOGGING.loggers.biliup.handlers', ['file'])
}}
showClear={true}
disabled
>
<Select.Option value="DEBUG">DEBUG</Select.Option>
<Select.Option value="INFO">INFO</Select.Option>
+9 -2
View File
@@ -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<EnvFilter, Registry>;
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
+8 -2
View File
@@ -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,
+31 -31
View File
@@ -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<AppError>>(())
});
Ok(Json(serde_json::json!({})))
}
@@ -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,
@@ -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())
+2 -4
View File
@@ -441,10 +441,8 @@ mod tests {
assert_eq!(
single,
Config::builder()
.streamers(Default::default())
.build(),
Config::builder().streamers(Default::default()).build(),
"普通Option正常包裹一层"
);
}
}
}
+3 -1
View File
@@ -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;
}
}
@@ -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};
/// 直播主播模型
/// 存储直播主播的配置信息和录制参数
@@ -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<RwLock<Config>>,
/// HTTP客户端
pub client: StatelessClient,
#[from_ref(skip)]
log_handle: LogHandle,
}
/// 简单的服务容器,负责管理API端点通过axum扩展获取的各种服务
@@ -39,6 +42,7 @@ impl ServiceRegister {
pool: ConnectionPool,
config: Arc<RwLock<Config>>,
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,
}
}
+30 -20
View File
@@ -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?;