From 7745f0adde0924dcb7e9583cfb00176b22103570 Mon Sep 17 00:00:00 2001 From: Caio Ramos Casimiro Date: Mon, 16 Sep 2024 16:50:44 +0100 Subject: [PATCH] feat(prometheus): Proxy-Wasm metrics --- .../kong/prometheus-wasmx-metrics.yml | 4 + kong-3.9.0-0.rockspec | 1 + kong/plugins/prometheus/exporter.lua | 8 +- kong/plugins/prometheus/wasmx.lua | 234 ++++++++++++++ kong/runloop/wasm.lua | 15 + .../26-prometheus/09-wasmx_spec.lua | 287 ++++++++++++++++++ .../proxy_wasm_filters/tests/src/filter.rs | 1 + .../proxy_wasm_filters/tests/src/metrics.rs | 54 ++++ .../proxy_wasm_filters/tests/src/test_http.rs | 23 ++ 9 files changed, 625 insertions(+), 2 deletions(-) create mode 100644 changelog/unreleased/kong/prometheus-wasmx-metrics.yml create mode 100644 kong/plugins/prometheus/wasmx.lua create mode 100644 spec/03-plugins/26-prometheus/09-wasmx_spec.lua create mode 100644 spec/fixtures/proxy_wasm_filters/tests/src/metrics.rs diff --git a/changelog/unreleased/kong/prometheus-wasmx-metrics.yml b/changelog/unreleased/kong/prometheus-wasmx-metrics.yml new file mode 100644 index 000000000000..626716b7ead7 --- /dev/null +++ b/changelog/unreleased/kong/prometheus-wasmx-metrics.yml @@ -0,0 +1,4 @@ +message: | + **Prometheus**: Added support for Proxy-Wasm metrics. +type: feature +scope: Plugin diff --git a/kong-3.9.0-0.rockspec b/kong-3.9.0-0.rockspec index 59645720c5d6..7664a9df8908 100644 --- a/kong-3.9.0-0.rockspec +++ b/kong-3.9.0-0.rockspec @@ -538,6 +538,7 @@ build = { ["kong.plugins.prometheus.prometheus"] = "kong/plugins/prometheus/prometheus.lua", ["kong.plugins.prometheus.serve"] = "kong/plugins/prometheus/serve.lua", ["kong.plugins.prometheus.schema"] = "kong/plugins/prometheus/schema.lua", + ["kong.plugins.prometheus.wasmx"] = "kong/plugins/prometheus/wasmx.lua", ["kong.plugins.session.handler"] = "kong/plugins/session/handler.lua", ["kong.plugins.session.schema"] = "kong/plugins/session/schema.lua", diff --git a/kong/plugins/prometheus/exporter.lua b/kong/plugins/prometheus/exporter.lua index b02b8655cd77..55c1aa637be4 100644 --- a/kong/plugins/prometheus/exporter.lua +++ b/kong/plugins/prometheus/exporter.lua @@ -1,11 +1,14 @@ +local balancer = require "kong.runloop.balancer" +local yield = require("kong.tools.yield").yield +local wasm = require "kong.plugins.prometheus.wasmx" + + local kong = kong local ngx = ngx local get_phase = ngx.get_phase local lower = string.lower local ngx_timer_pending_count = ngx.timer.pending_count local ngx_timer_running_count = ngx.timer.running_count -local balancer = require("kong.runloop.balancer") -local yield = require("kong.tools.yield").yield local get_all_upstreams = balancer.get_all_upstreams if not balancer.get_all_upstreams then -- API changed since after Kong 2.5 get_all_upstreams = require("kong.runloop.balancer.upstreams").get_all_upstreams @@ -517,6 +520,7 @@ local function metric_data(write_fn) -- notify the function if prometheus plugin is enabled, -- so that it can avoid exporting unnecessary metrics if not prometheus:metric_data(write_fn, not IS_PROMETHEUS_ENABLED) + wasm.metrics_data() end local function collect() diff --git a/kong/plugins/prometheus/wasmx.lua b/kong/plugins/prometheus/wasmx.lua new file mode 100644 index 000000000000..ada1d09ded8d --- /dev/null +++ b/kong/plugins/prometheus/wasmx.lua @@ -0,0 +1,234 @@ +local buffer = require "string.buffer" +local wasm = require "kong.runloop.wasm" +local wasmx_shm + + +local pcall = pcall +local str_sub = string.sub +local table_insert = table.insert +local table_sort = table.sort +local buf_new = buffer.new +local ngx_say = ngx.say +local ngx_re_match = ngx.re.match + + +local _M = {} + + +local FLUSH_EVERY = 100 +local GET_METRIC_OPTS = { prefix = false } + + +local metrics_data_buf = buf_new() +local labels_serialization_buf = buf_new() +local sum_lines_buf = buf_new() +local count_lines_buf = buf_new() + + +local function sorted_iter(ctx, i) + i = i + 1 + + local v = ctx.t[ctx.sorted_keys[i]] + + if v ~= nil then + return i, v + end +end + + +local function sorted_pairs(t) + local sorted_keys = {} + + for k, _ in pairs(t) do + table_insert(sorted_keys, k) + end + + table_sort(sorted_keys) + + return sorted_iter, { t = t, sorted_keys = sorted_keys }, 0 +end + +-- +-- Convert a pw_key into a pair of metric name and labels +-- +-- pw_key follows the form `pw::` +-- `` might contain labels, e.g. a_metric_label1="v1"; +-- if it does, the position of the first label corresponds to the end of the +-- metric name and is used to discard labels from . +local function parse_pw_key(pw_key) + local m_name = pw_key + local m_labels = {} + local m_1st_label_pos = #pw_key + + local matches = ngx_re_match(pw_key, [[pw:([\w\.]+):]]) + local f_name = matches[1] + local f_meta = wasm.filter_meta[f_name] or {} + local l_patterns = f_meta.metrics and f_meta.metrics.label_patterns or {} + + local match_ctx = {} + + for _, pair in ipairs(l_patterns) do + matches = ngx_re_match(pw_key, pair.pattern, "", match_ctx) + + if matches then + local l_pos, value = match_ctx.pos - #matches[1], matches[2] + + table_insert(m_labels, { pair.label, value }) + + m_1st_label_pos = (l_pos < m_1st_label_pos) and l_pos or m_1st_label_pos + end + end + + if m_1st_label_pos ~= #pw_key then + -- discarding labels from m_name + m_name = str_sub(pw_key, 1, m_1st_label_pos - 1) + end + + return m_name, m_labels +end + + +-- +-- Parse potential labels stored in the metric key +-- +-- If no labels are present, key is simply the metric name. +local function parse_key(key) + local name = key + local labels + + if #key > 3 and key:sub(0, 3) == "pw:" then + name, labels = parse_pw_key(key) + end + + name = name:gsub(":", "_") + + return name, labels or {} +end + + +local function serialize_labels(labels) + labels_serialization_buf:reset() + + for _, pair in ipairs(labels) do + labels_serialization_buf:putf(',%s="%s"', pair[1], pair[2]) + end + + labels_serialization_buf:skip(1) -- discard leading comma + + return "{" .. labels_serialization_buf:get() .. "}" +end + + +local function serialize_metric(m, buf) + buf:putf("# HELP %s\n# TYPE %s %s", m.name, m.name, m.type) + + if m.type == "histogram" then + sum_lines_buf:reset() + count_lines_buf:reset() + + for _, pair in ipairs(m.labels) do + local count, sum = 0, 0 + local labels, labeled_m = pair[1], pair[2] + local slabels, blabels = "", "{" + + if #labels > 0 then + slabels = serialize_labels(labels) + blabels = slabels:sub(1, #slabels - 1) .. "," + end + + for _, bin in ipairs(labeled_m.value) do + count = count + bin.count + + buf:putf('\n%s%sle="%s"} %s', + m.name, + blabels, + (bin.ub ~= 4294967295 and bin.ub or "+Inf"), + count) + end + + sum = sum + labeled_m.sum + + sum_lines_buf:putf("\n%s_sum%s %s", m.name, slabels, sum) + count_lines_buf:putf("\n%s_count%s %s", m.name, slabels, count) + end + + buf:put(sum_lines_buf:get()) + buf:put(count_lines_buf:get()) + + else + assert(m.type == "gauge" or m.type == "counter", "unknown metric type") + + for _, pair in ipairs(m.labels) do + local labels, labeled_m = pair[1], pair[2] + local slabels = (#labels > 0) and serialize_labels(labels) or "" + + buf:putf("\n%s%s %s", m.name, slabels, labeled_m.value) + end + end + + buf:put("\n") +end + + +local function require_wasmx() + if not wasmx_shm then + local ok, _wasmx_shm = pcall(require, "resty.wasmx.shm") + if ok then + wasmx_shm = _wasmx_shm + end + end +end + + +_M.metrics_data = function() + if not wasm.enabled() then + return + end + + local metrics = {} + local parsed = {} + + -- delayed require of the WasmX module, to ensure it is loaded + -- after ngx_wasm_module.so is loaded. + require_wasmx() + + if not wasmx_shm then + return + end + + wasmx_shm.metrics:lock() + + for key in wasmx_shm.metrics:iterate_keys() do + local pair = { key, wasmx_shm.metrics:get_by_name(key, GET_METRIC_OPTS) } + table_insert(metrics, pair) + end + + wasmx_shm.metrics:unlock() + + -- in WasmX the different labels of a metric are stored as separate metrics; + -- aggregate those separate metrics into a single one. + for _, pair in ipairs(metrics) do + local key = pair[1] + local m = pair[2] + local name, labels = parse_key(key) + + parsed[name] = parsed[name] or { name = name, type = m.type, labels = {} } + + table_insert(parsed[name].labels, { labels, m }) + end + + metrics_data_buf:reset() + + for i, metric_by_label in sorted_pairs(parsed) do + metrics_data_buf:put(serialize_metric(metric_by_label, metrics_data_buf)) + + if i % FLUSH_EVERY == 0 then + ngx_say(metrics_data_buf:get()) + end + end + + ngx_say(metrics_data_buf:get()) +end + + +return _M diff --git a/kong/runloop/wasm.lua b/kong/runloop/wasm.lua index 5a6a5538fadd..9bb9350a5b77 100644 --- a/kong/runloop/wasm.lua +++ b/kong/runloop/wasm.lua @@ -29,6 +29,7 @@ local _M = { ---@class kong.configuration.wasm_filter.meta --- ---@field config_schema kong.db.schema.json.schema_doc|nil +---@field metrics table|nil local uuid = require "kong.tools.uuid" @@ -72,6 +73,20 @@ local FILTER_META_SCHEMA = { type = "object", properties = { config_schema = json_schema.metaschema, + metrics = { + type = "object", + properties = { + label_patterns = { + type = "array", + items = { + properties = { + label = { type = "string" }, + pattern = { type = "string" }, + } + } + } + } + } }, } diff --git a/spec/03-plugins/26-prometheus/09-wasmx_spec.lua b/spec/03-plugins/26-prometheus/09-wasmx_spec.lua new file mode 100644 index 000000000000..d52d27bb5bdc --- /dev/null +++ b/spec/03-plugins/26-prometheus/09-wasmx_spec.lua @@ -0,0 +1,287 @@ +local helpers = require "spec.helpers" +local cjson = require "cjson" + + +local TEST_NAME_HEADER = "X-PW-Test" +local TESTS_FILTER_FILE = helpers.test_conf.wasm_filters_path .. "/tests.wasm" + +local fixtures = { + dns_mock = helpers.dns_mock.new({ + mocks_only = true + }), + http_mock = {}, + stream_mock = {} +} + +fixtures.dns_mock:A({ + name = "mock.io", + address = "127.0.0.1" +}) + +fixtures.dns_mock:A({ + name = "status.io", + address = "127.0.0.1" +}) + + +local function add_service_and_route(bp, name, path) + local service = assert(bp.services:insert({ + name = name, + url = helpers.mock_upstream_url, + })) + + local route = assert(bp.routes:insert({ + name = name .. "-route", + service = { id = service.id }, + paths = { path }, + hosts = { name }, + protocols = { "https" }, + })) + + return service, route +end + + +local function add_filter_to_service(bp, filter_name, service) + local filters = { + { name = filter_name, enabled = true, config = {} }, + } + + assert(bp.filter_chains:insert({ + service = { id = service.id }, filters = filters, + })) +end + + +for _, strategy in helpers.each_strategy() do + describe("Plugin: prometheus (metrics) [#" .. strategy .. "]", function() + local admin_client + + lazy_setup(function() + local filter_dir = helpers.make_temp_dir() + local filter_file = filter_dir .. "/tests.wasm" + local status_api_port = helpers.get_available_port() + + -- copy filter to a custom location to avoid filter metadata collision + assert(helpers.file.copy(TESTS_FILTER_FILE, filter_file)) + assert(helpers.file.write(filter_dir .. "/tests.meta.json", cjson.encode({ + config_schema = { type = "object", properties = {} }, + metrics = { + label_patterns = { + { label = "service", pattern = "(_s_id=([0-9a-z%-]+))" }, + { label = "route", pattern = "(_r_id=([0-9a-z%-]+))" }, + } + } + }))) + + require("kong.runloop.wasm").enable({ + { name = "tests", path = filter_file }, + }) + + local bp = helpers.get_db_utils(strategy, { + "services", "routes", "plugins", "filter_chains", + }) + + local service, _ = add_service_and_route(bp, "mock", "/") + local service2, _ = add_service_and_route(bp, "mock2", "/v2") + + add_service_and_route(bp, "status.io", "/metrics") + + add_filter_to_service(bp, "tests", service) + add_filter_to_service(bp, "tests", service2) + + bp.plugins:insert({ name = "prometheus" }) + + assert(helpers.start_kong({ + nginx_conf = "spec/fixtures/custom_nginx.template", + wasm = true, + wasm_filters_path = filter_dir, + plugins = "bundled,prometheus", + status_listen = '127.0.0.1:' .. status_api_port .. ' ssl', + status_access_log = "logs/status_access.log", + status_error_log = "logs/status_error.log" + }, nil, nil, fixtures)) + + local proxy_client = helpers.proxy_ssl_client() + + local res = proxy_client:get("/", { + headers = { host = "mock", [TEST_NAME_HEADER] = "update_metrics" }, + }) + assert.res_status(200, res) + + res = proxy_client:get("/v2", { + headers = { host = "mock2", [TEST_NAME_HEADER] = "update_metrics" }, + }) + assert.res_status(200, res) + + proxy_client:close() + end) + + lazy_teardown(function() + if admin_client then + admin_client:close() + end + + helpers.stop_kong() + end) + + before_each(function() + admin_client = helpers.admin_client() + end) + + after_each(function() + if admin_client then + admin_client:close() + end + end) + + it("exposes Proxy-Wasm counters", function() + local res = assert(admin_client:send{ + method = "GET", + path = "/metrics" + }) + + local body = assert.res_status(200, res) + local expected_c = '# HELP pw_tests_a_counter\n' + .. '# TYPE pw_tests_a_counter counter\n' + .. 'pw_tests_a_counter 2' + + assert.matches(expected_c, body, nil, true) + end) + + it("exposes Proxy-Wasm labeled counters", function() + local res = assert(admin_client:send{ + method = "GET", + path = "/metrics" + }) + + local body = assert.res_status(200, res) + + local expected_c = '# HELP pw_tests_a_labeled_counter\n' + .. '# TYPE pw_tests_a_labeled_counter counter\n' + .. 'pw_tests_a_labeled_counter{service="mock2",route="mock2-route"} 1\n' + .. 'pw_tests_a_labeled_counter{service="mock",route="mock-route"} 1' + + assert.matches(expected_c, body, nil, true) + end) + + it("exposes Proxy-Wasm gauges", function() + local res = assert(admin_client:send{ + method = "GET", + path = "/metrics" + }) + + local body = assert.res_status(200, res) + + local expected_g = '# HELP pw_tests_a_gauge\n' + .. '# TYPE pw_tests_a_gauge gauge\n' + .. 'pw_tests_a_gauge 1' + + assert.matches(expected_g, body, nil, true) + end) + + it("exposes Proxy-Wasm labeled gauges", function() + local res = assert(admin_client:send{ + method = "GET", + path = "/metrics" + }) + + local body = assert.res_status(200, res) + + local expected_g = '# HELP pw_tests_a_labeled_gauge\n' + .. '# TYPE pw_tests_a_labeled_gauge gauge\n' + .. 'pw_tests_a_labeled_gauge{service="mock2",route="mock2-route"} 1\n' + .. 'pw_tests_a_labeled_gauge{service="mock",route="mock-route"} 1' + + assert.matches(expected_g, body, nil, true) + end) + + it("exposes Proxy-Wasm histograms", function() + local res = assert(admin_client:send{ + method = "GET", + path = "/metrics" + }) + + local body = assert.res_status(200, res) + + local expected_h = '# HELP pw_tests_a_histogram\n' + .. '# TYPE pw_tests_a_histogram histogram\n' + .. 'pw_tests_a_histogram{le="1"} 2\n' + .. 'pw_tests_a_histogram{le="2"} 4\n' + .. 'pw_tests_a_histogram{le="4"} 6\n' + .. 'pw_tests_a_histogram{le="8"} 8\n' + .. 'pw_tests_a_histogram{le="16"} 10\n' + .. 'pw_tests_a_histogram{le="32"} 12\n' + .. 'pw_tests_a_histogram{le="64"} 14\n' + .. 'pw_tests_a_histogram{le="128"} 16\n' + .. 'pw_tests_a_histogram{le="256"} 18\n' + .. 'pw_tests_a_histogram{le="512"} 20\n' + .. 'pw_tests_a_histogram{le="1024"} 22\n' + .. 'pw_tests_a_histogram{le="2048"} 24\n' + .. 'pw_tests_a_histogram{le="4096"} 26\n' + .. 'pw_tests_a_histogram{le="8192"} 28\n' + .. 'pw_tests_a_histogram{le="16384"} 30\n' + .. 'pw_tests_a_histogram{le="32768"} 32\n' + .. 'pw_tests_a_histogram{le="65536"} 34\n' + .. 'pw_tests_a_histogram{le="+Inf"} 36\n' + .. 'pw_tests_a_histogram_sum 524286\n' + .. 'pw_tests_a_histogram_count 36' + + assert.matches(expected_h, body, nil, true) + end) + + it("exposes Proxy-Wasm labeled histograms", function() + local res = assert(admin_client:send{ + method = "GET", + path = "/metrics" + }) + + local body = assert.res_status(200, res) + + local expected_h = '# HELP pw_tests_a_labeled_histogram\n' + .. '# TYPE pw_tests_a_labeled_histogram histogram\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="1"} 1\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="2"} 2\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="4"} 3\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="8"} 4\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="16"} 5\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="32"} 6\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="64"} 7\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="128"} 8\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="256"} 9\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="512"} 10\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="1024"} 11\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="2048"} 12\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="4096"} 13\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="8192"} 14\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="16384"} 15\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="32768"} 16\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="65536"} 17\n' + .. 'pw_tests_a_labeled_histogram{service="mock2",route="mock2-route",le="+Inf"} 18\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="1"} 1\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="2"} 2\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="4"} 3\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="8"} 4\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="16"} 5\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="32"} 6\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="64"} 7\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="128"} 8\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="256"} 9\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="512"} 10\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="1024"} 11\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="2048"} 12\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="4096"} 13\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="8192"} 14\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="16384"} 15\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="32768"} 16\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="65536"} 17\n' + .. 'pw_tests_a_labeled_histogram{service="mock",route="mock-route",le="+Inf"} 18\n' + .. 'pw_tests_a_labeled_histogram_sum{service="mock2",route="mock2-route"} 262143\n' + .. 'pw_tests_a_labeled_histogram_sum{service="mock",route="mock-route"} 262143\n' + .. 'pw_tests_a_labeled_histogram_count{service="mock2",route="mock2-route"} 18\n' + .. 'pw_tests_a_labeled_histogram_count{service="mock",route="mock-route"} 18' + + assert.matches(expected_h, body, nil, true) + end) + end) +end diff --git a/spec/fixtures/proxy_wasm_filters/tests/src/filter.rs b/spec/fixtures/proxy_wasm_filters/tests/src/filter.rs index 9251987e6966..60dbb3f84aaa 100644 --- a/spec/fixtures/proxy_wasm_filters/tests/src/filter.rs +++ b/spec/fixtures/proxy_wasm_filters/tests/src/filter.rs @@ -1,6 +1,7 @@ mod routines; mod test_http; mod types; +mod metrics; use crate::routines::*; use crate::test_http::*; diff --git a/spec/fixtures/proxy_wasm_filters/tests/src/metrics.rs b/spec/fixtures/proxy_wasm_filters/tests/src/metrics.rs new file mode 100644 index 000000000000..dc96d6f2f7de --- /dev/null +++ b/spec/fixtures/proxy_wasm_filters/tests/src/metrics.rs @@ -0,0 +1,54 @@ +use std::collections::HashMap; +use std::cell::RefCell; +use proxy_wasm::hostcalls::{define_metric, increment_metric, record_metric}; +use proxy_wasm::types::{MetricType, Status}; + +thread_local! { + static METRICS: Metrics = Metrics::new(); +} + +struct Metrics { + metrics: RefCell>, +} + +impl Metrics { + fn new() -> Metrics { + Metrics { + metrics: RefCell::new(HashMap::new()), + } + } + + fn get_metric_id(&self, metric_type: MetricType, name: &str) -> Result { + let mut map = self.metrics.borrow_mut(); + + match map.get(name) { + Some(m_id) => Ok(*m_id), + None => { + match define_metric(metric_type, name) { + Ok(m_id) => { + map.insert(name.to_string(), m_id); + + Ok(m_id) + }, + Err(msg) => Err(msg) + } + } + } + } +} + +pub fn define(m_type: MetricType, name: &str) -> Result { + METRICS.with(|metrics| metrics.get_metric_id(m_type, name)) +} + +pub fn increment_counter(name: &str) -> Result<(), Status> { + increment_metric(define(MetricType::Counter, name).unwrap(), 1) +} + +pub fn record_gauge(name: &str, value: u64) -> Result<(), Status> { + record_metric(define(MetricType::Gauge, name).unwrap(), value) +} + +pub fn record_histogram(name: &str, value: u64) -> Result<(), Status> { + record_metric(define(MetricType::Histogram, name).unwrap(), value) +} diff --git a/spec/fixtures/proxy_wasm_filters/tests/src/test_http.rs b/spec/fixtures/proxy_wasm_filters/tests/src/test_http.rs index 38d78be5f2ba..9465eaf90758 100644 --- a/spec/fixtures/proxy_wasm_filters/tests/src/test_http.rs +++ b/spec/fixtures/proxy_wasm_filters/tests/src/test_http.rs @@ -25,6 +25,28 @@ impl TestHttp { self.set_property(vec![ns, prop], value); } + fn update_metrics(&self) { + let base: u64 = 2; + + let s_name = self.get_prop("kong", "service_name"); + let r_name = self.get_prop("kong", "route_name"); + + let labeled_c = format!("a_labeled_counter_s_id={}_r_id={}", s_name, r_name); + let labeled_g = format!("a_labeled_gauge_s_id={}_r_id={}", s_name, r_name); + let labeled_h = format!("a_labeled_histogram_s_id={}_r_id={}", s_name, r_name); + + metrics::increment_counter("a_counter").unwrap(); + metrics::increment_counter(&labeled_c).unwrap(); + + metrics::record_gauge("a_gauge", 1).unwrap(); + metrics::record_gauge(&labeled_g, 1).unwrap(); + + for i in 0..18 { + metrics::record_histogram("a_histogram", base.pow(i)).unwrap(); + metrics::record_histogram(&labeled_h, base.pow(i)).unwrap(); + } + } + fn send_http_dispatch(&mut self, config: TestConfig) -> Action { let mut timeout = Duration::from_secs(0); let mut headers = Vec::new(); @@ -137,6 +159,7 @@ impl TestHttp { return self.send_http_dispatch(config); } + "update_metrics" => self.update_metrics(), _ => (), } }