// The two RPC metric families, on moonp8s.
//
// A zRPC call is measured on two dimensions the HTTP side does not have: the
// method, because there is no route template to group by, and the status code,
// which is a number rather than an HTTP status. The names keep the reference
// implementation's `rpc_client_` and `rpc_server_` prefixes so a dashboard written
// for one reads the other.
///|
/// The per-method call metrics of one side of the wire.
pub struct RpcMetrics {
registry : @moonp8s.Registry
requests : @moonp8s.Family[@moonp8s.Counter]
latency : @moonp8s.Family[@moonp8s.Histogram]
prefix : String
side : String
}
///|
/// The latency buckets the reference implementation registers for an RPC method.
pub let default_latency_buckets : Array[Double] = [
1, 2, 5, 10, 25, 50, 100, 250, 500, 1000, 2000, 5000,
]
///|
/// Metrics for the client side (`kind="client"`) or the server side
/// (`kind="server"`).
pub fn RpcMetrics::new(
kind~ : String = "client",
) -> RpcMetrics raise @metric.Refused {
let prefix = "rpc_" + kind
let registry = @moonp8s.Registry::new()
{
registry,
requests: @moonp8s.Family::new(["method", "code"], () => {
@moonp8s.Counter::new()
}),
latency: @moonp8s.Family::new(["method"], () => {
@moonp8s.Histogram::new(buckets=default_latency_buckets)
}),
prefix,
side: kind,
}
}
///|
/// The registry these families hang off.
pub fn RpcMetrics::registry(self : RpcMetrics) -> @moonp8s.Registry {
self.registry
}
///|
/// Record one finished call: its duration in milliseconds and its status code,
/// under the method's full path.
pub fn RpcMetrics::observe(
self : RpcMetrics,
path : String,
status : Int32,
duration_ms : Double,
) -> Unit {
self.requests.labels([path, status.to_string()]).inc() catch {
_ => ()
}
self.latency.labels([path]).observe(duration_ms) catch {
_ => ()
}
}
///|
/// The number of calls recorded for `path` with `status`.
pub fn RpcMetrics::calls(
self : RpcMetrics,
path : String,
status : Int32,
) -> Double {
self.requests.labels([path, status.to_string()]).get() catch {
_ => 0.0
}
}
///|
/// The latency observations recorded for `path`.
pub fn RpcMetrics::observations(self : RpcMetrics, path : String) -> Int64 {
self.latency.labels([path]).total() catch {
_ => 0L
}
}
///|
/// The two families as the Prometheus text exposition a `/metrics` scrape reads.
pub fn RpcMetrics::to_exposition(self : RpcMetrics) -> String {
let families : Array[@moonp8s.MetricFamily] = [
@moonp8s.MetricFamily::{
name: self.prefix + "_requests_duration_ms",
kind: Histogram,
help: "rpc " + self.side + " requests duration(ms).",
unit: None,
metrics: self.latency.points(),
},
@moonp8s.MetricFamily::{
name: self.prefix + "_requests_code",
kind: Counter,
help: "rpc " + self.side + " requests code count.",
unit: None,
metrics: self.requests.points(),
},
]
@moonp8s.text(families)
}