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 resource_uri: Option<String>,
36 pub prompt_name: Option<String>,
37 pub latency_us: i64,
38 pub status: String,
39 pub error_code: Option<String>,
40 pub error_msg: Option<String>,
41 pub session_id: Option<String>,
42 pub bytes_in: Option<i64>,
43 pub bytes_out: Option<i64>,
44}
45
46pub(crate) fn map_log_row(row: &Row<'_>) -> rusqlite::Result<LogRow> {
49 Ok(LogRow {
50 request_id: row.get(0)?,
51 ts: row.get(1)?,
52 method: row.get(2)?,
53 tool: row.get(3)?,
54 resource_uri: row.get(4)?,
55 prompt_name: row.get(5)?,
56 latency_us: row.get(6)?,
57 status: row.get(7)?,
58 error_code: row.get(8)?,
59 error_msg: row.get(9)?,
60 session_id: row.get(10)?,
61 bytes_in: row.get(11)?,
62 bytes_out: row.get(12)?,
63 })
64}
65
66pub(crate) const LOG_COLUMNS: &str = "request_id, ts, method, tool, resource_uri, prompt_name, latency_us, status, error_code, error_msg, session_id, bytes_in, bytes_out";
68
69impl QueryEngine {
70 pub fn logs(&self, params: &LogsParams) -> Result<Vec<LogRow>, rusqlite::Error> {
72 let sql = format!(
73 "SELECT {LOG_COLUMNS}
74 FROM requests
75 WHERE (?1 IS NULL OR proxy = ?1)
76 AND (?2 IS NULL OR tool = ?2)
77 AND (?3 IS NULL OR status = ?3)
78 AND (?4 IS NULL OR method = ?4)
79 AND (?5 IS NULL OR session_id LIKE ?5 || '%')
80 AND (?6 IS NULL OR error_code = ?6)
81 AND ts >= ?7
82 ORDER BY ts DESC
83 LIMIT ?8"
84 );
85
86 let mut stmt = self.conn().prepare(&sql)?;
87 let rows = stmt.query_map(
88 params![
89 params.proxy,
90 params.tool,
91 params.status,
92 params.method,
93 params.session,
94 params.error_code,
95 params.since_ts,
96 params.limit,
97 ],
98 map_log_row,
99 )?;
100
101 rows.collect()
102 }
103
104 pub fn logs_since(
108 &self,
109 params: &LogsParams,
110 after_ts: i64,
111 ) -> Result<Vec<LogRow>, rusqlite::Error> {
112 let sql = format!(
113 "SELECT {LOG_COLUMNS}
114 FROM requests
115 WHERE (?1 IS NULL OR proxy = ?1)
116 AND (?2 IS NULL OR tool = ?2)
117 AND (?3 IS NULL OR status = ?3)
118 AND (?4 IS NULL OR method = ?4)
119 AND (?5 IS NULL OR session_id LIKE ?5 || '%')
120 AND (?6 IS NULL OR error_code = ?6)
121 AND ts > ?7
122 ORDER BY ts ASC"
123 );
124
125 let mut stmt = self.conn().prepare(&sql)?;
126 let rows = stmt.query_map(
127 params![
128 params.proxy,
129 params.tool,
130 params.status,
131 params.method,
132 params.session,
133 params.error_code,
134 after_ts
135 ],
136 map_log_row,
137 )?;
138
139 rows.collect()
140 }
141}