diff --git a/Cargo.lock b/Cargo.lock index 9458346f..0d45ad95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -670,7 +670,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -758,7 +758,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1374,7 +1374,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -1573,7 +1573,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2002,7 +2002,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2611,7 +2611,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.1", "rustls", - "socket2 0.6.1", + "socket2 0.5.10", "thiserror 2.0.17", "tokio", "tracing", @@ -2648,9 +2648,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.5.10", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -3002,7 +3002,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3167,7 +3167,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3281,9 +3281,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "shmipc" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b567fd4c4565398a0b75345f59906b3f6957a2d3cf2cc8c50a759d829597344b" +checksum = "a5f842e4e077bb5719fd45b7a6dd42b90c5697c46e852a4bec6415ab57142ea0" dependencies = [ "anyhow", "arc-swap", @@ -3527,7 +3527,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.3", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4308,7 +4308,7 @@ dependencies = [ [[package]] name = "volo-http" -version = "0.5.4" +version = "0.5.5" dependencies = [ "ahash", "async-broadcast", @@ -4552,7 +4552,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/volo-http/CLAUDE.md b/volo-http/CLAUDE.md index abdf464c..60e93f87 100644 --- a/volo-http/CLAUDE.md +++ b/volo-http/CLAUDE.md @@ -12,7 +12,8 @@ volo-http/src/ ├── response.rs # Response type alias ├── context/ # RPC contexts │ ├── client.rs # ClientContext (target, stats, timeout) -│ └── server.rs # ServerContext (RpcInfo, path params, extensions) +│ ├── server.rs # ServerContext (RpcInfo, path params, extensions) +│ └── stat.rs # CommonStats ├── error/ │ ├── client.rs # ClientError │ └── server.rs # ExtractBodyError diff --git a/volo-http/Cargo.toml b/volo-http/Cargo.toml index faac32fb..81cedcec 100644 --- a/volo-http/Cargo.toml +++ b/volo-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "volo-http" -version = "0.5.4" +version = "0.5.5" edition.workspace = true homepage.workspace = true repository.workspace = true @@ -117,7 +117,7 @@ client = [ ] # client core server = [ "hyper-util/server", - "dep:ipnet", "dep:matchit", "dep:memchr", "dep:scopeguard", "dep:mime_guess", + "dep:ipnet", "dep:matchit", "dep:memchr", "dep:scopeguard", "dep:mime_guess", "dep:chrono", ] # server core __serde = ["dep:serde"] # a private feature for enabling `serde` by `serde_xxx` diff --git a/volo-http/src/context/mod.rs b/volo-http/src/context/mod.rs index 2518986a..bbfb50d4 100644 --- a/volo-http/src/context/mod.rs +++ b/volo-http/src/context/mod.rs @@ -11,3 +11,6 @@ pub mod server; #[cfg(feature = "server")] pub use self::server::ServerContext; + +#[cfg(all(feature = "client", feature = "server"))] +pub mod stat; diff --git a/volo-http/src/context/server.rs b/volo-http/src/context/server.rs index 0b5e34a8..fcda69ab 100644 --- a/volo-http/src/context/server.rs +++ b/volo-http/src/context/server.rs @@ -1,5 +1,6 @@ //! Context and its utilities of server +use chrono::{DateTime, Local}; use volo::{ context::{Context, Reusable, Role, RpcCx, RpcInfo}, net::Address, @@ -8,7 +9,7 @@ use volo::{ use crate::{ server::param::PathParamsVec, - utils::macros::{impl_deref_and_deref_mut, impl_getter}, + utils::macros::{impl_deref_and_deref_mut, impl_getter, stat_impl}, }; /// RPC context of http server @@ -44,10 +45,38 @@ pub struct ServerCxInner { /// [`PathParamsMap`]: crate::server::param::PathParamsMap /// [`PathParams`]: crate::server::param::PathParams pub params: PathParamsVec, + + /// Statistics of the request + pub stats: ServerStats, } impl ServerCxInner { impl_getter!(params, PathParamsVec); + impl_getter!(stats, ServerStats); +} + +/// Statistics of server +#[derive(Debug, Default, Clone)] +pub struct ServerStats { + read_header_start: Option>, + read_header_finish: Option>, + read_body_start: Option>, + read_body_finish: Option>, + handle_start: Option>, + handle_finish: Option>, + write_start: Option>, + write_finish: Option>, +} + +impl ServerStats { + stat_impl!(read_header_start); + stat_impl!(read_header_finish); + stat_impl!(read_body_start); + stat_impl!(read_body_finish); + stat_impl!(handle_start); + stat_impl!(handle_finish); + stat_impl!(write_start); + stat_impl!(write_finish); } /// Configuration of the request diff --git a/volo-http/src/context/stat.rs b/volo-http/src/context/stat.rs new file mode 100644 index 00000000..eed8ae9f --- /dev/null +++ b/volo-http/src/context/stat.rs @@ -0,0 +1,34 @@ +//! HTTP request and response statistics shared across client and server contexts. + +use chrono::{DateTime, Local}; +use http::{method::Method, status::StatusCode, uri::Uri}; + +/// Shared HTTP statistics captured for every request on both client and server sides +#[derive(Debug, Default, Clone)] +pub struct CommonStats { + /// The time at which request processing began + pub process_start_time: DateTime, + + /// The time at which request processing completed + pub process_end_time: DateTime, + + /// The HTTP method of the request (e.g. `GET`, `POST`) + pub method: Method, + + /// The full URI of the request + pub uri: Uri, + + /// The HTTP status code of the response. + /// + /// Status code may be None if the service failed + pub status_code: Option, + + /// Size of the request body in bytes + pub req_size: i64, + + /// Size of the response body in bytes + pub resp_size: i64, + + /// Whether the request resulted in an error + pub is_error: bool, +} diff --git a/volo-http/src/server/handler.rs b/volo-http/src/server/handler.rs index 802cc034..7cea8033 100644 --- a/volo-http/src/server/handler.rs +++ b/volo-http/src/server/handler.rs @@ -74,7 +74,10 @@ macro_rules! impl_handler { Ok(value) => value, Err(rejection) => return rejection.into_response(), }; - self($($ty,)* $last).await.into_response() + cx.stats.record_handle_start(); + let result = self($($ty,)* $last).await; + cx.stats.record_handle_finish(); + result.into_response() } } }; diff --git a/volo-http/src/server/mod.rs b/volo-http/src/server/mod.rs index a14c1132..02d4deb6 100644 --- a/volo-http/src/server/mod.rs +++ b/volo-http/src/server/mod.rs @@ -534,7 +534,7 @@ where let mut cx = ServerContext::new(service.peer); cx.rpc_info_mut().set_config(service.config); let span = service.span_provider.on_serve(&cx); - let resp = service + let resp: http::Response = service .inner .call(&mut cx, req.map(Body::from_incoming)) .instrument(span) diff --git a/volo-http/src/utils/macros.rs b/volo-http/src/utils/macros.rs index fa4a3ec5..4b3a7308 100644 --- a/volo-http/src/utils/macros.rs +++ b/volo-http/src/utils/macros.rs @@ -94,7 +94,7 @@ macro_rules! impl_getter { #[cfg(feature = "server")] pub(crate) use impl_getter; -#[cfg(feature = "client")] +#[cfg(any(feature = "client", feature = "server"))] macro_rules! stat_impl { ($t: ident) => { paste::paste! { @@ -119,5 +119,5 @@ macro_rules! stat_impl { } }; } -#[cfg(feature = "client")] +#[cfg(any(feature = "client", feature = "server"))] pub(crate) use stat_impl; diff --git a/volo-thrift/src/codec/default/mod.rs b/volo-thrift/src/codec/default/mod.rs index 58878167..4b5fa57e 100644 --- a/volo-thrift/src/codec/default/mod.rs +++ b/volo-thrift/src/codec/default/mod.rs @@ -535,7 +535,7 @@ mod tests { } } - #[cfg(feature = "shmipc")] + #[cfg(all(feature = "shmipc", target_os = "linux"))] #[tokio::test] async fn test_decode_unexpected_eof_returns_none_when_shmipc_available() { let (_env, stream) = ShmipcTestEnv::new().await; @@ -562,7 +562,7 @@ mod tests { assert!(result.unwrap().is_none()); } - #[cfg(feature = "shmipc")] + #[cfg(all(feature = "shmipc", target_os = "linux"))] #[tokio::test] async fn test_decode_other_error_returns_error_when_shmipc_available() { let (_env, stream) = ShmipcTestEnv::new().await;