Skip to content

Commit 6da8e6d

Browse files
committed
Add metric related hostcalls to Context trait.
Also add an example filter demonstrating the use of metrics. Signed-off-by: Caio Ramos Casimiro <[email protected]>
1 parent bf50796 commit 6da8e6d

File tree

8 files changed

+285
-0
lines changed

8 files changed

+285
-0
lines changed

.github/workflows/rust.yml

+2
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ jobs:
193193
- 'http_body'
194194
- 'http_config'
195195
- 'http_headers'
196+
- 'metrics'
196197

197198
defaults:
198199
run:
@@ -247,6 +248,7 @@ jobs:
247248
- 'http_body'
248249
- 'http_config'
249250
- 'http_headers'
251+
- 'metrics'
250252

251253
defaults:
252254
run:

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
- [HTTP Headers](./examples/http_headers/)
2222
- [HTTP Response body](./examples/http_body/)
2323
- [HTTP Configuration](./examples/http_config/)
24+
- [Metrics](./examples/metrics/)
2425

2526
## Articles & blog posts from the community
2627

examples/metrics/Cargo.toml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
publish = false
3+
name = "proxy-wasm-example-metrics"
4+
version = "0.0.1"
5+
authors = ["Caio Ramos Casimiro <[email protected]>"]
6+
description = "Proxy-Wasm plugin example: metrics"
7+
license = "Apache-2.0"
8+
edition = "2018"
9+
10+
[lib]
11+
crate-type = ["cdylib"]
12+
13+
[dependencies]
14+
log = "0.4"
15+
proxy-wasm = { path = "../../" }
16+
17+
[profile.release]
18+
lto = true
19+
opt-level = 3
20+
codegen-units = 1
21+
panic = "abort"
22+
strip = "debuginfo"

