Skip to content

Commit 5b19423

Browse files
committed
comments
Signed-off-by: José Ulises Niño Rivera <[email protected]>
1 parent 6543af3 commit 5b19423

File tree

9 files changed

+264
-56
lines changed

9 files changed

+264
-56
lines changed

.github/workflows/rust.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ jobs:
238238
- 'http_body'
239239
- 'http_config'
240240
- 'http_headers'
241+
- 'metrics'
241242

242243
defaults:
243244
run:
@@ -301,6 +302,7 @@ jobs:
301302
- 'http_body'
302303
- 'http_config'
303304
- 'http_headers'
305+
- 'metrics'
304306

305307
defaults:
306308
run:

README.md

Lines changed: 1 addition & 0 deletions
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

Lines changed: 22 additions & 0 deletions
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 = ["José Ulises Niño Rivera <[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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
## Proxy-Wasm plugin example: HTTP headers
2+
3+
Proxy-Wasm plugin that logs HTTP request/response headers.
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 request to `localhost:10000/`:
21+
22+
```sh
23+
$ curl localhost:10000/ -H "x-envoy-wasm-metric-value: 100" -H "x-envoy-wasm-metric: gauge"
24+
```
25+
26+
For instance that request will set the example gauge to 100. Which you can see using the stats endpoint
27+
28+
```sh
29+
& curl -s localhost:9001/stats | grep wasmcustom.wasm_gauge
30+
31+
100
32+
```

examples/metrics/docker-compose.yaml

Lines changed: 28 additions & 0 deletions
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+
- "9001:9001"
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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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+
direct_response:
38+
status: 200
39+
body:
40+
inline_string: "Request /hello and be welcomed!\n"
41+
http_filters:
42+
- name: envoy.filters.http.wasm
43+
typed_config:
44+
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
45+
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
46+
value:
47+
config:
48+
name: "http_headers"
49+
vm_config:
50+
runtime: "envoy.wasm.runtime.v8"
51+
code:
52+
local:
53+
filename: "/etc/envoy/proxy-wasm-plugins/proxy_wasm_example_metrics.wasm"
54+
- name: envoy.filters.http.router
55+
typed_config:
56+
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
57+
58+
admin:
59+
profile_path: /tmp/envoy.prof
60+
address:
61+
socket_address: { address: 0.0.0.0, port_value: 9001 }

examples/metrics/src/lib.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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 proxy_wasm::stats;
16+
use proxy_wasm::traits::*;
17+
use proxy_wasm::types::*;
18+
19+
use std::convert::TryInto;
20+
21+
proxy_wasm::main! {{
22+
proxy_wasm::set_log_level(LogLevel::Trace);
23+
proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> { Box::new(
24+
MetricsRootContext {
25+
metrics: WasmMetrics {
26+
counter: stats::Counter::new(String::from("wasm_counter")),
27+
gauge: stats::Gauge::new(String::from("wasm_gauge")),
28+
histogram: stats::Histogram::new(String::from("wasm_histogram")),
29+
}
30+
}
31+
)});
32+
}}
33+
34+
#[derive(Copy, Clone)]
35+
struct WasmMetrics {
36+
counter: stats::Counter,
37+
gauge: stats::Gauge,
38+
histogram: stats::Histogram,
39+
}
40+
41+
struct MetricsRootContext {
42+
metrics: WasmMetrics,
43+
}
44+
45+
impl Context for MetricsRootContext {}
46+
47+
impl RootContext for MetricsRootContext {
48+
fn get_type(&self) -> Option<ContextType> {
49+
Some(ContextType::HttpContext)
50+
}
51+
52+
fn create_http_context(&self, _: u32) -> Option<Box<dyn HttpContext>> {
53+
Some(Box::new(StreamContext {
54+
metrics: self.metrics,
55+
}))
56+
}
57+
}
58+
59+
struct StreamContext {
60+
metrics: WasmMetrics,
61+
}
62+
63+
impl Context for StreamContext {}
64+
65+
impl HttpContext for StreamContext {
66+
fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
67+
let value = match self.get_http_request_header("x-envoy-wasm-metric-value") {
68+
Some(value) => value.parse::<i64>().unwrap(),
69+
_ => 0,
70+
};
71+
72+
let metric_type = match self.get_http_request_header("x-envoy-wasm-metric") {
73+
Some(metric_type) => metric_type,
74+
_ => return Action::Continue,
75+
};
76+
77+
match metric_type.as_str() {
78+
"counter" => self.metrics.counter.increment(value),
79+
"gauge" => self.metrics.gauge.record(value.try_into().unwrap()),
80+
"histogram" => self.metrics.histogram.record(value.try_into().unwrap()),
81+
_ => return Action::Continue,
82+
}
83+
84+
Action::Continue
85+
}
86+
}

