nginx 的 map 指令不能直接触发告警,仅能将 $request_time 映射为 alert_level 字符串,再通过自定义日志格式输出该字段,由 elk、prometheus 或 loki 等下游系统依据该字段实现高危告警。

Nginx 的 map 指令本身不能直接触发告警,它只能做变量映射;真正的“标记为高危告警级别”需结合日志格式、日志采集与后端告警系统(如 ELK + Watcher、Prometheus + Alertmanager)协同完成。
核心思路是:用 map 将 $request_time 映射为一个可识别的告警等级字段(如 alert_level),再通过日志输出该字段,由日志分析系统依据该字段自动触发高危告警。
1. 定义基于响应时长的告警等级映射
在 `http` 块中使用 `map`,将 `$request_time`(单位:秒,精度 3 位小数)映射为字符串等级:map $request_time $alert_level {
~^[0-9]*\.[0-9]{0,2}0$ "normal"; # ≤ 0.1s(含 0.100)
~^[0-9]*\.[0-9]{0,2}[1-9]$ "warn"; # > 0.1s 且 ≤ 1.0s
default "critical"; # > 1.0s(含 1.001 及以上)
}
更推荐按业务性能预算精确控制(例如预算 800ms):
map $request_time $alert_level {
# 小于等于 0.8 秒 → normal
0.000 "" ;
0.800 "" ;
# 大于 0.8 秒 → critical(注意:map 匹配是“等于”或“正则”,所以用范围兜底)
default "critical";
}
但 map 不支持区间语法,稳妥做法是用正则覆盖常见范围:
map $request_time $alert_level {
~^([0-7][0-9]{2}|[0-9]{1,2})\.[0-9]{3}$ "normal"; # 0.000–0.799s
~^([8-9][0-9]{2}|1[0-9]{3}|[2-9][0-9]{3,})\.[0-9]{3}$ "critical"; # ≥ 0.800s(含 0.800、0.801…)
default "critical";
}
⚠️ 注意:$request_time 是浮点字符串(如 "0.805"),正则需匹配完整字符串;建议统一用 ~^0\.([8-9][0-9]{2}|[1-9][0-9]{3,})$ 等更严谨写法,或改用 if + set(不推荐在 location 外用)——但 map 是唯一安全、高效、可全局复用的方式。
2. 在 log_format 中嵌入告警等级字段
定义带 `$alert_level` 的自定义日志格式:log_format alert_log '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '$request_time $upstream_response_time "$http_referer" "$http_user_agent" ' 'alert_level="$alert_level"';
然后在 access_log 中启用:
access_log /var/log/nginx/access_alert.log alert_log;
这样每条日志末尾会输出类似:alert_level="critical"
3. 日志侧实现自动化告警
- **ELK 方案**:Logstash 或 Filebeat 解析日志,提取 `alert_level: "critical"` 字段;Kibana 创建告警规则(如:过去 5 分钟 `alert_level=critical` 出现 ≥ 10 次)。 - **Prometheus + nginx-vts-exporter 或 nginx-lua-prometheus**:暴露 `nginx_request_duration_seconds_bucket{le="0.8"}` 指标,配合 PromQL 监控超阈值比例,触发 Alertmanager 告警。 - **轻量级方案(如 Grafana Loki)**:用 LogQL 查询 `|~ "alert_level=\"critical\""`,设置告警条件。关键点:map 只负责生成可结构化识别的标记字段,告警动作必须由下游可观测性系统执行。
4. 补充增强:关联异常路径与高危等级
若需对特定路径(如 `/api/pay`, `/search`)叠加更严格阈值,可组合 `$uri`:map $request_time $alert_level {
default "";
}
# 先按路径分组,再按耗时分级(需嵌套 map 或用复合 key)
map $uri$request_time $alert_level {
~^/api/pay.*0\.([0-9]{3})$ "normal";
~^/api/pay.*0\.([8-9][0-9]{2}|[1-9][0-9]{3})$ "critical";
~^/search.*0\.([0-9]{3})$ "normal";
~^/search.*0\.([5-9][0-9]{2}|[1-9][0-9]{3})$ "critical";
default "normal";
}
或更清晰地拆解为两级 map(推荐):
map $uri $path_budget {
/api/pay 0.3;
/search 0.5;
default 0.8;
}
map $request_time $alert_level {
default "normal";
}
# 注意:map 不支持数值比较,需用第三方模块(如 nginx-module-vts)或 Lua 实现动态判断
# 所以生产环境建议用 lua-resty-limit-traffic + ngx.log(ngx.ERR, ...) 打点,或交由日志系统做数值运算
因此,纯 OpenResty 场景下,更灵活的做法是用 access_by_lua_block 做运行时判断并设置 header 或 log tag,但 map 仍是零成本、零性能损耗的首选前置标记手段。
不复杂但容易忽略:map 的值是字符串,务必确保日志解析器能准确提取该字段,避免空格或引号转义问题。











