sqlx_core/
logger.rs

1use crate::connection::LogSettings;
2#[cfg(feature = "sqlite")]
3use std::collections::HashSet;
4#[cfg(feature = "sqlite")]
5use std::fmt::Debug;
6#[cfg(feature = "sqlite")]
7use std::hash::Hash;
8use std::time::Instant;
9
10pub(crate) struct QueryLogger<'q> {
11    sql: &'q str,
12    rows_returned: u64,
13    rows_affected: u64,
14    start: Instant,
15    settings: LogSettings,
16}
17
18impl<'q> QueryLogger<'q> {
19    pub(crate) fn new(sql: &'q str, settings: LogSettings) -> Self {
20        Self {
21            sql,
22            rows_returned: 0,
23            rows_affected: 0,
24            start: Instant::now(),
25            settings,
26        }
27    }
28
29    pub(crate) fn increment_rows_returned(&mut self) {
30        self.rows_returned += 1;
31    }
32
33    pub(crate) fn increase_rows_affected(&mut self, n: u64) {
34        self.rows_affected += n;
35    }
36
37    pub(crate) fn finish(&self) {
38        let elapsed = self.start.elapsed();
39
40        let lvl = if elapsed >= self.settings.slow_statements_duration {
41            self.settings.slow_statements_level
42        } else {
43            self.settings.statements_level
44        };
45
46        if let Some(lvl) = lvl
47            .to_level()
48            .filter(|lvl| log::log_enabled!(target: "sqlx::query", *lvl))
49        {
50            let mut summary = parse_query_summary(&self.sql);
51
52            let sql = if summary != self.sql {
53                summary.push_str(" …");
54                format!(
55                    "\n\n{}\n",
56                    sqlformat::format(
57                        &self.sql,
58                        &sqlformat::QueryParams::None,
59                        sqlformat::FormatOptions::default()
60                    )
61                )
62            } else {
63                String::new()
64            };
65
66            log::logger().log(
67                &log::Record::builder()
68                    .args(format_args!(
69                        "{}; rows affected: {}, rows returned: {}, elapsed: {:.3?}{}",
70                        summary, self.rows_affected, self.rows_returned, elapsed, sql
71                    ))
72                    .level(lvl)
73                    .module_path_static(Some("sqlx::query"))
74                    .target("sqlx::query")
75                    .build(),
76            );
77        }
78    }
79}
80
81impl<'q> Drop for QueryLogger<'q> {
82    fn drop(&mut self) {
83        self.finish();
84    }
85}
86
87#[cfg(feature = "sqlite")]
88pub(crate) struct QueryPlanLogger<'q, O: Debug + Hash + Eq, R: Debug, P: Debug> {
89    sql: &'q str,
90    unknown_operations: HashSet<O>,
91    results: Vec<R>,
92    program: &'q [P],
93    settings: LogSettings,
94}
95
96#[cfg(feature = "sqlite")]
97impl<'q, O: Debug + Hash + Eq, R: Debug, P: Debug> QueryPlanLogger<'q, O, R, P> {
98    pub(crate) fn new(sql: &'q str, program: &'q [P], settings: LogSettings) -> Self {
99        Self {
100            sql,
101            unknown_operations: HashSet::new(),
102            results: Vec::new(),
103            program,
104            settings,
105        }
106    }
107
108    pub(crate) fn log_enabled(&self) -> bool {
109        if let Some(_lvl) = self
110            .settings
111            .statements_level
112            .to_level()
113            .filter(|lvl| log::log_enabled!(target: "sqlx::explain", *lvl))
114        {
115            return true;
116        } else {
117            return false;
118        }
119    }
120
121    pub(crate) fn add_result(&mut self, result: R) {
122        self.results.push(result);
123    }
124
125    pub(crate) fn add_unknown_operation(&mut self, operation: O) {
126        self.unknown_operations.insert(operation);
127    }
128
129    pub(crate) fn finish(&self) {
130        let lvl = self.settings.statements_level;
131
132        if let Some(lvl) = lvl
133            .to_level()
134            .filter(|lvl| log::log_enabled!(target: "sqlx::explain", *lvl))
135        {
136            let mut summary = parse_query_summary(&self.sql);
137
138            let sql = if summary != self.sql {
139                summary.push_str(" …");
140                format!(
141                    "\n\n{}\n",
142                    sqlformat::format(
143                        &self.sql,
144                        &sqlformat::QueryParams::None,
145                        sqlformat::FormatOptions::default()
146                    )
147                )
148            } else {
149                String::new()
150            };
151
152            log::logger().log(
153                &log::Record::builder()
154                    .args(format_args!(
155                        "{}; program:{:?}, unknown_operations:{:?}, results: {:?}{}",
156                        summary, self.program, self.unknown_operations, self.results, sql
157                    ))
158                    .level(lvl)
159                    .module_path_static(Some("sqlx::explain"))
160                    .target("sqlx::explain")
161                    .build(),
162            );
163        }
164    }
165}
166
167#[cfg(feature = "sqlite")]
168impl<'q, O: Debug + Hash + Eq, R: Debug, P: Debug> Drop for QueryPlanLogger<'q, O, R, P> {
169    fn drop(&mut self) {
170        self.finish();
171    }
172}
173
174fn parse_query_summary(sql: &str) -> String {
175    // For now, just take the first 4 words
176    sql.split_whitespace()
177        .take(4)
178        .collect::<Vec<&str>>()
179        .join(" ")
180}