From f83757702a7f53e34c28efcad91b759df638e486 Mon Sep 17 00:00:00 2001 From: "Z. Charles Dziura" Date: Wed, 2 Oct 2024 12:41:25 -0400 Subject: [PATCH] Create a special logging formatter for release builds --- api/.env | 9 ++-- api/.vscode/launch.json | 16 +++--- api/Cargo.toml | 2 - api/assets/new-user-confirmation.html | 4 +- api/src/models/environment.rs | 68 ++++++++++++------------- api/src/models/error.rs | 5 +- api/src/requests/mod.rs | 36 +++++++++++-- api/src/requests/user/create/handler.rs | 16 +++--- api/src/services/auth_token.rs | 2 +- api/src/services/logger.rs | 65 +++++++++++++++++++++-- api/src/services/mailer/service.rs | 15 ++++-- 11 files changed, 163 insertions(+), 75 deletions(-) diff --git a/api/.env b/api/.env index cc9cbc9..b0548e2 100644 --- a/api/.env +++ b/api/.env @@ -1,7 +1,8 @@ -HOSTNAME=localhost -PORT=42069 -TOKEN_KEY=k4.local.hWoS2ZulK9xPEATtXH1Dvj_iynzqfUv5ER5_IFTg5-Q -DATABASE_URL=postgres://debt_pirate:HRURqlUmtjIy@192.168.122.251/debt_pirate ASSETS_DIR=/home/zcdziura/Documents/Projects/debt-pirate/api/assets +DATABASE_URL=postgres://debt_pirate:HRURqlUmtjIy@192.168.122.251/debt_pirate +HOSTNAME=localhost MAINTENANCE_USER_ACCOUNT=debt_pirate:HRURqlUmtjIy +PORT=42069 RUST_LOG=debt_pirate=trace +SEND_VERIFICATION_EMAIL=false +TOKEN_KEY=k4.local.hWoS2ZulK9xPEATtXH1Dvj_iynzqfUv5ER5_IFTg5-Q diff --git a/api/.vscode/launch.json b/api/.vscode/launch.json index 3f4b3f6..8207875 100644 --- a/api/.vscode/launch.json +++ b/api/.vscode/launch.json @@ -7,15 +7,15 @@ { "type": "lldb", "request": "launch", - "name": "Debug executable 'auth-test'", + "name": "Debug executable 'debt-pirate'", "cargo": { "args": [ "build", - "--bin=auth-test", - "--package=auth-test" + "--bin=debt-pirate", + "--package=debt-pirate" ], "filter": { - "name": "auth-test", + "name": "debt-pirate", "kind": "bin" } }, @@ -25,16 +25,16 @@ { "type": "lldb", "request": "launch", - "name": "Debug unit tests in executable 'auth-test'", + "name": "Debug unit tests in executable 'debt-pirate'", "cargo": { "args": [ "test", "--no-run", - "--bin=auth-test", - "--package=auth-test" + "--bin=debt-pirate", + "--package=debt-pirate" ], "filter": { - "name": "auth-test", + "name": "debt-pirate", "kind": "bin" } }, diff --git a/api/Cargo.toml b/api/Cargo.toml index 064021b..3f2cdc2 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -25,9 +25,7 @@ lettre = { version = "0.11", default-features = false, features = [ "tokio1", "tokio1-rustls-tls", ] } -log = "0.4" num_cpus = "1.16" -once_cell = "1.19" pasetors = "0.7" serde = { version = "1.0", features = ["derive", "rc", "std"] } serde_json = "1.0" diff --git a/api/assets/new-user-confirmation.html b/api/assets/new-user-confirmation.html index 6941c18..1d45cfd 100644 --- a/api/assets/new-user-confirmation.html +++ b/api/assets/new-user-confirmation.html @@ -123,7 +123,7 @@ -

Click or tap the button below to confirm your email address. If you didn't create an account with Auth-Test, you can safely delete this email.

+

Click or tap the button below to confirm your email address. If you didn't create an account with Debt Pirate, you can safely delete this email.

@@ -160,7 +160,7 @@ -

Thank you,
The Auth-Test Team

+

Thank you,
The Debt Pirate Team

