6 changed files with 200 additions and 24 deletions
@ -0,0 +1,148 @@
|
||||
/* |
||||
* from https://github.com/shssoichiro/diesel-logger
|
||||
* change Connection to &mut Connection |
||||
*/ |
||||
|
||||
use std::ops::Deref; |
||||
use std::time::{Duration, Instant}; |
||||
|
||||
use diesel::backend::{Backend, UsesAnsiSavepointSyntax}; |
||||
use diesel::connection::{AnsiTransactionManager, SimpleConnection}; |
||||
use diesel::debug_query; |
||||
use diesel::deserialize::QueryableByName; |
||||
use diesel::prelude::*; |
||||
use diesel::query_builder::{AsQuery, QueryFragment, QueryId}; |
||||
use diesel::sql_types::HasSqlType; |
||||
|
||||
/// Wraps a diesel `Connection` to time and log each query using
|
||||
/// the configured logger for the `log` crate.
|
||||
///
|
||||
/// Currently, this produces a `debug` log on every query,
|
||||
/// an `info` on queries that take longer than 1 second,
|
||||
/// and a `warn`ing on queries that take longer than 5 seconds.
|
||||
/// These thresholds will be configurable in a future version.
|
||||
pub struct LoggingConnection<'r, C: Connection>(&'r mut C); |
||||
|
||||
impl<'r, C: Connection> LoggingConnection<'r, C> { |
||||
pub fn new(conn: &'r mut C) -> Self { |
||||
LoggingConnection(conn) |
||||
} |
||||
} |
||||
|
||||
impl<'r, C: Connection> Deref for LoggingConnection<'r, C> { |
||||
type Target = C; |
||||
fn deref(&self) -> &Self::Target { |
||||
self.0 |
||||
} |
||||
} |
||||
|
||||
impl<'r, C> SimpleConnection for LoggingConnection<'r, C> |
||||
where |
||||
C: Connection + Send + 'static, |
||||
{ |
||||
fn batch_execute(&self, query: &str) -> QueryResult<()> { |
||||
let start_time = Instant::now(); |
||||
let result = self.0.batch_execute(query); |
||||
let duration = start_time.elapsed(); |
||||
log_query(query, duration); |
||||
result |
||||
} |
||||
} |
||||
|
||||
impl<C: Connection> Connection for LoggingConnection<'_, C> |
||||
where |
||||
C: Connection<TransactionManager = AnsiTransactionManager> + Send + 'static, |
||||
C::Backend: UsesAnsiSavepointSyntax, |
||||
<C::Backend as Backend>::QueryBuilder: Default, |
||||
{ |
||||
type Backend = C::Backend; |
||||
type TransactionManager = C::TransactionManager; |
||||
|
||||
fn establish(_: &str) -> ConnectionResult<Self> { |
||||
Err(ConnectionError::__Nonexhaustive) |
||||
//Ok(LoggingConnection(C::establish(database_url)?))
|
||||
} |
||||
|
||||
fn execute(&self, query: &str) -> QueryResult<usize> { |
||||
let start_time = Instant::now(); |
||||
let result = self.0.execute(query); |
||||
let duration = start_time.elapsed(); |
||||
log_query(query, duration); |
||||
result |
||||
} |
||||
|
||||
fn query_by_index<T, U>(&self, source: T) -> QueryResult<Vec<U>> |
||||
where |
||||
T: AsQuery, |
||||
T::Query: QueryFragment<Self::Backend> + QueryId, |
||||
Self::Backend: HasSqlType<T::SqlType>, |
||||
U: Queryable<T::SqlType, Self::Backend>, |
||||
{ |
||||
let query = source.as_query(); |
||||
let debug_query = debug_query::<Self::Backend, _>(&query).to_string(); |
||||
let start_time = Instant::now(); |
||||
let result = self.0.query_by_index(query); |
||||
let duration = start_time.elapsed(); |
||||
log_query(&debug_query, duration); |
||||
result |
||||
} |
||||
|
||||
fn query_by_name<T, U>(&self, source: &T) -> QueryResult<Vec<U>> |
||||
where |
||||
T: QueryFragment<Self::Backend> + QueryId, |
||||
U: QueryableByName<Self::Backend>, |
||||
{ |
||||
let debug_query = debug_query::<Self::Backend, _>(&source).to_string(); |
||||
let start_time = Instant::now(); |
||||
let result = self.0.query_by_name(source); |
||||
let duration = start_time.elapsed(); |
||||
log_query(&debug_query, duration); |
||||
result |
||||
} |
||||
|
||||
fn execute_returning_count<T>(&self, source: &T) -> QueryResult<usize> |
||||
where |
||||
T: QueryFragment<Self::Backend> + QueryId, |
||||
{ |
||||
let debug_query = debug_query::<Self::Backend, _>(&source).to_string(); |
||||
let start_time = Instant::now(); |
||||
let result = self.0.execute_returning_count(source); |
||||
let duration = start_time.elapsed(); |
||||
log_query(&debug_query, duration); |
||||
result |
||||
} |
||||
|
||||
fn transaction_manager(&self) -> &Self::TransactionManager { |
||||
self.0.transaction_manager() |
||||
} |
||||
} |
||||
|
||||
fn log_query(query: &str, duration: Duration) { |
||||
if duration.as_secs() >= 5 { |
||||
warn!( |
||||
"Slow query ran in {:.2} seconds: {}", |
||||
duration_to_secs(duration), |
||||
query |
||||
); |
||||
} else if duration.as_secs() >= 1 { |
||||
info!( |
||||
"Slow query ran in {:.2} seconds: {}", |
||||
duration_to_secs(duration), |
||||
query |
||||
); |
||||
} else { |
||||
debug!("Query ran in {:.1} ms: {}", duration_to_ms(duration), query); |
||||
} |
||||
} |
||||
|
||||
const NANOS_PER_MILLI: u32 = 1_000_000; |
||||
const MILLIS_PER_SEC: u32 = 1_000; |
||||
|
||||
fn duration_to_secs(duration: Duration) -> f32 { |
||||
duration_to_ms(duration) / MILLIS_PER_SEC as f32 |
||||
} |
||||
|
||||
fn duration_to_ms(duration: Duration) -> f32 { |
||||
(duration.as_secs() as u32 * 1000) as f32 |
||||
+ (duration.subsec_nanos() as f32 / NANOS_PER_MILLI as f32) |
||||
} |
Loading…
Reference in new issue