Skip to content

Commit d7acebc

Browse files
authored
feat(volo-http): add CommonStats and ServerStats struct to record lifecycle timings (#649)
* feat(volo-http): add CommonStats struct * feat(volo-http): record http lifecycle timings * chore(volo-http): remove debug lines * fix(volo-http): move handle start recording to correct place * fix(volo-http): remove Arc and Mutex from ServerStats * chore(volo-http): update CLAUDE.md to include stat.rs * fix(volo-http): correct server handle timings * chore(volo-http): bump crate version
1 parent c971091 commit d7acebc

10 files changed

Lines changed: 95 additions & 25 deletions

File tree

Cargo.lock

Lines changed: 15 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

volo-http/CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ volo-http/src/
1212
├── response.rs # Response type alias
1313
├── context/ # RPC contexts
1414
│ ├── client.rs # ClientContext (target, stats, timeout)
15-
│ └── server.rs # ServerContext (RpcInfo, path params, extensions)
15+
│ ├── server.rs # ServerContext (RpcInfo, path params, extensions)
16+
│ └── stat.rs # CommonStats
1617
├── error/
1718
│ ├── client.rs # ClientError
1819
│ └── server.rs # ExtractBodyError

volo-http/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "volo-http"
3-
version = "0.5.4"
3+
version = "0.5.5"
44
edition.workspace = true
55
homepage.workspace = true
66
repository.workspace = true
@@ -117,7 +117,7 @@ client = [
117117
] # client core
118118
server = [
119119
"hyper-util/server",
120-
"dep:ipnet", "dep:matchit", "dep:memchr", "dep:scopeguard", "dep:mime_guess",
120+
"dep:ipnet", "dep:matchit", "dep:memchr", "dep:scopeguard", "dep:mime_guess", "dep:chrono",
121121
] # server core
122122

123123
__serde = ["dep:serde"] # a private feature for enabling `serde` by `serde_xxx`

volo-http/src/context/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ pub mod server;
1111

1212
#[cfg(feature = "server")]
1313
pub use self::server::ServerContext;
14+
15+
#[cfg(all(feature = "client", feature = "server"))]
16+
pub mod stat;

volo-http/src/context/server.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Context and its utilities of server
22
3+
use chrono::{DateTime, Local};
34
use volo::{
45
context::{Context, Reusable, Role, RpcCx, RpcInfo},
56
net::Address,
@@ -8,7 +9,7 @@ use volo::{
89

910
use crate::{
1011
server::param::PathParamsVec,
11-
utils::macros::{impl_deref_and_deref_mut, impl_getter},
12+
utils::macros::{impl_deref_and_deref_mut, impl_getter, stat_impl},
1213
};
1314

1415
/// RPC context of http server
@@ -44,10 +45,38 @@ pub struct ServerCxInner {
4445
/// [`PathParamsMap`]: crate::server::param::PathParamsMap
4546
/// [`PathParams`]: crate::server::param::PathParams
4647
pub params: PathParamsVec,
48+
49+
/// Statistics of the request
50+
pub stats: ServerStats,
4751
}
4852

4953
impl ServerCxInner {
5054
impl_getter!(params, PathParamsVec);
55+
impl_getter!(stats, ServerStats);
56+
}
57+
58+
/// Statistics of server
59+
#[derive(Debug, Default, Clone)]
60+
pub struct ServerStats {
61+
read_header_start: Option<DateTime<Local>>,
62+
read_header_finish: Option<DateTime<Local>>,
63+
read_body_start: Option<DateTime<Local>>,
64+
read_body_finish: Option<DateTime<Local>>,
65+
handle_start: Option<DateTime<Local>>,
66+
handle_finish: Option<DateTime<Local>>,
67+
write_start: Option<DateTime<Local>>,
68+
write_finish: Option<DateTime<Local>>,
69+
}
70+
71+
impl ServerStats {
72+
stat_impl!(read_header_start);
73+
stat_impl!(read_header_finish);
74+
stat_impl!(read_body_start);
75+
stat_impl!(read_body_finish);
76+
stat_impl!(handle_start);
77+
stat_impl!(handle_finish);
78+
stat_impl!(write_start);
79+
stat_impl!(write_finish);
5180
}
5281

5382
/// Configuration of the request

volo-http/src/context/stat.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//! HTTP request and response statistics shared across client and server contexts.
2+
3+
use chrono::{DateTime, Local};
4+
use http::{method::Method, status::StatusCode, uri::Uri};
5+
6+
/// Shared HTTP statistics captured for every request on both client and server sides
7+
#[derive(Debug, Default, Clone)]
8+
pub struct CommonStats {
9+
/// The time at which request processing began
10+
pub process_start_time: DateTime<Local>,
11+
12+
/// The time at which request processing completed
13+
pub process_end_time: DateTime<Local>,
14+
15+
/// The HTTP method of the request (e.g. `GET`, `POST`)
16+
pub method: Method,
17+
18+
/// The full URI of the request
19+
pub uri: Uri,
20+
21+
/// The HTTP status code of the response.
22+
///
23+
/// Status code may be None if the service failed
24+
pub status_code: Option<StatusCode>,
25+
26+
/// Size of the request body in bytes
27+
pub req_size: i64,
28+
29+
/// Size of the response body in bytes
30+
pub resp_size: i64,
31+
32+
/// Whether the request resulted in an error
33+
pub is_error: bool,
34+
}

volo-http/src/server/handler.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ macro_rules! impl_handler {
7474
Ok(value) => value,
7575
Err(rejection) => return rejection.into_response(),
7676
};
77-
self($($ty,)* $last).await.into_response()
77+
cx.stats.record_handle_start();
78+
let result = self($($ty,)* $last).await;
79+
cx.stats.record_handle_finish();
80+
result.into_response()
7881
}
7982
}
8083
};

