Skip to content

Commit 16229fd

Browse files
shssoichiromehcode
authored andcommitted
Add basic query logging
Prints each query performed at DEBUG level, along with a timing of how long the query took to execute. Slow queries will be printed at WARN level. Currently the slow query threshold is hardcoded to 1 second.
1 parent 7df6d4d commit 16229fd

File tree

8 files changed

+95
-29
lines changed

8 files changed

+95
-29
lines changed

sqlx-core/src/executor.rs

+10
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ where
7979
/// prepare the query. Returning `Some(Default::default())` is an empty arguments object that
8080
/// will be prepared (and cached) before execution.
8181
fn into_parts(self) -> (&'q str, Option<DB::Arguments>);
82+
83+
/// Returns the query string, without any parameters replaced.
84+
#[doc(hidden)]
85+
fn query_string(&self) -> &'q str;
8286
}
8387

8488
impl<'q, DB> Execute<'q, DB> for &'q str
@@ -89,6 +93,12 @@ where
8993
fn into_parts(self) -> (&'q str, Option<DB::Arguments>) {
9094
(self, None)
9195
}
96+
97+
#[inline]
98+
#[doc(hidden)]
99+
fn query_string(&self) -> &'q str {
100+
self
101+
}
92102
}
93103

94104
impl<T> Executor for &'_ mut T

sqlx-core/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ pub mod types;
5656
#[macro_use]
5757
pub mod row;
5858

59+
#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
60+
#[macro_use]
61+
mod logging;
62+
5963
#[cfg(feature = "mysql")]
6064
#[cfg_attr(docsrs, doc(cfg(feature = "mysql")))]
6165
pub mod mysql;

sqlx-core/src/logging.rs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/// Logs the query and execution time of a statement as it runs.
2+
macro_rules! log_execution {
3+
( $query:expr, $block:expr ) => {{
4+
// TODO: Log bound parameters
5+
let query_string = $query.query_string();
6+
let timer = std::time::Instant::now();
7+
let result = $block;
8+
let elapsed = timer.elapsed();
9+
if elapsed >= std::time::Duration::from_secs(1) {
10+
log::warn!(
11+
"{} ..., elapsed: {:.3?}\n\n {}\n",
12+
crate::logging::parse_query_summary(query_string),
13+
elapsed,
14+
query_string
15+
);
16+
} else {
17+
log::debug!(
18+
"{} ..., elapsed: {:.3?}\n\n {}\n",
19+
crate::logging::parse_query_summary(query_string),
20+
elapsed,
21+
query_string
22+
);
23+
}
24+
result
25+
}};
26+
}
27+
28+
pub(crate) fn parse_query_summary(query: &str) -> String {
29+
// For now, just take the first 3 words
30+
query
31+
.split_whitespace()
32+
.take(3)
33+
.collect::<Vec<&str>>()
34+
.join(" ")
35+
}

