diff options
| author | Nikolay Govorov <me@govorov.online> | 2026-01-14 01:57:57 +0000 |
|---|---|---|
| committer | Nikolay Govorov <me@govorov.online> | 2026-01-14 02:01:07 +0000 |
| commit | a24fcbc469afa7241ec9d7a058e0953794d9b4f4 (patch) | |
| tree | a5d3fdbcbb8c9b1aeb8f3cd81200d5d71662cf23 | |
| parent | a49e094d3e85b0b0fb10cf6c5b1a8a4f4259dfae (diff) | |
| download | tar tar.gz tar.bz2 tar.lz tar.xz tar.zst zip | |
Add accept logging with context
Diffstat
| -rw-r--r-- | Cargo.lock | 165 | +163 −2 |
| -rw-r--r-- | Cargo.toml | 7 | +6 −1 |
| -rw-r--r-- | src/controller_zig.rs | 1 | +0 −1 |
| -rw-r--r-- | src/main.rs | 114 | +101 −13 |
4 files changed, 270 insertions, 17 deletions
diff --git a/Cargo.lock b/Cargo.lock index 62f2084..056f67e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,21 @@ dependencies = [ ] [[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] name = "allocator-api2" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -247,6 +262,27 @@ dependencies = [ ] [[package]] +name = "brotli" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] name = "bstr" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -482,9 +518,12 @@ version = "0.4.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a" dependencies = [ + "brotli", "compression-core", "flate2", "memchr", + "zstd", + "zstd-safe", ] [[package]] @@ -1911,6 +1950,12 @@ dependencies = [ ] [[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + +[[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2352,6 +2397,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] name = "matchit" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2400,6 +2454,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] name = "minijinja" version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3262,6 +3326,15 @@ dependencies = [ ] [[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] name = "shell-words" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3767,6 +3840,15 @@ dependencies = [ ] [[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] name = "time" version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3945,9 +4027,9 @@ checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -3966,6 +4048,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "async-compression", + "base64", "bitflags", "bytes", "futures-core", @@ -3973,13 +4056,20 @@ dependencies = [ "http", "http-body", "http-body-util", + "http-range-header", + "httpdate", "iri-string", + "mime", + "mime_guess", + "percent-encoding", "pin-project-lite", "tokio", "tokio-util", "tower", "tower-layer", "tower-service", + "tracing", + "uuid", ] [[package]] @@ -4024,6 +4114,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", ] [[package]] @@ -4045,6 +4178,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] name = "unicode-bidi" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4115,6 +4254,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] +name = "uuid" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +dependencies = [ + "getrandom 0.3.4", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4797,6 +4953,11 @@ dependencies = [ "thiserror 2.0.17", "tokio", "toml", + "tower", + "tower-http", + "tracing", + "tracing-subscriber", + "uuid", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5417587..17953b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,12 +19,17 @@ hyper-tls = "0.6.0" hyper-util = { version = "0.1.19", features = ["client", "http1", "http2", "tokio"] } minijinja = "2.14.0" semver = "1.0" -sha2 = "0.10" serde = { version = "1.0", features = ["derive"] } +sha2 = "0.10" sqlx = { version = "0.8.6", features = ["sqlite", "runtime-tokio", "macros", "derive", "chrono", "json"] } thiserror = "2.0" tokio = { version = "1.49.0", features = ["full"] } toml = "0.9.8" +tower-http = { version = "0.6.8", features = ["full"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } +tower = "0.5.3" +uuid = { version = "1.19.0", features = ["v4"] } [dev-dependencies] cargo-deny = "0.19.0" diff --git a/src/controller_zig.rs b/src/controller_zig.rs index 7403998..ba33462 100644 --- a/src/controller_zig.rs +++ b/src/controller_zig.rs @@ -188,7 +188,6 @@ impl ZigController { .upstream .fetch(service_upstream::DownloadRequest { url }) .await?; - // bytes: entry.bytes.clone(), match controller.storage.put("zig", &filename, &entry.bytes).await { Ok(()) => {} diff --git a/src/main.rs b/src/main.rs index 8edafe3..2c68dfb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,22 @@ // SPDX-FileCopyrightText: 2026 Nikolay Govorov <me@govorov.online> // SPDX-License-Identifier: AGPL-3.0-or-later -use axum::Router; use std::path::PathBuf; use std::sync::Arc; +use axum::{Router, http}; +use tower_http::{request_id, trace::TraceLayer}; +use tracing::{error, info}; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + mod controller_web; mod controller_zig; mod service_config; mod service_storage; mod service_upstream; +static REQUEST_ID_HEADER: http::HeaderName = http::HeaderName::from_static("x-request-id"); + const VERSION: &str = env!("CARGO_PKG_VERSION"); const HELP: &str = "\ Usage: zorian [--config=<path>] @@ -38,25 +44,44 @@ async fn main() { } } + tracing_subscriber::registry() + .with({ + #[cfg(debug_assertions)] + let fmt = tracing_subscriber::fmt::layer().pretty(); + #[cfg(not(debug_assertions))] + let fmt = tracing_subscriber::fmt::layer().json(); + fmt + }) + .with(match std::env::var_os("ZORIAN_LOG") { + None => tracing_subscriber::EnvFilter::new("info,tower_http=info"), + Some(val) => tracing_subscriber::EnvFilter::try_new(val.to_string_lossy()) + .expect("Invalid ZORIAN_LOG"), + }) + .init(); + let config = Arc::new(match config_path { - Some(path) => service_config::ConfigService::from_file(&path).unwrap_or_else(|e| { - eprintln!("error: {e}"); - std::process::exit(1); - }), - None => service_config::ConfigService::default(), + Some(path) => { + info!("use config file from {}", path.to_str().unwrap()); + + service_config::ConfigService::from_file(&path).unwrap_or_else(|e| { + error!("{e}"); + std::process::exit(1); + }) + } + None => { + info!("configuration file path not provided"); + service_config::ConfigService::default() + } }); config.validate().unwrap_or_else(|e| { - eprintln!("config error: {e}"); + error!("invalid config: {e}"); std::process::exit(1); }); let storage = Arc::new( service_storage::StorageService::new(config.clone()) .await - .unwrap_or_else(|e| { - eprintln!("storage error: {e}"); - std::process::exit(1); - }), + .unwrap(), ); let upstream = Arc::new(service_upstream::UpstreamService::new()); @@ -67,14 +92,77 @@ async fn main() { upstream.clone(), )); + let accept_logger = TraceLayer::new_for_http() + .make_span_with(|req: &http::Request<_>| { + let request_id = req + .headers() + .get(&REQUEST_ID_HEADER) + .and_then(|v| v.to_str().ok()) + .unwrap_or("<invalid>"); + + tracing::info_span!("http_request", request_id = %request_id) + }) + .on_request(log_request) + .on_response(log_response); + let app = Router::new() .merge(web_controller.router()) - .merge(zig_controller.router()); + .merge(zig_controller.router()) + .layer(accept_logger) + .layer(request_id::PropagateRequestIdLayer::new( + REQUEST_ID_HEADER.clone(), + )) + .layer(request_id::SetRequestIdLayer::new( + REQUEST_ID_HEADER.clone(), + request_id::MakeRequestUuid, + )); let listener = tokio::net::TcpListener::bind(config.listen()) .await .unwrap(); - println!("listening on {}", listener.local_addr().unwrap()); + info!("listening on {}", listener.local_addr().unwrap()); axum::serve(listener, app).await.unwrap(); } + +fn log_request<B>(req: &http::Request<B>, _span: &tracing::Span) { + let headers = req.headers(); + + let client_ip = headers + .get("x-forwarded-for") + .or_else(|| headers.get("x-real-ip")) + .and_then(|v| v.to_str().ok()); + let user_agent = headers + .get(http::header::USER_AGENT) + .and_then(|v| v.to_str().ok()); + let referer = headers + .get(http::header::REFERER) + .and_then(|v| v.to_str().ok()); + + info!( + method = %req.method(), + uri = %req.uri(), + client_ip, + user_agent, + referer, + "request started" + ); +} + +fn log_response<B>(res: &http::Response<B>, latency: std::time::Duration, _span: &tracing::Span) { + let headers = res.headers(); + let content_length = headers + .get(http::header::CONTENT_LENGTH) + .and_then(|v| v.to_str().ok()); + let content_type = headers + .get(http::header::CONTENT_TYPE) + .and_then(|v| v.to_str().ok()); + + info!( + status = %res.status(), + latency = ?latency, + content_length, + content_type, + "request finished" + ); +} |
