diff --git a/Cargo.lock b/Cargo.lock index a499082..cbcd9ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4457,6 +4457,7 @@ dependencies = [ "tower", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -4532,6 +4533,16 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.22" @@ -4542,12 +4553,15 @@ dependencies = [ "nu-ansi-term", "once_cell", "regex-automata", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index dfa48ff..1cda04e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,9 +12,9 @@ dotenvy = "0.15.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tracing = "0.1" -tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } +tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "json"] } hyper-util = { version = "0.1.19", features = ["client", "client-legacy"] } -tower-http = { version = "0.6", features = ["cors", "compression-zstd"] } +tower-http = { version = "0.6", features = ["cors", "compression-zstd", "trace"] } tower_governor = { version = "0.8.0", features = ["axum", "tracing"] } hex = "0.4" jwt-compact = { version = "0.8.0", features = ["es256k"] } diff --git a/src/main.rs b/src/main.rs index 168b92f..1dd0fb5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,7 +33,8 @@ use tower_governor::{ }; use tower_http::{ compression::CompressionLayer, - cors::{AllowHeaders, Any, CorsLayer}, + cors::{Any, CorsLayer}, + trace::TraceLayer, }; use tracing::log; use tracing_subscriber::{EnvFilter, fmt, prelude::*}; @@ -385,10 +386,44 @@ async fn main() -> Result<(), Box> { ); } - let app = app + let request_logging = env::var("GATEKEEPER_REQUEST_LOGGING") + .map(|v| v.eq_ignore_ascii_case("true") || v == "1") + .unwrap_or(false); + + let app = if request_logging { + app.layer(TraceLayer::new_for_http() + .make_span_with(|req: &axum::http::Request| { + let headers: std::collections::HashMap<&str, Vec<&str>> = req.headers() + .keys() + .map(|k| { + let vals: Vec<&str> = req.headers() + .get_all(k) + .iter() + .filter_map(|v| v.to_str().ok()) + .collect(); + (k.as_str(), vals) + }) + .collect(); + let headers_json = serde_json::to_string(&headers).unwrap_or_default(); + + tracing::info_span!("request", + method = %req.method(), + path = %req.uri().path(), + headers = %headers_json, + ) + }) + .on_response(|resp: &axum::http::Response, latency: Duration, _span: &tracing::Span| { + tracing::info!(status = resp.status().as_u16(), latency_ms = latency.as_millis() as u64, "response"); + }) + ) .layer(CompressionLayer::new()) .layer(cors) - .with_state(state); + .with_state(state) + } else { + app.layer(CompressionLayer::new()) + .layer(cors) + .with_state(state) + }; let host = env::var("GATEKEEPER_HOST").unwrap_or_else(|_| "0.0.0.0".to_string()); let port: u16 = env::var("GATEKEEPER_PORT") @@ -416,10 +451,21 @@ async fn main() -> Result<(), Box> { fn setup_tracing() { let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); - tracing_subscriber::registry() - .with(env_filter) - .with(fmt::layer()) - .init(); + let json = env::var("GATEKEEPER_LOG_FORMAT") + .map(|v| v.eq_ignore_ascii_case("json")) + .unwrap_or(false); + + if json { + tracing_subscriber::registry() + .with(env_filter) + .with(fmt::layer().json()) + .init(); + } else { + tracing_subscriber::registry() + .with(env_filter) + .with(fmt::layer()) + .init(); + } } async fn shutdown_signal() {