log sql query
This commit is contained in:
@@ -14,6 +14,9 @@ chrono = { version="0.*", features =["serde"] }
|
|||||||
rand = "0.*"
|
rand = "0.*"
|
||||||
dotenv = "0.*"
|
dotenv = "0.*"
|
||||||
sha2 = "0.*"
|
sha2 = "0.*"
|
||||||
|
diesel_logger = "0.1.1"
|
||||||
|
log = "0.4.16"
|
||||||
|
env_logger = "0.9.0"
|
||||||
|
|
||||||
[dependencies.rocket_sync_db_pools]
|
[dependencies.rocket_sync_db_pools]
|
||||||
version = "0.1.0-rc.1"
|
version = "0.1.0-rc.1"
|
||||||
|
|||||||
148
src/libs/diesel_logger.rs
Normal file
148
src/libs/diesel_logger.rs
Normal file
@@ -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)
|
||||||
|
}
|
||||||
1
src/libs/mod.rs
Normal file
1
src/libs/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod diesel_logger;
|
||||||
13
src/main.rs
13
src/main.rs
@@ -4,21 +4,26 @@ extern crate rocket;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate diesel;
|
extern crate diesel;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log;
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod db_conn;
|
mod db_conn;
|
||||||
mod rds_conn;
|
mod libs;
|
||||||
mod models;
|
mod models;
|
||||||
mod rds_models;
|
|
||||||
mod random_hasher;
|
mod random_hasher;
|
||||||
|
mod rds_conn;
|
||||||
|
mod rds_models;
|
||||||
mod schema;
|
mod schema;
|
||||||
|
|
||||||
use db_conn::Db;
|
use db_conn::Db;
|
||||||
use rds_conn::init_rds_client;
|
|
||||||
use random_hasher::RandomHasher;
|
use random_hasher::RandomHasher;
|
||||||
|
use rds_conn::init_rds_client;
|
||||||
|
|
||||||
#[rocket::main]
|
#[rocket::main]
|
||||||
async fn main() -> Result<(), rocket::Error> {
|
async fn main() -> Result<(), rocket::Error> {
|
||||||
load_env();
|
load_env();
|
||||||
|
env_logger::init();
|
||||||
rocket::build()
|
rocket::build()
|
||||||
.mount(
|
.mount(
|
||||||
"/_api/v1",
|
"/_api/v1",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#![allow(clippy::all)]
|
#![allow(clippy::all)]
|
||||||
|
|
||||||
use crate::db_conn::Db;
|
use crate::db_conn::Db;
|
||||||
|
use crate::libs::diesel_logger::LoggingConnection;
|
||||||
use crate::rds_conn::RdsConn;
|
use crate::rds_conn::RdsConn;
|
||||||
use crate::rds_models::PostCache;
|
use crate::rds_models::PostCache;
|
||||||
use crate::schema::*;
|
use crate::schema::*;
|
||||||
@@ -17,7 +18,8 @@ macro_rules! get {
|
|||||||
($table:ident) => {
|
($table:ident) => {
|
||||||
pub async fn get(db: &Db, id: i32) -> QueryResult<Self> {
|
pub async fn get(db: &Db, id: i32) -> QueryResult<Self> {
|
||||||
let pid = id;
|
let pid = id;
|
||||||
db.run(move |c| $table::table.find(pid).first(c)).await
|
db.run(move |c| $table::table.find(pid).first(with_log!((c))))
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -31,7 +33,7 @@ macro_rules! get_multi {
|
|||||||
.filter($table::id.eq_any(ids))
|
.filter($table::id.eq_any(ids))
|
||||||
.filter($table::is_deleted.eq(false))
|
.filter($table::is_deleted.eq(false))
|
||||||
.order($table::id.desc())
|
.order($table::id.desc())
|
||||||
.load(c)
|
.load(with_log!(c))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -45,7 +47,7 @@ macro_rules! set_deleted {
|
|||||||
db.run(move |c| {
|
db.run(move |c| {
|
||||||
diesel::update($table::table.find(pid))
|
diesel::update($table::table.find(pid))
|
||||||
.set($table::is_deleted.eq(true))
|
.set($table::is_deleted.eq(true))
|
||||||
.execute(c)
|
.execute(with_log!(c))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -60,6 +62,12 @@ macro_rules! base_query {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! with_log {
|
||||||
|
($c: expr) => {
|
||||||
|
&LoggingConnection::new($c)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Queryable, Insertable)]
|
#[derive(Queryable, Insertable)]
|
||||||
pub struct Comment {
|
pub struct Comment {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
@@ -152,7 +160,7 @@ impl Post {
|
|||||||
_ => panic!("Wrong order mode!"),
|
_ => panic!("Wrong order mode!"),
|
||||||
};
|
};
|
||||||
|
|
||||||
query.offset(start).limit(limit).load(c)
|
query.offset(start).limit(limit).load(with_log!(c))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -197,15 +205,19 @@ impl Post {
|
|||||||
.order(posts::id.desc())
|
.order(posts::id.desc())
|
||||||
.offset(start)
|
.offset(start)
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
.load(c)
|
.load(with_log!(c))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create(db: &Db, new_post: NewPost) -> QueryResult<Self> {
|
pub async fn create(db: &Db, new_post: NewPost) -> QueryResult<Self> {
|
||||||
// TODO: tags
|
// TODO: tags
|
||||||
db.run(move |c| insert_into(posts::table).values(&new_post).get_result(c))
|
db.run(move |c| {
|
||||||
.await
|
insert_into(posts::table)
|
||||||
|
.values(&new_post)
|
||||||
|
.get_result(with_log!(c))
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_cw(&self, db: &Db, new_cw: String) -> QueryResult<usize> {
|
pub async fn update_cw(&self, db: &Db, new_cw: String) -> QueryResult<usize> {
|
||||||
@@ -213,7 +225,7 @@ impl Post {
|
|||||||
db.run(move |c| {
|
db.run(move |c| {
|
||||||
diesel::update(posts::table.find(pid))
|
diesel::update(posts::table.find(pid))
|
||||||
.set(posts::cw.eq(new_cw))
|
.set(posts::cw.eq(new_cw))
|
||||||
.execute(c)
|
.execute(with_log!(c))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -223,7 +235,7 @@ impl Post {
|
|||||||
db.run(move |c| {
|
db.run(move |c| {
|
||||||
diesel::update(posts::table.find(pid))
|
diesel::update(posts::table.find(pid))
|
||||||
.set(posts::n_comments.eq(posts::n_comments + delta))
|
.set(posts::n_comments.eq(posts::n_comments + delta))
|
||||||
.get_result(c)
|
.get_result(with_log!(c))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -233,7 +245,7 @@ impl Post {
|
|||||||
db.run(move |c| {
|
db.run(move |c| {
|
||||||
diesel::update(posts::table.find(pid))
|
diesel::update(posts::table.find(pid))
|
||||||
.set(posts::n_attentions.eq(posts::n_attentions + delta))
|
.set(posts::n_attentions.eq(posts::n_attentions + delta))
|
||||||
.get_result(c)
|
.get_result(with_log!(c))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -243,7 +255,7 @@ impl Post {
|
|||||||
db.run(move |c| {
|
db.run(move |c| {
|
||||||
diesel::update(posts::table.find(pid))
|
diesel::update(posts::table.find(pid))
|
||||||
.set(posts::hot_score.eq(posts::hot_score + delta))
|
.set(posts::hot_score.eq(posts::hot_score + delta))
|
||||||
.get_result(c)
|
.get_result(with_log!(c))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -259,9 +271,13 @@ impl Post {
|
|||||||
impl User {
|
impl User {
|
||||||
pub async fn get_by_token(db: &Db, token: &str) -> Option<Self> {
|
pub async fn get_by_token(db: &Db, token: &str) -> Option<Self> {
|
||||||
let token = token.to_string();
|
let token = token.to_string();
|
||||||
db.run(move |c| users::table.filter(users::token.eq(token)).first(c))
|
db.run(move |c| {
|
||||||
.await
|
users::table
|
||||||
.ok()
|
.filter(users::token.eq(token))
|
||||||
|
.first(with_log!(c))
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +300,7 @@ impl Comment {
|
|||||||
db.run(move |c| {
|
db.run(move |c| {
|
||||||
insert_into(comments::table)
|
insert_into(comments::table)
|
||||||
.values(&new_comment)
|
.values(&new_comment)
|
||||||
.get_result(c)
|
.get_result(with_log!(c))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -295,7 +311,7 @@ impl Comment {
|
|||||||
comments::table
|
comments::table
|
||||||
.filter(comments::post_id.eq(pid))
|
.filter(comments::post_id.eq(pid))
|
||||||
.order(comments::id)
|
.order(comments::id)
|
||||||
.load(c)
|
.load(with_log!(c))
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,23 +58,26 @@ impl PostCache {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
dbg!("set post cache failed", e, p.id);
|
warn!("set post cache failed: {}, {}", e, p.id);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get(&mut self) -> Option<Post> {
|
pub async fn get(&mut self) -> Option<Post> {
|
||||||
let rds_result = self.rconn.get::<&String, String>(&self.key).await;
|
let rds_result = self.rconn.get::<&String, String>(&self.key).await;
|
||||||
if let Ok(s) = rds_result {
|
if let Ok(s) = rds_result {
|
||||||
dbg!("hint post cache", &s);
|
debug!("hint post cache: {}", &s);
|
||||||
self.rconn
|
self.rconn
|
||||||
.expire::<&String, bool>(&self.key, INSTANCE_EXPIRE_TIME)
|
.expire::<&String, bool>(&self.key, INSTANCE_EXPIRE_TIME)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
dbg!("get post cache, set new expire failed", e, &self.key, &s);
|
warn!(
|
||||||
|
"get post cache, set new expire failed: {}, {}, {} ",
|
||||||
|
e, &self.key, &s
|
||||||
|
);
|
||||||
false
|
false
|
||||||
});
|
});
|
||||||
serde_json::from_str(&s).unwrap_or_else(|e| {
|
serde_json::from_str(&s).unwrap_or_else(|e| {
|
||||||
dbg!("get post cache failed", e, s);
|
warn!("get post cache, decode failed {}, {}", e, s);
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user