# 缘起

通常我们的南北流量的链路是从云的 LB 到服务器的 Nginx 集群,为了利用好 lb 自动检测 Nginx 的功能,通常你会打开健康检查,此时,Nginx 的日志当中就会打印大量的健康检查日志,令人不胜其烦。

# 解决

此时,你可以通过如下配置方式,将健康检查的日志过滤掉,通常,健康检查的日志大概如下:

{
  "remote_addr": "1.1.1.1",
  "@timestamp": "2023-10-24T09:44:41+08:00",
  "request_uri": "/webStatus",
  "verb": "GET",
  "httpversion": "HTTP/1.1",
  "response": "200",
  "body_bytes_sent": "0",
  "referrer": "-",
  "user_agent": "clb-healthcheck",
  "http_x_forwarded_for": "-",
  "server_name": "test.eryajf.net",
  "request_time": "0.000",
  "upstream_response_time": "-",
  "upstream_addr": "-",
  "realpath_root": "/usr/local/nginx/html",
  "request_body": "-",
  "nginx_version": "1.14.0",
  "scheme": "http"
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

可以观察到一个特征,所有健康检查的日志的 ua,都是 clb-healthcheck,于是,我们可以在 Nginx 配置中增加如下配置:

    map $http_user_agent $log_ua {
        clb-healthcheck 0;
        default 1;
    }


    access_log /data/log/tmp.log json if=$log_ua;

1
2
3
4
5
6
7

简单说明:此处通过一个 map 来声明一个规则,并通过 0 和 1 表示是否匹配,然后 access_log 中通过 if 判断来应用这个规则。此规则表示如果该条日志中 ua 包括 clb-healthcheck 则不进行记录,否则记录。

可以看到,第一条请求日志正常打印,应用如上规则之后,同样的 ua 的第二条请求则不再输出对应日志,而非此 ua 的第三条请求,日志正常输出打印。

申明

原创文章eryajf,未经授权,严禁转载,侵权必究!此乃文中随机水印,敬请读者谅解。

# 扩展

事实上除了上边基于 ua 的方式之外,Nginx 所有的内置变量 (opens new window)都是支持这么定义的。里边变量比较多,我们常用的变量如下:

$host:访问域名 $uri:请求 uri $status:http 状态码 $request_method:请求方法 $request_completion :请求是否完成 $upstream_addr:反向代理的 upstream $upstream_status:upstream 响应值 $scheme:请求的协议 $http_referer:referer 来源 $http_user_agent:浏览器的 user agent $http_cookie: 本地所有 cookie,也可以打印指定 cookie,如打印 session: $http_session $args 或$arg\_: 请求时的参数

这里摘出几个简单做下示例。

基于域名的规则

map $host $log_host {
    ~nixops.me  1;
    ~nixops1.me 1;
    default 0;
}

server {
    […]
    access_log /var/log/nginx/access.log main;
    access_log /var/log/nginx/nixops.me.log main if=$log_host;
}

1
2
3
4
5
6
7
8
9
10
11

这样 /var/log/nginx/nixops.me.log 里面就只有 nixops.me 和 nixops1.me 这两个域名的日志了。

基于http状态码的规则

map $status $log_status {
    ~^[50] 1;
    404  1;
    default 0;
}

server {
    […]
    access_log /var/log/nginx/access.log main;
    access_log /var/log/nginx/http_error.log main if=$log_status;
}

1
2
3
4
5
6
7
8
9
10
11

这样就输出 502、503、504、404 日志到 /var/log/nginx/http_error.log

基于指定uri的规则

map $uri $log_uri {
    ~*admin 1;
    /api  1;
    default 0;
}

server {
    […]
    access_log /var/log/nginx/access.log main;
    access_log /var/log/nginx/http_uri.log main if=$log_uri;
}

1
2
3
4
5
6
7
8
9
10
11

另一种写法:

server {
    […]
    set $log_uri 0;
    if ( $uri ~ ^/api ) {
        set $log_uri 1;
    }
    access_log /var/log/nginx/access.log main;
    access_log /var/log/nginx/http_uri.log main if=$log_uri;
}

1
2
3
4
5
6
7
8
9

这种不使用 map 的方式可以支持正则表达式。

多个条件

上边的例子都是单项的条件,事实上还可以基于多个条件进行过滤。

使用 map 的方式配置:

map $http_user_agent $log_ua {

    ~*Googlebot 1;
    ~*Baiduspider 1;
    default 0;
}

map $http_cookie $log_cookie {

    ~PHPSESSION 1;
    default "";
}

map$log_ua:$log_cookie” $logging {
“1:1” 1;
default 0;
}

server {

    […]

    access_log /var/log/nginx/access.log main;
    access_log /var/log/nginx/http_multi_conditions.log main if=$logging;
 }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

使用 if 的方式进行过滤:

server {

    […]

    set $logging 0;
    set $logtmp "$log_ua$log_cookie"
    if ( $logtmp = "11" ) {
        set $logging 1;
    }

    access_log /var/log/nginx/access.log main if=$logging;

}

1
2
3
4
5
6
7
8
9
10
11
12
13