examples/metrics/README.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
## Proxy-Wasm plugin example: Metrics
2+
3+
Proxy-Wasm plugin that demonstrates how to define and update metrics.
4+
5+
### Building
6+
7+
```sh
8+
$ cargo build --target wasm32-wasi --release
9+
```
10+
11+
### Using in Envoy
12+
13+
This example can be run with [`docker compose`](https://docs.docker.com/compose/install/)
14+
and has a matching Envoy configuration.
15+
16+
```sh
17+
$ docker compose up
18+
```
19+
20+
Send HTTP requests to `localhost:10000/headers`:
21+
22+
23+
Retrieve metrics from `localhost:9901/stats`:
24+
25+
```console
26+
$ curl -s localhost:9901/stats | grep 'example'
27+
wasmcustom.conter_example: 1
28+
wasmcustom.gauge_example: 10
29+
wasmcustom.histogram_example: P0(nan,8) P25(nan,8.075) P50(nan,9.05) P75(nan,10.25) P90(nan,10.7) P95(nan,10.85) P99(nan,10.969999999999999) P99.5(nan,10.985) P99.9(nan,10.997) P100(nan,11)
30+
```

examples/metrics/docker-compose.yaml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright 2022 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
services:
16+
envoy:
17+
image: envoyproxy/envoy:v1.24-latest
18+
hostname: envoy
19+
ports:
20+
- "10000:10000"
21+
- "9901:9901"
22+
volumes:
23+
- ./envoy.yaml:/etc/envoy/envoy.yaml
24+
- ./target/wasm32-wasi/release:/etc/envoy/proxy-wasm-plugins
25+
networks:
26+
- envoymesh
27+
networks:
28+
envoymesh: {}

examples/metrics/envoy.yaml

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Copyright 2022 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
static_resources:
16+
listeners:
17+
address:
18+
socket_address:
19+
address: 0.0.0.0
20+
port_value: 10000
21+
filter_chains:
22+
- filters:
23+
- name: envoy.filters.network.http_connection_manager
24+
typed_config:
25+
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
26+
stat_prefix: ingress_http
27+
codec_type: AUTO
28+
route_config:
29+
name: local_routes
30+
virtual_hosts:
31+
- name: local_service
32+
domains:
33+
- "*"
34+
routes:
35+
- match:
36+
prefix: "/"
37+
route:
38+
cluster: httpbin
39+
http_filters:
40+
- name: envoy.filters.http.wasm
41+
typed_config:
42+
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
43+
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
44+
value:
45+
config:
46+
name: "metrics"
47+
vm_config:
48+
runtime: "envoy.wasm.runtime.v8"
49+
code:
50+
local:
51+
filename: "/etc/envoy/proxy-wasm-plugins/proxy_wasm_example_metrics.wasm"
52+
- name: envoy.filters.http.router
53+
typed_config:
54+
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
55+
clusters:
56+
- name: httpbin
57+
connect_timeout: 5s
58+
type: STRICT_DNS
59+
lb_policy: ROUND_ROBIN
60+
load_assignment:
61+
cluster_name: httpbin
62+
endpoints:
63+
- lb_endpoints:
64+
- endpoint:
65+
address:
66+
socket_address:
67+
address: httpbin.org
68+
port_value: 80
69+
hostname: "httpbin.org"
70+
admin:
71+
profile_path: /tmp/envoy.prof
72+
address:
73+
socket_address: { address: 0.0.0.0, port_value: 9901 }

examples/metrics/src/lib.rs

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use log::info;
16+
use proxy_wasm::traits::*;
17+
use proxy_wasm::types::*;
18+
19+
#[derive(Copy, Clone)]
20+
struct HttpAuthMetrics {
21+
counter_example: u32,
22+
gauge_example: u32,
23+
histogram_example: u32,
24+
}
25+
26+
struct HttpAuthRoot {
27+
metrics: HttpAuthMetrics,
28+
monitored_path: String,
29+
}
30+
31+
struct HttpAuth {
32+
metrics: HttpAuthMetrics,
33+
monitored_path: String,
34+
}
35+
36+
proxy_wasm::main! {{
37+
proxy_wasm::set_log_level(LogLevel::Trace);
38+
proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> {
39+
Box::new(HttpAuthRoot {
40+
metrics: HttpAuthMetrics {
41+
counter_example: 0,
42+
gauge_example: 0,
43+
histogram_example: 0,
44+
},
45+
monitored_path: "".to_string(),
46+
})
47+
});
48+
}}
49+
50+
impl Context for HttpAuth {}
51+
impl HttpContext for HttpAuth {
52+
fn on_http_request_headers(&mut self, num_headers: usize, _: bool) -> Action {
53+
self.record_metric(self.metrics.histogram_example, num_headers as u64)
54+
.unwrap();
55+
56+
match self.get_http_request_header(":path") {
57+
Some(path) if path == self.monitored_path => {
58+
info!("Monitored path {} accessed.", self.monitored_path);
59+
60+
self.increment_metric(self.metrics.counter_example, 1)
61+
.unwrap();
62+
63+
Action::Continue
64+
}
65+
_ => Action::Continue,
66+
}
67+
}
68+
69+
fn on_http_response_headers(&mut self, _: usize, _: bool) -> Action {
70+
let counter_value = self.get_metric(self.metrics.counter_example).unwrap();
71+
let gauge_value = self.get_metric(self.metrics.gauge_example).unwrap();
72+
// histogram retrieval isn't supported
73+
74+
self.set_http_response_header("Powered-By", Some("proxy-wasm"));
75+
self.set_http_response_header("My-Counter", Some(format!("{}", counter_value).as_str()));
76+
self.set_http_response_header("My-Gauge", Some(format!("{}", gauge_value).as_str()));
77+
78+
Action::Continue
79+
}
80+
}
81+
82+
impl Context for HttpAuthRoot {}
83+
84+
impl RootContext for HttpAuthRoot {
85+
fn on_configure(&mut self, _: usize) -> bool {
86+
self.metrics.counter_example = self
87+
.define_metric(MetricType::Counter, "counter_example")
88+
.expect("failed defining counter_example metric");
89+
self.metrics.gauge_example = self
90+
.define_metric(MetricType::Gauge, "gauge_example")
91+
.expect("failed defining gauge_example metric");
92+
self.metrics.histogram_example = self
93+
.define_metric(MetricType::Histogram, "histogram_example")
94+
.expect("failed defining histogram_example metric");
95+
96+
self.record_metric(self.metrics.gauge_example, 10).unwrap();
97+
98+
self.monitored_path = "/headers".to_string();
99+
100+
true
101+
}
102+
103+
fn create_http_context(&self, _: u32) -> Option<Box<dyn HttpContext>> {
104+
Some(Box::new(HttpAuth {
105+
metrics: self.metrics,
106+
monitored_path: self.monitored_path.clone(),
107+
}))
108+
}
109+
110+
fn get_type(&self) -> Option<ContextType> {
111+
Some(ContextType::HttpContext)
112+
}
113+
}

src/traits.rs

+16
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,22 @@ pub trait Context {
5858
hostcalls::enqueue_shared_queue(queue_id, value)
5959
}
6060

61+
fn define_metric(&self, metric_type: MetricType, name: &str) -> Result<u32, Status> {
62+
hostcalls::define_metric(metric_type, name)
63+
}
64+
65+
fn get_metric(&self, metric_id: u32) -> Result<u64, Status> {
66+
hostcalls::get_metric(metric_id)
67+
}
68+
69+
fn record_metric(&self, metric_id: u32, value: u64) -> Result<(), Status> {
70+
hostcalls::record_metric(metric_id, value)
71+
}
72+
73+
fn increment_metric(&self, metric_id: u32, offset: i64) -> Result<(), Status> {
74+
hostcalls::increment_metric(metric_id, offset)
75+
}
76+
6177
fn dispatch_http_call(
6278
&self,
6379
upstream: &str,

0 commit comments

Comments
 (0)