Skip to content

Commit

Permalink
Define HTTP request latency histogram timeseries in TOML (#6006)
Browse files Browse the repository at this point in the history
  • Loading branch information
bnaecker authored Jul 9, 2024
1 parent dceb1cb commit d7a5c1c
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 33 deletions.
2 changes: 1 addition & 1 deletion nexus/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ impl ServerContext {
let authz = Arc::new(authz::Authz::new(&log));
let create_tracker = |name: &str| {
let target = HttpService {
name: name.to_string(),
name: name.to_string().into(),
id: config.deployment.id,
};
const START_LATENCY_DECADE: i16 = -6;
Expand Down
3 changes: 2 additions & 1 deletion openapi/nexus.json
Original file line number Diff line number Diff line change
Expand Up @@ -19798,7 +19798,8 @@
"type": "string",
"enum": [
"count",
"bytes"
"bytes",
"seconds"
]
},
"User": {
Expand Down
1 change: 1 addition & 0 deletions oximeter/impl/src/schema/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ impl quote::ToTokens for Units {
let toks = match self {
Units::Count => quote! { ::oximeter::schema::Units::Count },
Units::Bytes => quote! { ::oximeter::schema::Units::Bytes },
Units::Seconds => quote! { ::oximeter::schema::Units::Seconds },
};
toks.to_tokens(tokens);
}
Expand Down
1 change: 1 addition & 0 deletions oximeter/impl/src/schema/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ pub struct TimeseriesDescription {
pub enum Units {
Count,
Bytes,
Seconds,
}

/// The schema for a timeseries.
Expand Down
47 changes: 16 additions & 31 deletions oximeter/instruments/src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,24 @@ use futures::Future;
use http::StatusCode;
use http::Uri;
use oximeter::{
histogram::Histogram, histogram::Record, Metric, MetricsError, Producer,
Sample, Target,
histogram::Histogram, histogram::Record, MetricsError, Producer, Sample,
};
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use uuid::Uuid;

/// The [`HttpService`] is an [`oximeter::Target`] for monitoring HTTP servers.
#[derive(Debug, Clone, Target)]
pub struct HttpService {
pub name: String,
pub id: Uuid,
}

/// An [`oximeter::Metric`] that tracks a histogram of the latency of requests to a specified HTTP
/// endpoint.
#[derive(Debug, Clone, Metric)]
pub struct RequestLatencyHistogram {
pub route: String,
pub method: String,
pub status_code: i64,
#[datum]
pub latency: Histogram<f64>,
}
oximeter::use_timeseries!("http-service.toml");
pub use http_service::HttpService;
pub use http_service::RequestLatencyHistogram;

// Return the route portion of the request, normalized to include a single
// leading slash and no trailing slashes.
fn normalized_uri_path(uri: &Uri) -> String {
format!("/{}", uri.path().trim_end_matches('/').trim_start_matches('/'))
fn normalized_uri_path(uri: &Uri) -> Cow<'static, str> {
Cow::Owned(format!(
"/{}",
uri.path().trim_end_matches('/').trim_start_matches('/')
))
}

impl RequestLatencyHistogram {
Expand All @@ -56,9 +44,9 @@ impl RequestLatencyHistogram {
) -> Self {
Self {
route: normalized_uri_path(request.uri()),
method: request.method().to_string(),
method: request.method().to_string().into(),
status_code: status_code.as_u16().into(),
latency: histogram,
datum: histogram,
}
}

Expand Down Expand Up @@ -154,7 +142,7 @@ impl LatencyTracker {
self.histogram.clone(),
)
});
entry.latency.sample(latency.as_secs_f64()).map_err(MetricsError::from)
entry.datum.sample(latency.as_secs_f64()).map_err(MetricsError::from)
}

/// Instrument the given Dropshot endpoint handler function.
Expand Down Expand Up @@ -228,10 +216,8 @@ mod tests {

#[test]
fn test_latency_tracker() {
let service = HttpService {
name: String::from("my-service"),
id: ID.parse().unwrap(),
};
let service =
HttpService { name: "my-service".into(), id: ID.parse().unwrap() };
let hist = Histogram::new(&[0.0, 1.0]).unwrap();
let tracker = LatencyTracker::new(service, hist);
let request = http::request::Builder::new()
Expand All @@ -249,8 +235,7 @@ mod tests {
.unwrap();

let key = "/some/uri:GET:200";
let actual_hist =
tracker.latencies.lock().unwrap()[key].latency.clone();
let actual_hist = tracker.latencies.lock().unwrap()[key].datum.clone();
assert_eq!(actual_hist.n_samples(), 1);
let bins = actual_hist.iter().collect::<Vec<_>>();
assert_eq!(bins[1].count, 1);
Expand Down
38 changes: 38 additions & 0 deletions oximeter/oximeter/schema/http-service.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
format_version = 1

[target]
name = "http_service"
description = "An Oxide HTTP server"
authz_scope = "fleet"
versions = [
{ version = 1, fields = [ "name", "id" ] },
]

[[metrics]]
name = "request_latency_histogram"
description = "Duration for the server to handle a request"
units = "seconds"
datum_type = "histogram_f64"
versions = [
{ added_in = 1, fields = [ "route", "method", "status_code" ] }
]

[fields.name]
type = "string"
description = "The name of the HTTP server, or program running it"

[fields.id]
type = "uuid"
description = "UUID of the HTTP server"

[fields.route]
type = "string"
description = "HTTP route in the request"

[fields.method]
type = "string"
description = "HTTP method in the request"

[fields.status_code]
type = "i64"
description = "HTTP status code in the server's response"

0 comments on commit d7a5c1c

Please sign in to comment.