diff --git a/api/src/models/environment.rs b/api/src/models/environment.rs index 8a60ba8..b33cd1d 100644 --- a/api/src/models/environment.rs +++ b/api/src/models/environment.rs @@ -1,38 +1,24 @@ use std::{ - collections::HashSet, path::{Path, PathBuf}, sync::mpsc::Sender, }; -use once_cell::sync::Lazy; use pasetors::{keys::SymmetricKey, version4::V4}; use crate::services::UserConfirmationMessage; use super::AppError; -static REQUIRED_ENV_VARS: Lazy> = Lazy::new(|| { - [ - "HOSTNAME", - "PORT", - "TOKEN_KEY", - "DATABASE_URL", - "ASSETS_DIR", - "RUST_LOG", - ] - .into_iter() - .collect() -}); - #[derive(Clone)] pub struct Environment { - hostname: String, - port: u32, - token_key: SymmetricKey, + assets_dir: PathBuf, database_url: String, email_sender: Sender, - assets_dir: PathBuf, + hostname: String, + port: u32, rust_log: String, + send_verification_email: bool, + token_key: SymmetricKey, } impl Environment { @@ -41,14 +27,14 @@ impl Environment { dotenvy::dotenv_iter() .expect("Missing .env file") .filter_map(|item| item.ok()) - .filter(|(key, _)| REQUIRED_ENV_VARS.contains(key.as_str())) .for_each(|(key, value)| match key.as_str() { + "ASSETS_DIR" => builder.with_assets_dir(value), + "DATABASE_URL" => builder.with_database_url(value), "HOSTNAME" => builder.with_hostname(value), "PORT" => builder.with_port(value), - "TOKEN_KEY" => builder.with_token_key(value), - "DATABASE_URL" => builder.with_database_url(value), - "ASSETS_DIR" => builder.with_assets_dir(value), "RUST_LOG" => builder.with_rust_log(value), + "SEND_VERIFICATION_EMAIL" => builder.with_send_verification_email(value), + "TOKEN_KEY" => builder.with_token_key(value), _ => {} }); @@ -87,41 +73,48 @@ impl Environment { pub fn rust_log(&self) -> &str { self.rust_log.as_str() } + + pub fn send_verification_email(&self) -> bool { + self.send_verification_email + } } impl From for Environment { fn from(builder: EnvironmentObjectBuilder) -> Self { let EnvironmentObjectBuilder { - hostname, - port, - token_key, + assets_dir, database_url, email_sender, - assets_dir, + hostname, + port, rust_log, + send_verification, + token_key, } = builder; Self { - hostname: hostname.unwrap(), - port: port.unwrap(), - token_key: token_key.unwrap(), + assets_dir: assets_dir.unwrap(), database_url: database_url.unwrap(), email_sender: email_sender.unwrap(), - assets_dir: assets_dir.unwrap(), + hostname: hostname.unwrap(), + port: port.unwrap(), rust_log: rust_log.unwrap(), + send_verification_email: send_verification.unwrap_or(true), + token_key: token_key.unwrap(), } } } #[derive(Debug, Default)] pub struct EnvironmentObjectBuilder { - pub hostname: Option, - pub port: Option, - pub token_key: Option>, + pub assets_dir: Option, pub database_url: Option, pub email_sender: Option>, - pub assets_dir: Option, + pub hostname: Option, + pub port: Option, pub rust_log: Option, + pub send_verification: Option, + pub token_key: Option>, } impl EnvironmentObjectBuilder { @@ -199,4 +192,9 @@ impl EnvironmentObjectBuilder { pub fn with_rust_log(&mut self, rust_log: String) { self.rust_log = Some(rust_log); } + + pub fn with_send_verification_email(&mut self, send_verification_email: String) { + let send_verification_email = send_verification_email.to_lowercase() == "true"; + self.send_verification = Some(send_verification_email); + } } diff --git a/api/src/models/error.rs b/api/src/models/error.rs index 4bf2487..ff85385 100644 --- a/api/src/models/error.rs +++ b/api/src/models/error.rs @@ -116,10 +116,7 @@ enum ErrorKind { impl IntoResponse for AppError { fn into_response(self) -> axum::response::Response { - match &self.kind { - ErrorKind::DuplicateRecord => (), - _ => (), - } + if let ErrorKind::DuplicateRecord = &self.kind { } StatusCode::INTERNAL_SERVER_ERROR.into_response() } diff --git a/api/src/requests/mod.rs b/api/src/requests/mod.rs index 49df903..7c9fdb2 100644 --- a/api/src/requests/mod.rs +++ b/api/src/requests/mod.rs @@ -1,8 +1,15 @@ mod user; -use axum::Router; +use std::time::Duration; + +use axum::{ + extract::{MatchedPath, Request}, + response::Response, + Router, +}; use tokio::net::TcpListener; -use tracing::info; +use tower_http::trace::TraceLayer; +use tracing::{error, info, info_span, warn, Span}; use crate::{ db::DbPool, @@ -38,8 +45,31 @@ pub async fn start_app(pool: DbPool, env: Environment) -> Result<(), AppError> { .await .unwrap(); + let logging_layer = TraceLayer::new_for_http() + .make_span_with(|request: &Request| { + let request_path = request + .extensions() + .get::() + .map(MatchedPath::as_str).unwrap(); + + info_span!("http_request", method = %request.method(), path = %request_path, status = tracing::field::Empty) + }) + .on_response(|response: &Response, _duration: Duration, _span: &Span| { + let status = response.status(); + + _span.record("status", status.to_string()); + + match status.as_u16() { + w if (300..400).contains(&w) => warn!(""), + e if e >= 400 => error!(""), + _ => info!("") + } + }); + let app_state = AppState::new(pool, env); - let app = Router::new().merge(user::requests(app_state.clone())); + let app = Router::new() + .merge(user::requests(app_state.clone())) + .layer(logging_layer); info!("API started successfully."); axum::serve(listener, app) diff --git a/api/src/requests/user/create/handler.rs b/api/src/requests/user/create/handler.rs index 22c051f..0bbc723 100644 --- a/api/src/requests/user/create/handler.rs +++ b/api/src/requests/user/create/handler.rs @@ -51,13 +51,15 @@ pub async fn user_registration_post_handler( auth_token: auth_token.clone(), }; - let _ = app_state - .env() - .email_sender() - .send(new_user_confirmation_message) - .inspect_err(|err| { - eprintln!("Got the rollowing error while sending across the channel: {err}"); - }); + if app_state.env().send_verification_email() { + let _ = app_state + .env() + .email_sender() + .send(new_user_confirmation_message) + .inspect_err(|err| { + eprintln!("Got the rollowing error while sending across the channel: {err}"); + }); + } let response = ( StatusCode::CREATED, diff --git a/api/src/services/auth_token.rs b/api/src/services/auth_token.rs index 376479d..8fa496c 100644 --- a/api/src/services/auth_token.rs +++ b/api/src/services/auth_token.rs @@ -104,7 +104,7 @@ fn generate_token( }; local::encrypt( - &key, + key, &claims, Some(&footer), Some("TODO_ENV_NAME_HERE".as_bytes()), diff --git a/api/src/services/logger.rs b/api/src/services/logger.rs index 6a6cb7e..ead58de 100644 --- a/api/src/services/logger.rs +++ b/api/src/services/logger.rs @@ -1,18 +1,75 @@ -use tracing::level_filters::LevelFilter; -use tracing_subscriber::{fmt::time::UtcTime, EnvFilter}; +#![allow(unused)] +use std::time::SystemTime; + +use humantime::format_rfc3339_millis; +use tracing::{level_filters::LevelFilter, Event, Subscriber}; +use tracing_subscriber::{ + fmt::{ + format::{DefaultFields, Writer}, + FmtContext, FormatEvent, FormatFields, FormattedFields, + }, + registry::{LookupSpan, Scope}, + EnvFilter, +}; use crate::models::Environment; pub fn initialize_logger(env: &Environment) { let log_level = env.rust_log(); + #[cfg(debug_assertions)] + tracing_subscriber::fmt() + .with_env_filter( + EnvFilter::builder() + .with_default_directive(LevelFilter::TRACE.into()) + .parse_lossy(log_level), + ) + .event_format(tracing_subscriber::fmt::format().compact()) + .with_target(false) + .with_file(true) + .with_line_number(true) + .init(); + + #[cfg(not(debug_assertions))] tracing_subscriber::fmt() .with_env_filter( EnvFilter::builder() .with_default_directive(LevelFilter::INFO.into()) .parse_lossy(log_level), ) - .with_timer(UtcTime::rfc_3339()) - .with_target(false) + .event_format(EventFormatter) + .fmt_fields(DefaultFields::new()) .init(); } + +struct EventFormatter; +impl FormatEvent for EventFormatter +where + S: Subscriber + for<'a> LookupSpan<'a>, + N: for<'a> FormatFields<'a> + 'static, +{ + fn format_event( + &self, + ctx: &FmtContext<'_, S, N>, + mut writer: Writer<'_>, + event: &Event<'_>, + ) -> std::fmt::Result { + let timestamp = format_rfc3339_millis(SystemTime::now()); + let level = event.metadata().level(); + + write!(writer, "{timestamp} {level:<5} ")?; + + ctx.field_format().format_fields(writer.by_ref(), event)?; + + for span in ctx.event_scope().into_iter().flat_map(Scope::from_root) { + let exts = span.extensions(); + if let Some(fields) = exts.get::>() { + if !fields.is_empty() { + write!(writer, "{}", fields.fields)?; + } + } + } + + writeln!(writer) + } +} diff --git a/api/src/services/mailer/service.rs b/api/src/services/mailer/service.rs index 703c270..c836207 100644 --- a/api/src/services/mailer/service.rs +++ b/api/src/services/mailer/service.rs @@ -1,4 +1,10 @@ -use std::{fs::File, io::Read, path::Path, sync::mpsc::Receiver, thread}; +use std::{ + fs::File, + io::Read, + path::Path, + sync::{mpsc::Receiver, LazyLock}, + thread, +}; use lettre::{ message::{header::ContentType, Mailbox}, @@ -8,21 +14,20 @@ use lettre::{ }, AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor, }; -use once_cell::sync::Lazy; use tokio::runtime::Handle; use super::UserConfirmationMessage; -static CREDENTIALS: Lazy = Lazy::new(|| { +static CREDENTIALS: LazyLock = LazyLock::new(|| { Credentials::new( "donotreply@mail.dziura.cloud".to_owned(), "hunter2".to_owned(), ) }); -static NUM_CPUS: Lazy = Lazy::new(|| num_cpus::get() as u32); +static NUM_CPUS: LazyLock = LazyLock::new(|| num_cpus::get() as u32); -static FROM_MAILBOX: Lazy = Lazy::new(|| { +static FROM_MAILBOX: LazyLock = LazyLock::new(|| { "No Not Reply Auth-Test " .parse() .unwrap()