use std::sync::Arc;
use std::time::Duration;
use crate::cli::command::{Command, CommandFuture};
use crate::cli::format as fmt;
use crate::core::shared_component::SharedComponent;
use infrarust_config::LogType;
use tracing::debug;
const NA: &str = "N/A";
pub struct DebugCommand {
shared: Arc<SharedComponent>,
}
impl DebugCommand {
pub fn new(shared: Arc<SharedComponent>) -> Self {
Self { shared }
}
async fn format_debug_info(&self) -> String {
let mut result = String::new();
let supervisor = self.shared.actor_supervisor();
let actors_data = supervisor.get_all_actors().await;
result.push_str(&format!(
"{}\n\n",
fmt::header("Actor and Task Debug Information")
));
let mut total_actors = 0;
for (config_id, actors) in &actors_data {
let actor_count = actors.len();
total_actors += actor_count;
result.push_str(&format!(
"{} {} - {} actors\n",
fmt::sub_header("Config"),
fmt::entity(config_id),
actor_count
));
for (i, actor) in actors.iter().enumerate() {
let alive_duration = format_duration(actor.created_at.elapsed());
let is_shutdown = actor.shutdown.load(std::sync::atomic::Ordering::SeqCst);
result.push_str(&format!(
" {}. {} - Session: {} - Age: {} - {}\n",
i + 1,
if actor.username.is_empty() {
fmt::secondary("<status>")
} else {
fmt::entity(&actor.username)
},
fmt::id(&actor.session_id.to_string()),
fmt::secondary(&alive_duration),
if is_shutdown {
fmt::warning("SHUTDOWN")
} else {
fmt::success("ACTIVE")
}
));
}
result.push('\n');
}
result.push_str(&self.format_memory_metrics());
if let Some(usage) = get_memory_usage() {
result.push_str(&format!(
"\n{}\n",
fmt::sub_header(&format!("Current process memory usage: {:.2} MB", usage))
));
}
result.push_str(&format!(
"\n{}\n",
fmt::header(&format!("Total Actors: {}", total_actors))
));
result
}
fn format_memory_metrics(&self) -> String {
let mut result = String::new();
result.push_str(&format!("{}\n\n", fmt::header("Memory Retention Metrics")));
result.push_str(&format!("{}\n", fmt::warning("[CRITICAL]")));
let supervisor = self.shared.actor_supervisor();
if let Some(metrics) = supervisor.get_memory_metrics() {
result.push_str(&format!(
" ActorSupervisor :: actors = {}\n",
metrics.total_actor_entries
));
for (config_id, count) in &metrics.actors_by_config {
if *count > 0 {
result.push_str(&format!(" - {}: {}\n", fmt::entity(config_id), count));
}
}
result.push_str(&format!(
" ActorSupervisor :: tasks = {}\n",
metrics.total_task_entries
));
for (config_id, count) in &metrics.tasks_by_config {
if *count > 0 {
result.push_str(&format!(" - {}: {}\n", fmt::entity(config_id), count));
}
}
result.push_str(&format!(
" ActorSupervisor :: orphaned_tasks = {}\n",
if metrics.orphaned_tasks > 0 {
fmt::warning(&metrics.orphaned_tasks.to_string())
} else {
fmt::success(&metrics.orphaned_tasks.to_string())
}
));
} else {
result.push_str(&format!(" ActorSupervisor :: actors = {}\n", NA));
result.push_str(&format!(" ActorSupervisor :: tasks = {}\n", NA));
result.push_str(&format!(" ActorSupervisor :: orphaned_tasks = {}\n", NA));
}
if let Some(rate_limiter_metrics) = self.shared.filter_registry().get_rate_limiter_metrics()
{
for (name, counter_size) in rate_limiter_metrics {
match counter_size {
Some(size) => {
result.push_str(&format!(
" RateLimiter ({}) :: counters = {} {}\n",
fmt::entity(&name),
size,
fmt::secondary("(no limit)")
));
}
None => {
result.push_str(&format!(
" RateLimiter ({}) :: counters = {}\n",
fmt::entity(&name),
NA
));
}
}
}
} else {
result.push_str(&format!(" RateLimiter :: counters = {}\n", NA));
}
result.push('\n');
result.push_str(&format!("{}\n", fmt::warning("[HIGH]")));
if let Some(gateway) = self.shared.gateway() {
if let Some(metrics) = gateway.get_memory_metrics() {
result.push_str(&format!(
" Gateway :: pending_status_requests = {}\n",
metrics.pending_status_requests_count
));
} else {
result.push_str(&format!(" Gateway :: pending_status_requests = {}\n", NA));
}
} else {
result.push_str(&format!(" Gateway :: pending_status_requests = {}\n", NA));
}
if let Some(metrics) = self.shared.server_managers().get_memory_metrics() {
result.push_str(&format!(
" Manager :: starting_servers = {}\n",
metrics.starting_servers_count
));
} else {
result.push_str(&format!(" Manager :: starting_servers = {}\n", NA));
}
result.push('\n');
result.push_str(&format!("{}\n", fmt::secondary("[MEDIUM]")));
if let Some(gateway) = self.shared.gateway() {
if let Some(metrics) = gateway.get_memory_metrics() {
result.push_str(&format!(
" Gateway :: status_cache_entries = {} / {}\n",
metrics.status_cache_entries, metrics.status_cache_max_size
));
} else {
result.push_str(&format!(" Gateway :: status_cache_entries = {}\n", NA));
}
} else {
result.push_str(&format!(" Gateway :: status_cache_entries = {}\n", NA));
}
if let Some(metrics) = self.shared.server_managers().get_memory_metrics() {
result.push_str(&format!(
" Manager :: time_since_empty = {}\n",
metrics.time_since_empty_count
));
result.push_str(&format!(
" Manager :: shutdown_tasks = {}\n",
metrics.shutdown_tasks_count
));
result.push_str(&format!(
" Manager :: shutdown_timers = {}\n",
metrics.shutdown_timers_count
));
} else {
result.push_str(&format!(" Manager :: time_since_empty = {}\n", NA));
result.push_str(&format!(" Manager :: shutdown_tasks = {}\n", NA));
result.push_str(&format!(" Manager :: shutdown_timers = {}\n", NA));
}
if let Some(count) = self.shared.configuration_service().config_count() {
result.push_str(&format!(
" ConfigurationService :: configurations = {}\n",
count
));
} else {
result.push_str(&format!(
" ConfigurationService :: configurations = {}\n",
NA
));
}
if let Some(count) = self.shared.filter_registry().filter_count() {
result.push_str(&format!(
" FilterRegistry :: registered_filters = {}\n",
count
));
} else {
result.push_str(&format!(
" FilterRegistry :: registered_filters = {}\n",
NA
));
}
result
}
}
impl Command for DebugCommand {
fn name(&self) -> &'static str {
"debug"
}
fn description(&self) -> &'static str {
"Shows detailed debug information about active actors and tasks"
}
fn execute(&self, _args: Vec<String>) -> CommandFuture {
debug!(
log_type = LogType::Supervisor.as_str(),
"Executing debug command"
);
let shared = self.shared.clone();
Box::pin(async move {
let debug_cmd = DebugCommand { shared };
debug_cmd.format_debug_info().await
})
}
}
fn format_duration(duration: Duration) -> String {
if duration.as_secs() < 60 {
format!("{}s", duration.as_secs())
} else if duration.as_secs() < 3600 {
format!("{}m {}s", duration.as_secs() / 60, duration.as_secs() % 60)
} else {
format!(
"{}h {}m {}s",
duration.as_secs() / 3600,
(duration.as_secs() % 3600) / 60,
duration.as_secs() % 60
)
}
}
#[cfg(target_os = "linux")]
fn get_memory_usage() -> Option<f64> {
use std::fs::File;
use std::io::Read;
let mut status = String::new();
if File::open("/proc/self/status")
.ok()?
.read_to_string(&mut status)
.is_ok()
{
for line in status.lines() {
if line.starts_with("VmRSS:") {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2
&& let Ok(kb) = parts[1].parse::<f64>()
{
return Some(kb / 1024.0); }
}
}
}
None
}
#[cfg(not(target_os = "linux"))]
fn get_memory_usage() -> Option<f64> {
None
}