diff --git a/Cargo.toml b/Cargo.toml index ce797f3..5965d58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,5 +14,7 @@ chrono = { version = "0.4", features = [ "serde" ] } ring = "0.16.20" data-encoding = "2.3.2" futures-util = "0.3" +actix-web-utils = "0.2" +log = { version = "0.4", features = ["serde"] } dev-dtos = { git = "https://backend:Eo1n1TPsyWV7wwo9uFwgUJGKKheMM6paM2mDkVPA4zqkh5dt6Q6XPkbtojzYQudQsM84vSwKmhHHTPjyn535d6NLBmA3meeGj0Gb8if4sceAwvySdmzedg5mN2P5zzQt@gitea.blancoinfante.com/blancoinfante_backend/dev-dtos-rust.git" } \ No newline at end of file diff --git a/src/dao/main_dao.rs b/src/dao/main_dao.rs index e4c8296..982d710 100644 --- a/src/dao/main_dao.rs +++ b/src/dao/main_dao.rs @@ -1,6 +1,7 @@ -use std::collections::HashMap; +use std::{collections::HashMap, str::FromStr, time::Duration}; -use sqlx::{MySqlPool}; +use log::{LevelFilter, info}; +use sqlx::{MySqlPool, mysql::{MySqlConnectOptions, MySqlPoolOptions}, ConnectOptions}; pub async fn start_database_connection(env_vars: &HashMap) -> Result{ let db_url = match env_vars.get("DATABASE_URL") { @@ -8,11 +9,14 @@ pub async fn start_database_connection(env_vars: &HashMap) -> Re None => panic!("DATABASE_URL env var not found") }; let formatted_db_url = &db_url; - sqlx::MySqlPool::connect(&formatted_db_url).await + let mut options = MySqlConnectOptions::from_str(formatted_db_url)?; + options.log_slow_statements(LevelFilter::Warn, Duration::from_secs(1)) + .log_statements(LevelFilter::Trace); + MySqlPoolOptions::new().connect_with(options).await } pub async fn run_all_migrations(conn: &MySqlPool){ match sqlx::migrate!("./migrations").run(conn).await { - Ok(()) => {println!("{}", "Successfully ran migrations.")}, + Ok(()) => {info!("Successfully ran migrations.")}, Err(error) => {panic!("{error}")} } } \ No newline at end of file diff --git a/src/do/loggers.rs b/src/do/loggers.rs new file mode 100644 index 0000000..667c440 --- /dev/null +++ b/src/do/loggers.rs @@ -0,0 +1,67 @@ +use std::{future::{ready, Ready}, rc::Rc, io::Read}; + +use actix_web::{ + dev::{self, Service, ServiceRequest, ServiceResponse, Transform}, + Error, HttpMessage, web::BytesMut, +}; +use futures_util::{future::LocalBoxFuture, StreamExt, Stream}; +// There are two steps in middleware processing. +// 1. Middleware initialization, middleware factory gets called with +// next service in chain as parameter. +// 2. Middleware's call method gets called with normal request. +pub struct BodyLogger; + +// Middleware factory is `Transform` trait from actix-service crate +// `S` - type of the next service +// `B` - type of response's body +impl Transform for BodyLogger +where + S: Service, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Response = ServiceResponse; + type Error = Error; + type InitError = (); + type Transform = BodyLoggerMiddleware; + type Future = Ready>; + + fn new_transform(&self, service: S) -> Self::Future { + ready(Ok(BodyLoggerMiddleware { service: Rc::new(service) })) + } +} + +pub struct BodyLoggerMiddleware { + service: Rc, +} + +impl Service for BodyLoggerMiddleware +where + S: Service, Error = Error> + 'static, + S::Future: 'static, + B: 'static, +{ + type Response = ServiceResponse; + type Error = Error; + type Future = LocalBoxFuture<'static, Result>; + + dev::forward_ready!(service); + + fn call(&self, mut req: ServiceRequest) -> Self::Future { + let svc = self.service.clone(); + Box::pin(async move { + + let mut body = BytesMut::new(); + let mut stream = req.take_payload(); + while let Some(chunk) = stream.next().await { + body.extend_from_slice(&chunk?); + } + + println!("request body: {body:?}"); + let res = svc.call(req).await?; + + println!("response: {:?}", res.headers()); + Ok(res) + }) + } +} \ No newline at end of file diff --git a/src/do/mod.rs b/src/do/mod.rs index 60d821c..2a674bb 100644 --- a/src/do/mod.rs +++ b/src/do/mod.rs @@ -1 +1,2 @@ -pub mod shared_state; \ No newline at end of file +pub mod shared_state; +//pub mod loggers; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index aa76690..a888ba4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ mod routes; mod service; mod util; mod dto; mod validation; mod resources; +use actix_web_utils::utils::logger_util::{self}; use r#do::shared_state::SharedStateObj; use util::env_util; use routes::main_router::{start_all_routes, after_startup_fn}; @@ -13,6 +14,8 @@ use dao::{main_dao::{self, run_all_migrations}}; #[tokio::main] async fn main(){ + logger_util::init_logger_custom(2).expect("LOGGER FAILED TO LOAD"); + // Retrieve env variables and send to services that need them. let env_vars = env_util::get_dot_env_map(); diff --git a/src/routes/main_router.rs b/src/routes/main_router.rs index 055695f..eeb1bd5 100644 --- a/src/routes/main_router.rs +++ b/src/routes/main_router.rs @@ -1,11 +1,13 @@ use std::sync::{Mutex, Arc}; -use actix_web::{HttpServer, App, web}; -use crate::r#do::shared_state::SharedStateObj; +use actix_web::{HttpServer, App, web, middleware::Logger}; +use log::info; +use crate::{r#do::shared_state::SharedStateObj}; use super::user_routes; + // This function is to be used in case code is meant to be run after server startup pub fn after_startup_fn() { - println!("{}", "Started server."); + info!("Started server.") } pub async fn start_all_routes(after_startup_fn_call: &dyn Fn(), state: SharedStateObj) @@ -34,6 +36,8 @@ pub async fn start_all_routes(after_startup_fn_call: &dyn Fn(), state: SharedSta let server_future = HttpServer::new( move || { App::new() // Define routes & pass in shared state + //.wrap(loggers::BodyLogger) This is how you add middleware + .wrap(Logger::default()) .app_data(db_conn_state.clone()) .app_data(env_vars_state.clone()) .service(user_routes::create_user)