Skip to main content

systemprompt_cli/commands/admin/agents/
logs_db.rs

1//! Database-log patterns for `admin agents logs`.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use std::sync::Arc;
7
8use anyhow::{Context, Result, anyhow};
9use systemprompt_database::DbPool;
10use systemprompt_loader::ConfigLoader;
11use systemprompt_logging::LoggingRepository;
12use systemprompt_runtime::AppContext;
13
14use super::logs::LogsArgs;
15use super::types::AgentLogsOutput;
16use crate::CliConfig;
17use crate::shared::CommandOutput;
18
19pub(super) async fn execute_db_mode(args: &LogsArgs, config: &CliConfig) -> Result<CommandOutput> {
20    let ctx = Arc::new(
21        AppContext::new()
22            .await
23            .context("Failed to initialize app context")?,
24    );
25    execute_db_mode_with_pool(args, ctx.db_pool(), config).await
26}
27
28pub async fn execute_db_mode_with_pool(
29    args: &LogsArgs,
30    pool: &DbPool,
31    _config: &CliConfig,
32) -> Result<CommandOutput> {
33    let repo = LoggingRepository::new(pool)?;
34
35    let patterns = match &args.agent {
36        Some(agent) => build_agent_patterns(agent),
37        None => build_all_agent_patterns()?,
38    };
39
40    let entries = repo
41        .get_logs_by_module_patterns(&patterns, args.lines as i64)
42        .await
43        .context("Failed to query logs from database")?;
44
45    if entries.is_empty() {
46        return Err(anyhow!("No logs found in database for agents"));
47    }
48
49    let logs: Vec<String> = entries
50        .iter()
51        .map(|e| {
52            let message = console::strip_ansi_codes(&e.message);
53            format!(
54                "{} {} [{}] {}",
55                e.timestamp.format("%Y-%m-%d %H:%M:%S"),
56                e.level,
57                e.module,
58                message
59            )
60        })
61        .filter(|line| !line.contains("[profile:"))
62        .collect();
63
64    let agent_label = args.agent.clone().unwrap_or_else(|| "all".to_owned());
65
66    Ok(CommandOutput::card_value(
67        format!("Agent Logs (DB): {}", agent_label),
68        &AgentLogsOutput {
69            agent: Some(agent_label),
70            source: "database".to_owned(),
71            logs,
72            log_files: vec![],
73        },
74    ))
75}
76
77fn build_agent_patterns(agent: &str) -> Vec<String> {
78    vec![
79        format!("%{}%", agent),
80        "%agent%".to_owned(),
81        "%a2a%".to_owned(),
82    ]
83}
84
85fn build_all_agent_patterns() -> Result<Vec<String>> {
86    let services_config = ConfigLoader::load().context("Failed to load services config")?;
87
88    let mut patterns: Vec<String> = services_config
89        .agents
90        .keys()
91        .flat_map(|name| vec![format!("%{}%", name)])
92        .collect();
93
94    patterns.push("%agent%".to_owned());
95    patterns.push("%a2a%".to_owned());
96    patterns.push("%orchestration%".to_owned());
97
98    Ok(patterns)
99}