Nginx 条件日志报 unknown log format "if=$loggable"
Nginx access_log 把 if=$loggable 当作未知格式时,在条件前补齐日志格式名;再核对 map 完整匹配、默认值和日志作用范围,避免配置通过却过滤错误。
给访问日志加上过滤条件后,nginx -t 可能直接报错:
nginx: [emerg] unknown log format "if=$loggable" in /etc/nginx/nginx.conf:55
nginx: configuration file /etc/nginx/nginx.conf test failed
如果出错行是下面这样,缺少的是日志格式名:
access_log /var/log/nginx/access.log if=$loggable;
Nginx 把路径后面的第一个参数当成格式名,于是尝试寻找名为 if=$loggable 的日志格式,还没走到条件求值就失败了。这一步先修正参数位置,再检查变量。
本文针对 HTTP 模块的 access_log,不适用于 error_log 或 stream 模块。条件日志的 if 参数从 Nginx 1.7.0 开始提供;下面的配置检查命令按 Linux 上直接管理 Nginx 的环境编写。容器、面板或发行版服务管理流程中,应使用实际运行的二进制和相同配置参数。
在条件前明确写出格式
使用内置格式时,改为:
access_log /var/log/nginx/access.log combined if=$loggable;
combined 是 HTTP 日志模块预定义的格式,无须重新定义。只写日志路径时,Nginx 可以默认使用它;但在后面添加 if= 等参数时,必须把格式位置补出来。给 if=$loggable 加引号不会改变参数位置,也不能解决此错误。
如果原来采用自定义格式,应保留那个格式名,不要为了消除报错一律换成 combined。例如已有名为 app_json 的格式,就在日志路径后使用 app_json,再写条件。切换格式可能破坏下游日志解析。log_format 的定义属于 http 上下文;若改后报的是未知 app_json,要检查该定义是否在使用之前读入,而不是继续改条件变量。
条件通过后,还要确认过滤规则真的匹配
下面是放在已有 http 块中的配置片段。$loggable 值为 0 或空字符串时不记录,其余值会记录:
map $http_user_agent $loggable {
default 1;
~*^ExampleMonitor/ 0;
}
access_log /var/log/nginx/access.log combined if=$loggable;
这段示例会排除 User-Agent 以 ExampleMonitor/ 开头的请求,且不区分大小写。ExampleMonitor/ 是演示前缀,应替换成已核对的监控请求标识。配置片段没有包含现有站点、证书和监听设置,不要拿它覆盖整份 nginx.conf,也不要在已经处于 http 上下文的 include 文件里再包一层 http。
map 中的普通字符串按完整字符串匹配,并且不区分大小写。例如写 pingdom 0;,不会匹配 Pingdom.com_bot_version_1.4_... 这样的长字符串;差别在于完整值不同,不能简单归因于大小写。要按前缀或部分内容匹配,需要适当的正则表达式:~ 区分大小写,~* 不区分大小写。范围应尽量与实际监控标识一致,避免把包含相同单词的普通请求也排除。
访问日志的 if= 表达的是“是否记录”,不是“是否排除”。变量即使叫 $excluded_ua,返回 1 仍会记录;不要仅凭变量名猜它的含义。保留明确的 default 1,能让未匹配的普通流量继续被记录。省略 default 时,未匹配结果默认为空字符串,可能把本来需要保留的请求一并过滤掉。
User-Agent 由客户端提供,这种过滤不能用于认证或可信身份判断。若访问日志还承担审计和安全排查职责,应先评估是否允许按它过滤;减少监控日志也会减少可追溯记录。
用实际配置检查,再平滑加载
先保存原配置和相关 include 文件,确认要修改的日志目标。查看版本并检查配置:
nginx -v
nginx -t -c /etc/nginx/nginx.conf
路径是示例,使用非默认前缀或额外启动参数时要保持一致。检查进程需要有读取配置及打开相关文件的权限。-t 不仅检查语法,也尝试打开配置引用的文件;若后续报日志路径或权限错误,应按新的错误处理,不要用放宽整个日志目录权限的方式掩盖问题。
需要确认 include 文件和日志指令时,可以在受控终端查看完整配置:
nginx -T -c /etc/nginx/nginx.conf
-T 从 1.9.2 开始提供,也会执行配置检查;更老版本需直接核对入口文件及 include 链。完整输出可能含敏感配置,不要原样放进公共工单。
只在检查成功后,才通过当前实例原有的管理方式重新加载。直接管理且配置、前缀、PID 位置均与运行实例一致时,可使用:
nginx -s reload -c /etc/nginx/nginx.conf
如果实例由服务管理器或面板维护,沿用该实例的重载入口,避免把信号发给另一套 Nginx。收到重载请求不等于新配置已经生效;继续查看错误日志。主进程应用新配置失败时会保留旧配置运行,因此网站仍可访问也不能证明新过滤规则生效。
同时验证“该记录”和“不该记录”的请求
在可控环境对同一个站点发起两类新请求:一类使用普通 User-Agent,另一类使用刚配置的监控前缀。给它们使用不同、可辨认的测试路径或查询标记,再查看对应访问日志。
普通请求应出现,监控请求应在这条条件日志中缺席。不要仅用“没看到日志”判断成功:请求可能没到这个实例,也可能被另一层代理处理。如果启用了日志缓冲,还需要按配置的写出时机等待,再做判断。
配置通过但目标文件仍出现监控请求时,检查同一层是否有其他 access_log 写向同一个文件,以及具体 server、location 是否重新声明了日志。子层有自己的日志配置时,不会继续沿用父层那一组日志设置。请求最终在哪个 location 结束处理,也会影响采用哪份日志配置,内部跳转后尤其需要核对。
排查结束后移除临时诊断配置。若过滤范围不符合预期,恢复备份中的日志指令及 map,再次检查并重载;不要通过 access_log off 把全部日志关掉来作为修复。被过滤掉的历史请求不会因为回退而自动补写,验证阶段就应控制过滤范围和持续时间。


