sqlx_core_guts/
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 + Hash + Eq, P: Debug> {
89    sql: &'q str,
90    unknown_operations: HashSet<O>,
91    results: HashSet<R>,
92    program: Vec<P>,
93    settings: LogSettings,
94}
95
96#[cfg(feature = "sqlite")]
97impl<'q, O: Debug + Hash + Eq, R: Debug + Hash + Eq, P: Debug> QueryPlanLogger<'q, O, R, P> {
98    pub(crate) fn new(sql: &'q str, settings: LogSettings) -> Self {
99        Self {
100            sql,
101            unknown_operations: HashSet::new(),
102            results: HashSet::new(),
103            program: Vec::new(),
104            settings,
105        }
106    }
107
108    pub(crate) fn add_result(&mut self, result: R) {
109        self.results.insert(result);
110    }
111
112    pub(crate) fn add_program(&mut self, program: Vec<P>) {
113        self.program = program;
114    }
115
116    pub(crate) fn add_unknown_operation(&mut self, operation: O) {
117        self.unknown_operations.insert(operation);
118    }
119
120    pub(crate) fn finish(&self) {
121        let lvl = self.settings.statements_level;
122
123        if let Some(lvl) = lvl
124            .to_level()
125            .filter(|lvl| log::log_enabled!(target: "sqlx::explain", *lvl))
126        {
127            let mut summary = parse_query_summary(&self.sql);
128
129            let sql = if summary != self.sql {
130                summary.push_str(" …");
131                format!(
132                    "\n\n{}\n",
133                    sqlformat::format(
134                        &self.sql,
135                        &sqlformat::QueryParams::None,
136                        sqlformat::FormatOptions::default()
137                    )
138                )
139            } else {
140                String::new()
141            };
142
143            log::logger().log(
144                &log::Record::builder()
145                    .args(format_args!(
146                        "{}; program:{:?}, unknown_operations:{:?}, results: {:?}{}",
147                        summary, self.program, self.unknown_operations, self.results, sql
148                    ))
149                    .level(lvl)
150                    .module_path_static(Some("sqlx::explain"))
151                    .target("sqlx::explain")
152                    .build(),
153            );
154        }
155    }
156}
157
158#[cfg(feature = "sqlite")]
159impl<'q, O: Debug + Hash + Eq, R: Debug + Hash + Eq, P: Debug> Drop
160    for QueryPlanLogger<'q, O, R, P>
161{
162    fn drop(&mut self) {
163        self.finish();
164    }
165}
166
167fn parse_query_summary(sql: &str) -> String {
168    // For now, just take the first 4 words
169    sql.split_whitespace()
170        .take(4)
171        .collect::<Vec<&str>>()
172        .join(" ")
173}