volo-http/src/server/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ where
534534
let mut cx = ServerContext::new(service.peer);
535535
cx.rpc_info_mut().set_config(service.config);
536536
let span = service.span_provider.on_serve(&cx);
537-
let resp = service
537+
let resp: http::Response<Body> = service
538538
.inner
539539
.call(&mut cx, req.map(Body::from_incoming))
540540
.instrument(span)

volo-http/src/utils/macros.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ macro_rules! impl_getter {
9494
#[cfg(feature = "server")]
9595
pub(crate) use impl_getter;
9696

97-
#[cfg(feature = "client")]
97+
#[cfg(any(feature = "client", feature = "server"))]
9898
macro_rules! stat_impl {
9999
($t: ident) => {
100100
paste::paste! {
@@ -119,5 +119,5 @@ macro_rules! stat_impl {
119119
}
120120
};
121121
}
122-
#[cfg(feature = "client")]
122+
#[cfg(any(feature = "client", feature = "server"))]
123123
pub(crate) use stat_impl;

volo-thrift/src/codec/default/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ mod tests {
535535
}
536536
}
537537

538-
#[cfg(feature = "shmipc")]
538+
#[cfg(all(feature = "shmipc", target_os = "linux"))]
539539
#[tokio::test]
540540
async fn test_decode_unexpected_eof_returns_none_when_shmipc_available() {
541541
let (_env, stream) = ShmipcTestEnv::new().await;
@@ -562,7 +562,7 @@ mod tests {
562562
assert!(result.unwrap().is_none());
563563
}
564564

565-
#[cfg(feature = "shmipc")]
565+
#[cfg(all(feature = "shmipc", target_os = "linux"))]
566566
#[tokio::test]
567567
async fn test_decode_other_error_returns_error_when_shmipc_available() {
568568
let (_env, stream) = ShmipcTestEnv::new().await;

0 commit comments

Comments
 (0)