feat(redis): support MONITOR live command log

This commit is contained in:
zipg authored and GitHub committed 2026-09-10 19:23:18 +08:00
1 parent 7b7ed6f7b1
commit 77323715e7
25 files changed
+439 -7

No files matched your search

+34
View File
@@ -1121,6 +1121,35 @@ pub async fn connect_direct_node(
connect_client(client).await
}
pub async fn connect_monitor(
config: &ConnectionConfig,
host: &str,
port: u16,
timeout: std::time::Duration,
) -> Result<redis::aio::Monitor, String> {
let mut last_error = String::new();
for info in standalone_connection_infos(config, host, port) {
let client = redis::Client::open(info).map_err(|error| error.to_string())?;
let connect = async {
let mut validation = client.get_multiplexed_async_connection().await?;
redis::cmd("MONITOR").query_async::<()>(&mut validation).await?;
drop(validation);
client.get_async_monitor().await
};
match tokio::time::timeout(timeout, connect).await {
Ok(Ok(monitor)) => return Ok(monitor),
Ok(Err(error)) => {
last_error = error.to_string();
if !is_redis_auth_error(&last_error) {
break;
}
}
Err(_) => return Err("Redis MONITOR connection timed out".to_string()),
}
}
Err(last_error)
}
pub async fn connect_pubsub(
config: &ConnectionConfig,
host: &str,
@@ -2218,6 +2247,11 @@ where
{
let argv = parse_command_argv(command_text)?;
let command = argv[0].to_ascii_uppercase();
if command == "MONITOR" {
return Err(
"MONITOR requires a dedicated streaming connection; run MONITOR alone in the query editor".to_string()
);
}
let safety = classify_command(&command);
if !skip_safety_check && safety == RedisCommandSafety::Blocked {
return Err(format!("Redis command is blocked for safety: {command}"));
+22
View File
@@ -1141,6 +1141,28 @@ pub async fn redis_publish_core(
}
}
pub async fn redis_create_monitor_core(state: &AppState, connection_id: &str) -> Result<redis::aio::Monitor, String> {
let config = state.configs.read().await.get(connection_id).ok_or("Connection config not found")?.clone();
if config.db_type != crate::models::connection::DatabaseType::Redis {
return Err("Not a Redis connection".to_string());
}
if config.uses_redis_sentinel() || config.uses_redis_cluster() {
return Err(
"MONITOR requires a direct Redis node connection; connect to the node you want to monitor".to_string()
);
}
let (host, port) = state.connection_host_port(connection_id, &config).await?;
let mut runtime_config = config.clone();
state.apply_session_credential(&config, &mut runtime_config, connection_id);
redis_driver::connect_monitor(
&runtime_config,
&host,
port,
std::time::Duration::from_secs(config.effective_connect_timeout_secs()),
)
.await
}
pub async fn redis_create_pubsub_core(state: &AppState, connection_id: &str) -> Result<redis::aio::PubSub, String> {
let configs = state.configs.read().await;
let config = configs.get(connection_id).ok_or("Connection config not found")?.clone();