lua脚本
建目录custom-latency,编辑文件,打zip包为
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 CustomLatencyprometheus 配置
- job_name: 'kong' static_configs: - targets: ['127.0.0.1:2001'] labels: instance_service_name: kongGrafana 配置
{ "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 }