mcpr_integrations/store/query/
logs.rs1use rusqlite::{Row, params};
4use serde::Serialize;
5
6use super::QueryEngine;
7
8pub struct LogsParams {
10 pub proxy: Option<String>,
12 pub since_ts: i64,
14 pub limit: i64,
16 pub tool: Option<String>,
18 pub method: Option<String>,
20 pub session: Option<String>,
22 pub status: Option<String>,
24 pub error_code: Option<String>,
26}
27
28#[derive(Debug, Clone, Serialize)]
30pub struct LogRow {
31 pub request_id: String,
32 pub ts: i64,
33 pub method: String,
34 pub tool: Option<String>,
35 pub latency_us: i64,
36 pub status: String,
37 pub error_code: Option<String>,
38 pub error_msg: Option<String>,
39 pub session_id: Option<String>,
40 pub bytes_in: Option<i64>,
41 pub bytes_out: Option<i64>,
42}
43
44pub(crate) fn map_log_row(row: &Row<'_>) -> rusqlite::Result<LogRow> {
47 Ok(LogRow {
48 request_id: row.get(0)?,
49 ts: row.get(1)?,
50 method: row.get(2)?,
51 tool: row.get(3)?,
52 latency_us: row.get(4)?,
53 status: row.get(5)?,
54 error_code: row.get(6)?,
55 error_msg: row.get(7)?,
56 session_id: row.get(8)?,
57 bytes_in: row.get(9)?,
58 bytes_out: row.get(10)?,
59 })
60}
61
62pub(crate) const LOG_COLUMNS: &str = "request_id, ts, method, tool, latency_us, status, error_code, error_msg, session_id, bytes_in, bytes_out";
64
65impl QueryEngine {
66 pub fn logs(&self, params: &LogsParams) -> Result<Vec<LogRow>, rusqlite::Error> {
68 let sql = format!(
69 "SELECT {LOG_COLUMNS}
70 FROM requests
71 WHERE (?1 IS NULL OR proxy = ?1)
72 AND (?2 IS NULL OR tool = ?2)
73 AND (?3 IS NULL OR status = ?3)
74 AND (?4 IS NULL OR method = ?4)
75 AND (?5 IS NULL OR session_id LIKE ?5 || '%')
76 AND (?6 IS NULL OR error_code = ?6)
77 AND ts >= ?7
78 ORDER BY ts DESC
79 LIMIT ?8"
80 );
81
82 let mut stmt = self.conn().prepare(&sql)?;
83 let rows = stmt.query_map(
84 params![
85 params.proxy,
86 params.tool,
87 params.status,
88 params.method,
89 params.session,
90 params.error_code,
91 params.since_ts,
92 params.limit,
93 ],
94 map_log_row,
95 )?;
96
97 rows.collect()
98 }
99
100 pub fn logs_since(
104 &self,
105 params: &LogsParams,
106 after_ts: i64,
107 ) -> Result<Vec<LogRow>, rusqlite::Error> {
108 let sql = format!(
109 "SELECT {LOG_COLUMNS}
110 FROM requests
111 WHERE (?1 IS NULL OR proxy = ?1)
112 AND (?2 IS NULL OR tool = ?2)
113 AND (?3 IS NULL OR status = ?3)
114 AND (?4 IS NULL OR method = ?4)
115 AND (?5 IS NULL OR session_id LIKE ?5 || '%')
116 AND (?6 IS NULL OR error_code = ?6)
117 AND ts > ?7
118 ORDER BY ts ASC"
119 );
120
121 let mut stmt = self.conn().prepare(&sql)?;
122 let rows = stmt.query_map(
123 params![
124 params.proxy,
125 params.tool,
126 params.status,
127 params.method,
128 params.session,
129 params.error_code,
130 after_ts
131 ],
132 map_log_row,
133 )?;
134
135 rows.collect()
136 }
137}