werkzeug:登录到文件时禁用 bash 颜色

werkzeug: disable bash colors when logging to file

在 Flask 应用程序中,我使用 RotatingFileLogger 将 werkzeug 访问日志记录到文件中,如 this question:

中所示
file_handler_access_log = RotatingFileHandler("access.log",
                                              backupCount=5,
                                              encoding='utf-8')
formatter = logging.Formatter('%(asctime)s %(module)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
file_handler_access_log.setFormatter(formatter)
werkzeug_logger.addHandler(file_handler_access_log)
werkzeug_logger.setLevel(logging.DEBUG)

access.log 文件中,请求如下所示:

2020-10-07 09:43:51 _internal INFO: 127.0.0.1 - - [07/Oct/2020 09:43:51] "[37mGET /api/foo HTTP/1.1[0m" 200 -

我想去掉日志文件中 [37m 这样的颜色代码。

werkzeug documentation 指出:

The development server can optionally highlight the request logs in different colors based on the status code. Install Click to enable this feature.

Click 是一个 Flask 依赖项,所以我无法卸载它。如何禁用彩色日志记录?

好的,你打的是

if click:
    color = click.style

    if code[0] == "1":  # 1xx - Informational
        msg = color(msg, bold=True)
    ...
self.log("info", '"%s" %s %s', msg, code, size)

来源:https://github.com/pallets/werkzeug/blob/ef545f0d0bf28cbad02066b4cb7471bea50a93ee/src/werkzeug/serving.py

不容易阻止这种行为。第二个选项是从消息中删除颜色代码。我会尝试使用 log Filter 来更新消息,例如

import logging

import click
    

class RemoveColorFilter(logging.Filter):
    def filter(self, record):
        if record and record.msg and isinstance(record.msg, str):
            record.msg = click.unstyle(record.msg) 
        return True

remove_color_filter = RemoveColorFilter()
file_handler_access_log.addFilter(remove_color_filter)

以上建议的灵感来自于下面的回答。

我没有测试建议的解决方案。

我采用了我认为更直接的解决方案,即简单地丢弃所有样式参数 - 在您的应用程序启动时就像这样


    old_color = click.style

    def new_color(text, fg=None, bg=None, bold=None, dim=None, underline=None, blink=None, reverse=None, reset=True):
        return old_color(text)

    # replace flask styling with non-colorized styling
    click.style = new_color

这与@jhodges 的想法类似,但更简单 - 它完全删除了所有“点击”模块的样式。

import click
click.style = lambda text, *args, **kwargs: text

编辑:最后,我最终使用过滤器从发送到文件处理程序的日志消息中删除转义序列。因为 werkzeug 将转义序列放在日志消息的 args 列表中,所以这些也需要被剥离。这是基本大纲:

class NoEscape(logging.Filter):
    def __init__(self):
        self.regex = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]')
    def strip_esc(self, s):
        try: # string-like 
            return self.regex.sub('',s)
        except: # non-string-like
            return s
    def filter(self, record: logging.LogRecord) -> int:
        record.msg = self.strip_esc(record.msg)
        if type(record.args) is tuple:
            record.args = tuple(map(self.strip_esc, record.args))
        return 1

希望对您有所帮助!

我自己遇到了这个问题,但其他答案中提供的过滤器解决方案修改了 所有 案例的记录,而我只想删除文件处理程序的样式并将其留给控制台处理程序。

我最终继承了 Formatter 而不是 Filter:

import logging
import click

class AntiColorFormatter(logging.Formatter):
    def format(self, record: logging.LogRecord) -> str:
        return click.unstyle(super().format(record))

然后适合于类似的东西:

logger = logging.getLogger()
FMT = '{asctime} {levelname} {message}'

handler = logging.StreamHandler()
# regular console output gets normal styling
handler.setFormatter(logging.Formatter(FMT, style='{'))
logger.addHandler(handler)

handler = logging.FileHandler('log.log', encoding='utf8')
# file handler gets styling stripped
handler.setFormatter(AntiColorFormatter(FMT, style='{'))
logger.addHandler(handler)