systemprompt_cli/commands/infrastructure/logs/
mod.rs1mod audit;
14mod cleanup;
15pub(super) mod delete;
16pub mod duration;
17mod export;
18pub mod request;
19mod search;
20pub mod shared;
21mod show;
22mod stream;
23mod summary;
24pub mod tools;
25pub mod trace;
26pub mod types;
27mod view;
28
29pub use audit::{AuditOutput, AuditToolCall, build_audit, not_found_output as audit_not_found};
30pub use shared::{
31 cost_microdollars_to_dollars, display_log_row, format_optional_duration_ms, format_timestamp,
32};
33pub use summary::{LogsSummaryOutput, build_logs_summary};
34pub use types::{MessageRow, ToolCallRow};
35
36use anyhow::{Result, bail};
37use clap::Subcommand;
38use schemars::JsonSchema;
39use serde::{Deserialize, Serialize};
40use systemprompt_identifiers::{LogId, TraceId};
41
42use crate::context::CommandContext;
43use crate::shared::render_result;
44
45#[derive(Debug, Subcommand)]
46pub enum LogsCommands {
47 #[command(
48 about = "View log entries",
49 after_help = "EXAMPLES:\n systemprompt infra logs view --tail 20\n systemprompt infra \
50 logs view --level error\n systemprompt infra logs view --since 1h"
51 )]
52 View(view::ViewArgs),
53
54 #[command(
55 about = "Search logs by pattern",
56 after_help = "EXAMPLES:\n systemprompt infra logs search \"error\"\n systemprompt infra \
57 logs search \"timeout\" --level error --since 1h"
58 )]
59 Search(search::SearchArgs),
60
61 #[command(
62 about = "Stream logs in real-time (like tail -f)",
63 visible_alias = "follow",
64 after_help = "EXAMPLES:\n systemprompt infra logs stream\n systemprompt infra logs \
65 stream --level error --module agent\n systemprompt infra logs follow"
66 )]
67 Stream(stream::StreamArgs),
68
69 #[command(
70 about = "Export logs to file",
71 after_help = "EXAMPLES:\n systemprompt infra logs export --format json --since 24h\n \
72 systemprompt infra logs export --format csv -o logs.csv"
73 )]
74 Export(export::ExportArgs),
75
76 #[command(about = "Clean up old log entries")]
77 Cleanup(cleanup::CleanupArgs),
78
79 #[command(about = "Delete all log entries")]
80 Delete(delete::DeleteArgs),
81
82 #[command(
83 about = "Show logs summary statistics",
84 after_help = "EXAMPLES:\n systemprompt infra logs summary\n systemprompt infra logs \
85 summary --since 24h"
86 )]
87 Summary(summary::SummaryArgs),
88
89 #[command(
90 about = "Show details of a log entry or all logs for a trace",
91 after_help = "EXAMPLES:\n systemprompt infra logs show log_abc123\n systemprompt infra \
92 logs show trace_def456"
93 )]
94 Show(show::ShowArgs),
95
96 #[command(subcommand, about = "Debug execution traces")]
97 Trace(trace::TraceCommands),
98
99 #[command(subcommand, about = "Inspect AI requests")]
100 Request(request::RequestCommands),
101
102 #[command(subcommand, about = "List and search MCP tool executions")]
103 Tools(tools::ToolsCommands),
104
105 #[command(
106 about = "Full chain reconstruction by request, task, or trace id (identity, policy, prompt/response, tool calls, cost)",
107 after_help = "EXAMPLES:\n systemprompt infra logs audit abc123\n systemprompt infra \
108 logs audit task-xyz"
109 )]
110 Audit(audit::AuditArgs),
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
114pub struct LogEntryRow {
115 pub id: LogId,
116 pub trace_id: TraceId,
117 pub timestamp: String,
118 pub level: String,
119 pub module: String,
120 pub message: String,
121 #[serde(skip_serializing_if = "Option::is_none")]
122 pub metadata: Option<serde_json::Value>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
126pub struct LogFilters {
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub level: Option<String>,
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub module: Option<String>,
131 #[serde(skip_serializing_if = "Option::is_none")]
132 pub since: Option<String>,
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub pattern: Option<String>,
135 pub tail: i64,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
139pub struct LogViewOutput {
140 pub logs: Vec<LogEntryRow>,
141 pub total: u64,
142 pub filters: LogFilters,
143}
144
145#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
146pub struct LogDeleteOutput {
147 pub deleted_count: u64,
148 pub vacuum_performed: bool,
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
152pub struct LogCleanupOutput {
153 pub deleted_count: u64,
154 pub dry_run: bool,
155 pub cutoff_date: String,
156 pub vacuum_performed: bool,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
160pub struct LogExportOutput {
161 pub exported_count: u64,
162 pub format: String,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub file_path: Option<String>,
165}
166
167pub async fn execute(command: LogsCommands, ctx: &CommandContext) -> Result<()> {
168 if ctx.is_database_scoped()
169 && matches!(
170 command,
171 LogsCommands::Stream(_) | LogsCommands::Cleanup(_) | LogsCommands::Delete(_)
172 )
173 {
174 bail!("This logs command requires full profile context");
175 }
176
177 match command {
178 LogsCommands::View(args) => {
179 let result = view::execute(args, ctx).await?;
180 render_result(&result, &ctx.cli);
181 Ok(())
182 },
183 LogsCommands::Search(args) => {
184 let result = search::execute(args, ctx).await?;
185 render_result(&result, &ctx.cli);
186 Ok(())
187 },
188 LogsCommands::Stream(args) => stream::execute(args, ctx).await,
189 LogsCommands::Export(args) => {
190 let result = export::execute(args, ctx).await?;
191 render_result(&result, &ctx.cli);
192 Ok(())
193 },
194 LogsCommands::Cleanup(args) => cleanup::execute(args, ctx).await,
195 LogsCommands::Delete(args) => delete::execute(args, ctx).await,
196 LogsCommands::Summary(args) => summary::execute(args, ctx).await,
197 LogsCommands::Show(args) => show::execute(args, ctx).await,
198 LogsCommands::Trace(cmd) => trace::execute(cmd, ctx).await,
199 LogsCommands::Request(cmd) => request::execute(cmd, ctx).await,
200 LogsCommands::Tools(cmd) => tools::execute(cmd, ctx).await,
201 LogsCommands::Audit(args) => audit::execute(args, ctx).await,
202 }
203}