lua脚本
建目录custom-latency,编辑文件,打zip包为
Lua
file schema.lua
local typedefs = require "kong.db.schema.typedefs"
return {
name = "custom-latency",
fields = {
{ consumer = typedefs.no_consumer },
{ protocols = typedefs.protocols_http },
{ config = { type = "record", fields = {} } },
},
}
file handler.lua
-- custom-latency 插件
-- 在 /metrics 上追加带 uri/method/status/service 标签的延迟与请求数指标
-- 复用 kong.plugins.prometheus.exporter 的 prometheus 实例(Kong 2.5.1 实测 API)
local kong_prometheus = require "kong.plugins.prometheus.exporter"
local CustomLatency = {
PRIORITY = 899,
VERSION = "1.0.0",
}
local pm
local uri_histogram
local uri_counter
-- 将路径中的数字段归一化为 :id,避免同类型接口因参数不同产生过多标签
-- 注意:ngx.re.* 使用 PCRE 正则,数字需用 \d 而非 Lua pattern 的 %d
local function normalize_uri(uri)
return ngx.re.gsub(uri or "", "/\\d+", "/:id", "jo")
end
-- 取当前请求命中的 service 名({env}-{service} 命名,前缀即环境),匹配不到返回空串
local function get_service_name()
local srvc = kong.router.get_service()
if not srvc then
return ""
end
return srvc.name or ""
end
function CustomLatency:init_worker()
pm = kong_prometheus.get_prometheus()
if not pm then
ngx.log(ngx.ERR, "[custom-latency] prometheus exporter not initialized")
return
end
-- 注意:exporter 会自动加 "kong_" 前缀,这里不能再带前缀
uri_histogram = pm:histogram(
"uri_latency_ms",
"Request latency in milliseconds, labeled by uri, method, status and service",
{ "uri", "method", "status", "service" },
{ 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 30000 }
)
uri_counter = pm:counter(
"uri_requests_total",
"Total number of requests, labeled by uri, method, status and service",
{ "uri", "method", "status", "service" }
)
end
function CustomLatency:log()
if not pm or not uri_histogram or not uri_counter then
return
end
local latency_ms = (ngx.var.request_time or 0) * 1000
local uri = normalize_uri(kong.request.get_path() or "")
local method = kong.request.get_method() or ""
local status = tostring(ngx.status or 0)
local service = get_service_name()
local labels = { uri, method, status, service }
uri_histogram:observe(latency_ms, labels)
uri_counter:inc(1, labels)
end
return CustomLatency
prometheus 配置
bash
- job_name: 'kong'
static_configs:
- targets: ['127.0.0.1:2001']
labels:
instance_service_name: kong
Grafana 配置
bash
{
"annotations": {
"list": []
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
"id": null,
"links": [],
"panels": [
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": {
"defaults": {
"color": { "mode": "palette-classic" },
"custom": {
"drawStyle": "line",
"fillOpacity": 8,
"lineInterpolation": "smooth",
"lineWidth": 1,
"showPoints": "never",
"spanNulls": true
},
"min": 0,
"unit": "reqps"
},
"overrides": []
},
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 },
"id": 1,
"options": {
"legend": { "calcs": ["lastNotNull", "max"], "displayMode": "table", "placement": "bottom" },
"tooltip": { "mode": "multi", "sort": "desc" }
},
"targets": [
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"expr": "sum by (uri, method) (rate(kong_uri_requests_total{method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval]))",
"legendFormat": "{{method}} {{uri}}",
"refId": "A"
}
],
"title": "接口请求 QPS",
"type": "timeseries"
},
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": {
"defaults": {
"color": { "mode": "palette-classic" },
"custom": {
"drawStyle": "line",
"fillOpacity": 8,
"lineInterpolation": "smooth",
"lineWidth": 2,
"showPoints": "never",
"spanNulls": true
},
"min": 0,
"unit": "ms"
},
"overrides": []
},
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 },
"id": 2,
"options": {
"legend": { "calcs": ["lastNotNull", "max"], "displayMode": "table", "placement": "bottom" },
"tooltip": { "mode": "multi", "sort": "desc" }
},
"targets": [
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"expr": "histogram_quantile(0.95, sum by (uri, le) (rate(kong_uri_latency_ms_bucket{method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval])))",
"legendFormat": "{{uri}}",
"refId": "A"
}
],
"title": "接口 P95 响应时间",
"type": "timeseries"
},
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": {
"defaults": {
"color": { "mode": "palette-classic" },
"custom": {
"drawStyle": "line",
"fillOpacity": 8,
"lineInterpolation": "smooth",
"lineWidth": 1,
"showPoints": "never",
"spanNulls": true
},
"min": 0,
"unit": "ms"
},
"overrides": []
},
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 9 },
"id": 4,
"options": {
"legend": { "calcs": ["lastNotNull", "max"], "displayMode": "table", "placement": "bottom" },
"tooltip": { "mode": "multi", "sort": "desc" }
},
"targets": [
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"expr": "sum by (uri) (rate(kong_uri_latency_ms_sum{method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval])) / sum by (uri) (rate(kong_uri_latency_ms_count{method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval]))",
"legendFormat": "avg {{uri}}",
"refId": "A"
}
],
"title": "接口平均响应时间",
"type": "timeseries"
},
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": {
"defaults": {
"color": { "mode": "continuous-reds" },
"custom": {
"drawStyle": "line",
"fillOpacity": 8,
"lineInterpolation": "smooth",
"lineWidth": 1,
"showPoints": "never",
"spanNulls": true
},
"min": 0,
"unit": "percent"
},
"overrides": []
},
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 9 },
"id": 5,
"options": {
"legend": { "calcs": ["lastNotNull", "max"], "displayMode": "table", "placement": "bottom" },
"tooltip": { "mode": "multi", "sort": "desc" }
},
"targets": [
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"expr": "100 * sum by (uri) (rate(kong_uri_requests_total{status=~\"5..\", method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval])) / clamp_min(sum by (uri) (rate(kong_uri_requests_total{method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval])), 0.0001)",
"legendFormat": "{{uri}}",
"refId": "A"
}
],
"title": "接口 5xx 错误率",
"type": "timeseries"
},
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": {
"defaults": {
"color": { "mode": "palette-classic" },
"unit": "reqps"
},
"overrides": []
},
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 17 },
"id": 6,
"options": {
"displayMode": "gradient",
"orientation": "horizontal",
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
"showUnfilled": true
},
"targets": [
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"expr": "topk(10, sum by (uri) (rate(kong_uri_requests_total{method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval])))",
"legendFormat": "{{uri}}",
"refId": "A"
}
],
"title": "请求量 Top10",
"type": "bargauge"
},
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"fieldConfig": {
"defaults": {
"color": { "mode": "continuous-BlYlRd" },
"unit": "ms"
},
"overrides": []
},
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 25 },
"id": 7,
"options": {
"displayMode": "gradient",
"orientation": "horizontal",
"reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
"showUnfilled": true
},
"targets": [
{
"datasource": { "type": "prometheus", "uid": "prometheus" },
"expr": "topk(10, histogram_quantile(0.95, sum by (uri, le) (rate(kong_uri_latency_ms_bucket{method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval]))))",
"legendFormat": "{{uri}}",
"refId": "A"
}
],
"title": "P95 耗时 Top10(最慢接口)",
"type": "bargauge"
}
],
"refresh": "10s",
"schemaVersion": 39,
"tags": ["kong"],
"templating": {
"list": [
{
"current": {
"text": "dev-current",
"value": "dev-current",
"selected": true
},
"datasource": { "type": "prometheus", "uid": "prometheus" },
"definition": "label_values(kong_uri_requests_total, service)",
"includeAll": true,
"allValue": ".*",
"multi": false,
"name": "env",
"label": "环境",
"options": [],
"query": "label_values(kong_uri_requests_total, service)",
"regex": "/(.*)-[a-z0-9]+$/",
"refresh": 1,
"type": "query"
},
{
"current": { "text": "All", "value": "$__all", "selected": true },
"includeAll": true,
"allValue": ".*",
"multi": false,
"name": "method",
"label": "方法",
"options": [
{ "text": "All", "value": "$__all", "selected": true },
{ "text": "GET", "value": "GET", "selected": false },
{ "text": "POST", "value": "POST", "selected": false }
],
"query": "GET,POST",
"refresh": 1,
"type": "custom"
},
{
"current": { "text": "All", "value": "$__all" },
"datasource": { "type": "prometheus", "uid": "prometheus" },
"definition": "label_values(kong_uri_requests_total, uri)",
"includeAll": true,
"allValue": ".*",
"multi": true,
"name": "uri",
"label": "接口路径",
"options": [],
"query": "label_values(kong_uri_requests_total, uri)",
"refresh": 1,
"type": "query"
}
]
},
"time": { "from": "now-1h", "to": "now" },
"timepicker": { "refresh_intervals": ["5s", "10s", "30s", "1m", "5m"] },
"timezone": "browser",
"title": "Kong 接口响应时间监控(按环境)",
"uid": "kong-uri-metrics-cloud",
"version": 1
}