sqlx-core/src/mysql/executor.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -182,19 +182,21 @@ impl Executor for super::MySqlConnection {
182182
where
183183
E: Execute<'q, Self::Database>,
184184
{
185-
Box::pin(async move {
186-
let (query, arguments) = query.into_parts();
185+
log_execution!(query, {
186+
Box::pin(async move {
187+
let (query, arguments) = query.into_parts();
187188

188-
self.run(query, arguments).await?;
189-
self.affected_rows().await
189+
self.run(query, arguments).await?;
190+
self.affected_rows().await
191+
})
190192
})
191193
}
192194

193195
fn fetch<'q, E>(&mut self, query: E) -> MySqlCursor<'_, 'q>
194196
where
195197
E: Execute<'q, Self::Database>,
196198
{
197-
MySqlCursor::from_connection(self, query)
199+
log_execution!(query, { MySqlCursor::from_connection(self, query) })
198200
}
199201

200202
#[doc(hidden)]
@@ -216,6 +218,6 @@ impl<'c> RefExecutor<'c> for &'c mut super::MySqlConnection {
216218
where
217219
E: Execute<'q, Self::Database>,
218220
{
219-
MySqlCursor::from_connection(self, query)
221+
log_execution!(query, { MySqlCursor::from_connection(self, query) })
220222
}
221223
}

sqlx-core/src/postgres/executor.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -488,19 +488,21 @@ impl Executor for super::PgConnection {
488488
where
489489
E: Execute<'q, Self::Database>,
490490
{
491-
Box::pin(async move {
492-
let (query, arguments) = query.into_parts();
491+
log_execution!(query, {
492+
Box::pin(async move {
493+
let (query, arguments) = query.into_parts();
493494

494-
self.run(query, arguments).await?;
495-
self.affected_rows().await
495+
self.run(query, arguments).await?;
496+
self.affected_rows().await
497+
})
496498
})
497499
}
498500

499501
fn fetch<'q, E>(&mut self, query: E) -> PgCursor<'_, 'q>
500502
where
501503
E: Execute<'q, Self::Database>,
502504
{
503-
PgCursor::from_connection(self, query)
505+
log_execution!(query, { PgCursor::from_connection(self, query) })
504506
}
505507

506508
#[doc(hidden)]
@@ -522,6 +524,6 @@ impl<'c> RefExecutor<'c> for &'c mut super::PgConnection {
522524
where
523525
E: Execute<'q, Self::Database>,
524526
{
525-
PgCursor::from_connection(self, query)
527+
log_execution!(query, { PgCursor::from_connection(self, query) })
526528
}
527529
}

sqlx-core/src/query.rs

+5
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ where
5151
fn into_parts(self) -> (&'q str, Option<DB::Arguments>) {
5252
(self.query, Some(self.arguments))
5353
}
54+
55+
#[doc(hidden)]
56+
fn query_string(&self) -> &'q str {
57+
self.query
58+
}
5459
}
5560

5661
impl<'q, DB> Query<'q, DB>

sqlx-core/src/query_as.rs

+6
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ where
4444
fn into_parts(self) -> (&'q str, Option<<DB as Database>::Arguments>) {
4545
(self.query, Some(self.arguments))
4646
}
47+
48+
#[inline]
49+
#[doc(hidden)]
50+
fn query_string(&self) -> &'q str {
51+
self.query
52+
}
4753
}
4854

4955
/// Construct a raw SQL query that is mapped to a concrete type

sqlx-core/src/sqlite/executor.rs

+19-17
Original file line numberDiff line numberDiff line change
@@ -91,35 +91,37 @@ impl Executor for SqliteConnection {
9191
where
9292
E: Execute<'q, Self::Database>,
9393
{
94-
let (mut query, mut arguments) = query.into_parts();
94+
log_execution!(query, {
95+
let (mut query, mut arguments) = query.into_parts();
9596

96-
Box::pin(async move {
97-
loop {
98-
let key = self.prepare(&mut query, arguments.is_some())?;
99-
let statement = self.statement_mut(key);
97+
Box::pin(async move {
98+
loop {
99+
let key = self.prepare(&mut query, arguments.is_some())?;
100+
let statement = self.statement_mut(key);
100101

101-
if let Some(arguments) = &mut arguments {
102-
statement.bind(arguments)?;
103-
}
102+
if let Some(arguments) = &mut arguments {
103+
statement.bind(arguments)?;
104+
}
104105

105-
while let Step::Row = statement.step().await? {
106-
// We only care about the rows modified; ignore
107-
}
106+
while let Step::Row = statement.step().await? {
107+
// We only care about the rows modified; ignore
108+
}
108109

109-
if query.is_empty() {
110-
break;
110+
if query.is_empty() {
111+
break;
112+
}
111113
}
112-
}
113114

114-
Ok(self.changes())
115+
Ok(self.changes())
116+
})
115117
})
116118
}
117119

118120
fn fetch<'q, E>(&mut self, query: E) -> SqliteCursor<'_, 'q>
119121
where
120122
E: Execute<'q, Self::Database>,
121123
{
122-
SqliteCursor::from_connection(self, query)
124+
log_execution!(query, { SqliteCursor::from_connection(self, query) })
123125
}
124126

125127
#[doc(hidden)]
@@ -186,6 +188,6 @@ impl<'e> RefExecutor<'e> for &'e mut SqliteConnection {
186188
where
187189
E: Execute<'q, Self::Database>,
188190
{
189-
SqliteCursor::from_connection(self, query)
191+
log_execution!(query, { SqliteCursor::from_connection(self, query) })
190192
}
191193
}

0 commit comments

Comments
 (0)