src/stats.rs

Lines changed: 21 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -16,92 +16,65 @@ use crate::hostcalls;
1616
use crate::traits;
1717
use crate::types;
1818

19+
#[derive(Copy, Clone)]
1920
pub struct Counter {
2021
id: u32,
21-
name: String,
2222
}
2323

2424
impl Counter {
25-
pub fn counter(name: String) -> Counter {
26-
let returned_id = hostcalls::define_metric(types::MetricType::Counter, &name).unwrap();
27-
Counter {
28-
id: returned_id,
29-
name,
30-
}
25+
pub fn new(name: String) -> Counter {
26+
let returned_id = hostcalls::define_metric(types::MetricType::Counter, &name)
27+
.expect("failed to define counter '{}', name");
28+
Counter { id: returned_id }
3129
}
3230
}
3331

3432
impl traits::Metric for Counter {
3533
fn id(&self) -> u32 {
3634
self.id
3735
}
38-
39-
fn name(&self) -> &str {
40-
self.name.as_str()
41-
}
42-
43-
fn record(&self, _: u64) {
44-
// A Counter can only be incremented.
45-
return;
46-
}
4736
}
4837

38+
impl traits::IncrementingMetric for Counter {}
39+
40+
#[derive(Copy, Clone)]
4941
pub struct Gauge {
5042
id: u32,
51-
name: String,
5243
}
5344

5445
impl Gauge {
55-
pub fn gauge(name: String) -> Gauge {
56-
let returned_id = hostcalls::define_metric(types::MetricType::Gauge, &name).unwrap();
57-
Gauge {
58-
id: returned_id,
59-
name,
60-
}
46+
pub fn new(name: String) -> Gauge {
47+
let returned_id = hostcalls::define_metric(types::MetricType::Gauge, &name)
48+
.expect("failed to define gauge '{}', name");
49+
Gauge { id: returned_id }
6150
}
6251
}
6352

6453
impl traits::Metric for Gauge {
6554
fn id(&self) -> u32 {
6655
self.id
6756
}
68-
69-
fn name(&self) -> &str {
70-
self.name.as_str()
71-
}
72-
73-
fn increment(&self, _: i64) {
74-
// A gauge can only be recorded.
75-
return;
76-
}
7757
}
7858

59+
impl traits::RecordingMetric for Gauge {}
60+
61+
#[derive(Copy, Clone)]
7962
pub struct Histogram {
8063
id: u32,
81-
name: String,
8264
}
8365

8466
impl Histogram {
85-
pub fn histogram(name: String) -> Histogram {
86-
let returned_id = hostcalls::define_metric(types::MetricType::Histogram, &name).unwrap();
87-
Histogram {
88-
id: returned_id,
89-
name,
90-
}
67+
pub fn new(name: String) -> Histogram {
68+
let returned_id = hostcalls::define_metric(types::MetricType::Histogram, &name)
69+
.expect("failed to define histogram '{}', name");
70+
Histogram { id: returned_id }
9171
}
9272
}
9373

9474
impl traits::Metric for Histogram {
9575
fn id(&self) -> u32 {
9676
self.id
9777
}
98-
99-
fn name(&self) -> &str {
100-
self.name.as_str()
101-
}
102-
103-
fn increment(&self, _: i64) {
104-
// A Histogram can only be recorded.
105-
return;
106-
}
10778
}
79+
80+
impl traits::RecordingMetric for Histogram {}

0 commit comments

Comments
 (0)