From 2b702d88e4d3b2ed3947181ce45c1641a995a128 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sat, 11 Jul 2026 21:58:58 +0200 Subject: [PATCH] feat: add tracing throughout, HTTP TraceLayer, detailed error logs --- crates/application/Cargo.toml | 1 + crates/application/src/auth/login.rs | 8 +++++++- crates/application/src/auth/logout.rs | 1 + crates/application/src/auth/refresh.rs | 2 ++ crates/application/src/auth/register.rs | 8 +++++++- crates/application/src/songs/delete_song.rs | 4 +++- crates/application/src/songs/save_song.rs | 5 ++++- crates/application/src/songs/update_meta.rs | 7 +++++-- crates/application/src/tabs/parse_tab.rs | 16 ++++++++++++++-- crates/presentation/src/errors.rs | 19 ++++++++++++++----- crates/presentation/src/routes/mod.rs | 2 ++ 11 files changed, 60 insertions(+), 13 deletions(-) diff --git a/crates/application/Cargo.toml b/crates/application/Cargo.toml index a552b16..fd1ac7e 100644 --- a/crates/application/Cargo.toml +++ b/crates/application/Cargo.toml @@ -5,5 +5,6 @@ edition = "2024" [dependencies] uuid = { workspace = true } +tracing = { workspace = true } chrono = { version = "0.4", features = ["serde"] } domain = { workspace = true } diff --git a/crates/application/src/auth/login.rs b/crates/application/src/auth/login.rs index 7dbb673..72ebee4 100644 --- a/crates/application/src/auth/login.rs +++ b/crates/application/src/auth/login.rs @@ -21,13 +21,17 @@ pub async fn execute(deps: &LoginDeps, cmd: LoginCommand) -> Result Result Result<(), DomainError> { + tracing::debug!("user logged out"); deps.refresh_repo.revoke(&cmd.refresh_token).await } diff --git a/crates/application/src/auth/refresh.rs b/crates/application/src/auth/refresh.rs index e1b7a18..15c2851 100644 --- a/crates/application/src/auth/refresh.rs +++ b/crates/application/src/auth/refresh.rs @@ -42,6 +42,8 @@ pub async fn execute( }; deps.refresh_repo.create(&new_session).await?; + tracing::debug!(user_id = %session.user_id.value(), "token refreshed"); + Ok(RefreshResult { access_token: generated.token, refresh_token: new_refresh_token, diff --git a/crates/application/src/auth/register.rs b/crates/application/src/auth/register.rs index a1ea67e..cc883ed 100644 --- a/crates/application/src/auth/register.rs +++ b/crates/application/src/auth/register.rs @@ -7,7 +7,10 @@ use super::deps::RegisterDeps; pub async fn execute(deps: &RegisterDeps, cmd: RegisterCommand) -> Result<(), DomainError> { if !deps.allow_registration { - return Err(DomainError::Unauthorized("registration is disabled".into())); + tracing::warn!("registration attempt while disabled"); + return Err(DomainError::Unauthorized( + "registration is disabled".into(), + )); } let password = Password::new(&cmd.password)?; @@ -28,6 +31,9 @@ pub async fn execute(deps: &RegisterDeps, cmd: RegisterCommand) -> Result<(), Do let hash = deps.password_hasher.hash(password.value()).await?; let user = User::new(email, username, hash); + + tracing::info!(user_id = %user.id().value(), "new user registered"); + deps.user_repo.save(&user).await?; Ok(()) diff --git a/crates/application/src/songs/delete_song.rs b/crates/application/src/songs/delete_song.rs index 0ede33a..6134200 100644 --- a/crates/application/src/songs/delete_song.rs +++ b/crates/application/src/songs/delete_song.rs @@ -4,5 +4,7 @@ use super::commands::DeleteSongCommand; use super::deps::SongCommandDeps; pub async fn execute(deps: &SongCommandDeps, cmd: DeleteSongCommand) -> Result<(), DomainError> { - deps.repo.delete(cmd.id).await + deps.repo.delete(cmd.id).await?; + tracing::debug!(id = %cmd.id, "song deleted"); + Ok(()) } diff --git a/crates/application/src/songs/save_song.rs b/crates/application/src/songs/save_song.rs index 33cffd2..c444388 100644 --- a/crates/application/src/songs/save_song.rs +++ b/crates/application/src/songs/save_song.rs @@ -8,5 +8,8 @@ pub async fn execute( deps: &SongCommandDeps, cmd: SaveSongCommand, ) -> Result { - deps.repo.save(&cmd.song).await + let title = cmd.song.meta.title.clone(); + let result = deps.repo.save(&cmd.song).await?; + tracing::debug!(id = %result.id, title, "song saved"); + Ok(result) } diff --git a/crates/application/src/songs/update_meta.rs b/crates/application/src/songs/update_meta.rs index 3683c7f..2761926 100644 --- a/crates/application/src/songs/update_meta.rs +++ b/crates/application/src/songs/update_meta.rs @@ -8,12 +8,15 @@ pub async fn execute( deps: &SongCommandDeps, cmd: UpdateSongMetaCommand, ) -> Result { - deps.repo + let result = deps + .repo .update_meta( cmd.id, cmd.title.as_deref(), cmd.artist.as_deref(), cmd.original_key.as_deref(), ) - .await + .await?; + tracing::debug!(id = %cmd.id, "song meta updated"); + Ok(result) } diff --git a/crates/application/src/tabs/parse_tab.rs b/crates/application/src/tabs/parse_tab.rs index 78e0aa1..abd161a 100644 --- a/crates/application/src/tabs/parse_tab.rs +++ b/crates/application/src/tabs/parse_tab.rs @@ -9,12 +9,14 @@ use super::deps::ParseTabDeps; pub async fn execute(deps: &ParseTabDeps, cmd: ParseTabCommand) -> Result { let html = if let Some(raw_html) = cmd.html { + tracing::debug!("parsing from raw HTML input"); Ok(raw_html) } else if let Some(source) = cmd.source { let tab_source = if source.starts_with("file://") { let path = source.trim_start_matches("file://"); TabSource::File(PathBuf::from(path)) } else { + tracing::debug!(url = source, "fetching tab from URL"); TabSource::Url(source) }; deps.fetcher @@ -27,7 +29,17 @@ pub async fn execute(deps: &ParseTabDeps, cmd: ParseTabCommand) -> Result (StatusCode, Json) { let (status, message) = match &e { DomainError::NotFound => (StatusCode::NOT_FOUND, "not found".to_string()), - DomainError::ValidationError(msg) => (StatusCode::BAD_REQUEST, msg.clone()), - DomainError::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, msg.clone()), - DomainError::Forbidden(msg) => (StatusCode::FORBIDDEN, msg.clone()), - DomainError::InfrastructureError(_) => { - tracing::error!("{e}"); + DomainError::ValidationError(msg) => { + tracing::debug!(error = msg, "validation error"); + (StatusCode::BAD_REQUEST, msg.clone()) + } + DomainError::Unauthorized(msg) => { + tracing::debug!(error = msg, "unauthorized"); + (StatusCode::UNAUTHORIZED, msg.clone()) + } + DomainError::Forbidden(msg) => { + tracing::warn!(error = msg, "forbidden"); + (StatusCode::FORBIDDEN, msg.clone()) + } + DomainError::InfrastructureError(detail) => { + tracing::error!(error = detail, "infrastructure error"); ( StatusCode::INTERNAL_SERVER_ERROR, "internal error".to_string(), diff --git a/crates/presentation/src/routes/mod.rs b/crates/presentation/src/routes/mod.rs index 5bc6ff5..314b64c 100644 --- a/crates/presentation/src/routes/mod.rs +++ b/crates/presentation/src/routes/mod.rs @@ -10,6 +10,7 @@ use axum::{ use infra_wiring::CorsOrigins; use tower_http::cors::{Any, CorsLayer}; use tower_http::services::{ServeDir, ServeFile}; +use tower_http::trace::TraceLayer; use crate::state::AppState; @@ -37,6 +38,7 @@ pub fn build_router(state: AppState) -> Router<()> { Router::new() .nest("/api", api) .fallback_service(spa_service) + .layer(TraceLayer::new_for_http()) .layer(cors) .with_state(state) }