Merge pull request #5 from katanemo/stats

Add stats API
This commit is contained in:
José Ulises Niño Rivera 2024-07-15 17:39:50 -07:00 committed by GitHub
commit d741fdc2de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 126 additions and 0 deletions

View file

@ -1,14 +1,24 @@
use log::info;
use stats::IncrementingMetric;
use stats::Metric;
use stats::RecordingMetric;
use std::time::Duration;
use proxy_wasm::traits::*;
use proxy_wasm::types::*;
mod stats;
proxy_wasm::main! {{
proxy_wasm::set_log_level(LogLevel::Trace);
proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> {
Box::new(HttpHeaderRoot {
header_content: String::new(),
metrics: WasmMetrics {
counter: stats::Counter::new(String::from("wasm_counter")),
gauge: stats::Gauge::new(String::from("wasm_gauge")),
histogram: stats::Histogram::new(String::from("wasm_histogram")),
}
})
});
}}
@ -16,6 +26,7 @@ proxy_wasm::main! {{
struct HttpHeader {
context_id: u32,
header_content: String,
metrics: WasmMetrics,
}
// HttpContext is the trait that allows the Rust code to interact with HTTP objects.
@ -23,6 +34,14 @@ impl HttpContext for HttpHeader {
// Envoy's HTTP model is event driven. The WASM ABI has given implementors events to hook onto
// the lifecycle of the http request and response.
fn on_http_request_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action {
// Metrics
self.metrics.counter.increment(10);
info!("counter -> {}", self.metrics.counter.value());
self.metrics.gauge.record(20);
info!("gauge -> {}", self.metrics.gauge.value());
self.metrics.histogram.record(30);
info!("histogram -> {}", self.metrics.histogram.value());
// Example of reading the HTTP headers on the incoming request
for (name, value) in &self.get_http_request_headers() {
info!("#{} -> {}: {}", self.context_id, name, value);
@ -86,8 +105,16 @@ impl Context for HttpHeader {
}
}
#[derive(Copy, Clone)]
struct WasmMetrics {
counter: stats::Counter,
gauge: stats::Gauge,
histogram: stats::Histogram,
}
struct HttpHeaderRoot {
header_content: String,
metrics: WasmMetrics,
}
impl Context for HttpHeaderRoot {}
@ -105,6 +132,7 @@ impl RootContext for HttpHeaderRoot {
Some(Box::new(HttpHeader {
context_id,
header_content: self.header_content.clone(),
metrics: self.metrics,
}))
}

98
envoyfilter/src/stats.rs Normal file
View file

@ -0,0 +1,98 @@
use proxy_wasm::hostcalls;
use proxy_wasm::types::*;
pub trait Metric {
fn id(&self) -> u32;
fn value(&self) -> u64 {
match hostcalls::get_metric(self.id()) {
Ok(value) => value,
Err(Status::NotFound) => panic!("metric not found: {}", self.id()),
Err(err) => panic!("unexpected status: {:?}", err),
}
}
}
pub trait IncrementingMetric: Metric {
fn increment(&self, offset: i64) {
match hostcalls::increment_metric(self.id(), offset) {
Ok(_) => return,
Err(Status::NotFound) => panic!("metric not found: {}", self.id()),
Err(err) => panic!("unexpected status: {:?}", err),
}
}
}
pub trait RecordingMetric: Metric {
fn record(&self, value: u64) {
match hostcalls::record_metric(self.id(), value) {
Ok(_) => return,
Err(Status::NotFound) => panic!("metric not found: {}", self.id()),
Err(err) => panic!("unexpected status: {:?}", err),
}
}
}
#[derive(Copy, Clone)]
pub struct Counter {
id: u32,
}
impl Counter {
pub fn new(name: String) -> Counter {
let returned_id = hostcalls::define_metric(MetricType::Counter, &name)
.expect("failed to define counter '{}', name");
Counter { id: returned_id }
}
}
impl Metric for Counter {
fn id(&self) -> u32 {
self.id
}
}
impl IncrementingMetric for Counter {}
#[derive(Copy, Clone)]
pub struct Gauge {
id: u32,
}
impl Gauge {
pub fn new(name: String) -> Gauge {
let returned_id = hostcalls::define_metric(MetricType::Gauge, &name)
.expect("failed to define gauge '{}', name");
Gauge { id: returned_id }
}
}
impl Metric for Gauge {
fn id(&self) -> u32 {
self.id
}
}
impl RecordingMetric for Gauge {}
#[derive(Copy, Clone)]
pub struct Histogram {
id: u32,
}
impl Histogram {
pub fn new(name: String) -> Histogram {
let returned_id = hostcalls::define_metric(MetricType::Histogram, &name)
.expect("failed to define histogram '{}', name");
Histogram { id: returned_id }
}
}
impl Metric for Histogram {
fn id(&self) -> u32 {
self.id
}
}
impl RecordingMetric for Histogram {}