如何过滤古尼康的原木?

The*_*Lad 2 python logging gunicorn

我有一个带有gunicorn的Flask API。Gunicorn将所有请求记录到我的API中,即

172.17.0.1 - - [19/Sep/2018:13:50:58 +0000] "GET /api/v1/myview HTTP/1.1" 200 16 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"

但是,我想过滤日志以排除某个端点,该端点在几秒钟内都从其他服务调用。

我编写了一个过滤器以排除此端点被记录:

class NoReadyFilter(logging.Filter):
    def filter(self, record):
        return record.getMessage().find('/api/v1/ready') == -1
Run Code Online (Sandbox Code Playgroud)

并且如果我将此过滤器添加到werkzeug记录器并使用Flask开发服务器,则该过滤器将起作用。请求/api/v1/ready将不会出现在日志文件中。但是,我似乎无法将过滤器添加到gunicorn记录器。使用以下代码,请求/api/v1/ready仍然出现:

if __name__ != '__main__':
    gunicorn_logger = logging.getLogger('gunicorn.glogging.Logger')
    gunicorn_logger.setLevel(logging.INFO)
    gunicorn_logger.addFilter(NoReadyFilter())
Run Code Online (Sandbox Code Playgroud)

如何为gunicorn记录器添加过滤器?我尝试gunicorn.error按照此处的建议将其添加到-logger中,但没有帮助。

The*_*Lad 7

我终于找到了一种创建子类的方法

class CustomGunicornLogger(glogging.Logger):

    def setup(self, cfg):
        super().setup(cfg)

        # Add filters to Gunicorn logger
        logger = logging.getLogger("gunicorn.access") 
        logger.addFilter(NoReadyFilter())
Run Code Online (Sandbox Code Playgroud)

继承自guncorn.glogging.Logger。然后,您可以提供此类作为参数gunicorn,例如

gunicorn --logger-class "myproject.CustomGunicornLogger" app
Run Code Online (Sandbox Code Playgroud)


m00*_*000 5

虽然自定义日志记录类可以工作,但对于简单的访问日志过滤器来说可能有点过分了。相反,我会使用 Gunicorn 的on_starting()服务器挂钩向访问记录器添加过滤器。

该钩子可以添加到设置文件中(默认gunicorn.conf.py),因此所有gunicorn 配置都保留在一处。

import logging
import re

wsgi_app = 'myapp.wsgi'
bind = '0.0.0.0:9000'
workers = 5
accesslog = '-'

class RequestPathFilter(logging.Filter):
    def __init__(self, *args, path_re, **kwargs):
        super().__init__(*args, **kwargs)
        self.path_filter = re.compile(path_re)

    def filter(self, record):
        req_path = record.args['U']
        if not self.path_filter.match(req_path):
            return True  # log this entry
        # ... additional conditions can be added here ...
        return False     # do not log this entry

def on_starting(server):
    server.log.access_log.addFilter(RequestPathFilter(path_re=r'^/api/v1/ready$'))
Run Code Online (Sandbox Code Playgroud)

关于此示例实现的一些注释:

  • RequestPathFilter也可以嵌套on_starting()以对外部模块隐藏它。
  • 过滤应用于record.args. 这包含用于构造日志消息的原始值。
  • 对结果record.getMessage()而不是原始值应用过滤是不好的,因为:
    1. Gunicorn 已经完成了构建消息的工作。
    2. 过滤机制可以由客户端操作。这将允许攻击者通过将其用户代理设置为 来隐藏其活动Wget/1.20.1/api/v1/ready (linux-gnu)