Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(prometheus) add wasmx metrics #13681

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/unreleased/kong/prometheus-wasmx-metrics.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
message: Expose WasmX metrics as part of the Prometheus plugin
type: feature
scope: Plugin
8 changes: 6 additions & 2 deletions kong/plugins/prometheus/exporter.lua
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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.metric_data()
end

local function collect()
Expand Down
165 changes: 165 additions & 0 deletions kong/plugins/prometheus/wasmx.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
local buffer = require "string.buffer"
local json_safe = require "cjson.safe"
local wasm = require "kong.runloop.wasm"
local wasmx_shm = require "resty.wasmx.shm"


local fmt = string.format
local str_match = string.match
local str_find = string.find
local str_sub = string.sub
local buf_new = buffer.new
local ngx_say = ngx.say
local table_insert = table.insert


local _M = {}


local function parse_key(key)
local name
local labels = {}

local header = {
pw = "pw.", -- proxy-wasm metrics
lua = "lua.", -- lua land metrics
wa = "wa." -- internal ngx_wasm_module metrics
}

local is_pw = #key > #header.pw and key:sub(0, #header.pw) == header.pw
local is_lua = #key > #header.lua and key:sub(0, #header.lua) == header.lua
local is_wa = #key > #header.wa and key:sub(0, #header.wa) == header.wa

local hs = is_lua and #header.lua or #header.pw -- wa's size == pw's size

if is_lua or is_wa then
name = key
-- label support NYI

elseif is_pw then
local second_dot_pos, _ = str_find(key, "%.", hs + 1)
local ns = str_sub(key, hs + 1, second_dot_pos - 1)

local filter_config = json_safe.decode(wasm.filters_by_name[ns].config)
local patterns = filter_config and filter_config.metrics.label_patterns

local first_match = #key

if patterns then
for _, pair in ipairs(patterns) do
local lkv, lv = str_match(key, pair.pattern)
if lkv then
local lk = str_sub(lkv, 0, str_find(lkv, "="))
local lk_start, _ = str_find(key, lk)

first_match = (lk_start < first_match) and lk_start or first_match

table_insert(labels, { pair.label, lv })
end
end

name = str_sub(key, 0, first_match - 1)

else
name = key
end
else
-- unreachable
error("unknown metric prefix: " .. key, 2)
end

name = name:gsub("%.", "_")

return name, labels
end


local function serialize_labels(labels)
local buf = buf_new()

for _, pair in ipairs(labels) do
buf:put(fmt('%s="%s",', pair[1], pair[2]))
end

local slabels = buf:get()

if #slabels > 0 then
return slabels:sub(0, #slabels - 1) -- discard trailing comma
end

return slabels
end


local function serialize_metric(m)
local buf = buf_new()

buf:put(fmt("# HELP %s\n# TYPE %s %s", m.name, m.name, m.type))

for _, pair in ipairs(m.labels) do
local labels = pair[1]
local labeled_m = pair[2]
local slabels = serialize_labels(labels)

if m.type == "counter" or m.type == "gauge" then
if #slabels > 0 then
buf:put(fmt("\n%s{%s} %s", m.name, slabels, labeled_m.value))
else
buf:put(fmt("\n%s %s", m.name, labeled_m.value))
end

elseif m.type == "histogram" then
for _, bin in ipairs(labeled_m.value) do
local ubl = fmt('le="%s"', bin.ub)

slabels = (#slabels > 0) and (slabels .. "," .. ubl) or ubl

buf:put(fmt("\n%s{%s} %s", m.name, slabels, bin.count))
end
end
end

buf:put("\n")

return buf:get()
end


_M.metric_data = function()
local i = 0
local flush_after = 50
local metrics = {}
local parsed = {}
local buf = buf_new()

wasmx_shm.metrics:lock()

for key in wasmx_shm.metrics:iterate_keys() do
metrics[key] = wasmx_shm.metrics:get_by_name(key, { prefix = false })
end

wasmx_shm.metrics:unlock()

for key, m in pairs(metrics) do
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

for _, metric_by_label in pairs(parsed) do
buf:put(serialize_metric(metric_by_label))

i = i + 1

if i % flush_after == 0 then
ngx_say(buf:get())
end
end

ngx_say(buf:get())
end


return _M
16 changes: 16 additions & 0 deletions kong/runloop/wasm.lua
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,8 @@ local function rebuild_state(db, version, old_state)
if filter.config ~= nil and type(filter.config) ~= "string" then
filter.config = cjson_encode(filter.config)
end

_M.filters_by_name[filter.name].config = filter.config
end
end

Expand Down Expand Up @@ -778,6 +780,13 @@ local function register_property_handlers()
return ok, value, const
end)

properties.add_getter("kong.route_name", function(_, _, ctx)
local value = ctx.route and ctx.route.name
local ok = value ~= nil
local const = ok
return ok, value, const
end)

properties.add_getter("kong.service.response.status", function(kong)
return true, kong.service.response.get_status(), false
end)
Expand All @@ -789,6 +798,13 @@ local function register_property_handlers()
return ok, value, const
end)

properties.add_getter("kong.service_name", function(_, _, ctx)
local value = ctx.service and ctx.service.name
local ok = value ~= nil
local const = ok
return ok, value, const
end)

properties.add_getter("kong.version", function(kong)
return true, kong.version, true
end)
Expand Down
Loading
Loading