FastAPI Gunicorn 添加日志记录时间戳

use*_*186 2 logging gunicorn docker fastapi

我正在使用 docker 运行 FastAPI https://fastapi.tiangolo.com/deployment/

tiangolo/uvicorn-gunicorn-fastapi:python3.7
Run Code Online (Sandbox Code Playgroud)

start.sh 看起来像:

exec gunicorn -k uvicorn.workers.UvicornWorker -c "$GUNICORN_CONF" "$APP_MODULE"
Run Code Online (Sandbox Code Playgroud)

我的 docker 日志看起来没有时间戳:

INFO:     123.123.123.123:48736 - "GET /wp-login.php HTTP/1.0" 404 Not Found
INFO:     123.123.123.123:48808 - "GET /robots.txt HTTP/1.0" 404 Not Found
INFO:     123.123.123.123:48810 - "GET / HTTP/1.0" 200 OK
Run Code Online (Sandbox Code Playgroud)

似乎在gunicorn_conf.py它使用

use_loglevel = os.getenv("LOG_LEVEL", "info")
Run Code Online (Sandbox Code Playgroud)

如何轻松优雅地修改带有时间戳的 INFO 和 ERROR 记录器格式?

tot*_*ack 6

我相信使用 uvicorn/gunicorn/fastapi 组合时该access_log_format选项目前被忽略。但这主要是为了编辑%(message)s日志的一部分。如果您只想添加时间戳,您应该能够覆盖记录器的行为(尽管默认值对我来说有一个时间戳)。

我在定义__init__.pyfastapi 之前将下面的示例放在其中。app

import logging, logging.config

LOG_CONFIG = {
    "version": 1,
    "disable_existing_loggers": True,
    "formatters": {"default": {"format": "%(asctime)s [%(process)s] %(levelname)s: %(message)s"}},
    "handlers": {
        "console": {
            "formatter": "default",
            "class": "logging.StreamHandler",
            "stream": "ext://sys.stdout",
            "level": "INFO",
        }
    },
    "root": {"handlers": ["console"], "level": "INFO"},
    "loggers": {
        "gunicorn": {"propagate": True},
        "gunicorn.access": {"propagate": True},
        "gunicorn.error": {"propagate": True},
        "uvicorn": {"propagate": True},
        "uvicorn.access": {"propagate": True},
        "uvicorn.error": {"propagate": True},
    },
}

logging.config.dictConfig(LOG_CONFIG)
logger = logging.getLogger(__name__)
Run Code Online (Sandbox Code Playgroud)

查看答案以获取日志记录字典配置的一些很好的示例。

如果您确实想编辑 uvicorn 的访问日志格式,我不确定是否有“官方”方法可以做到这一点。截至撰写本文时,他们的代码中似乎有硬编码格式:

            if self.access_log:
                self.access_logger.info(
                    '%s - "%s %s HTTP/%s" %d',
                    get_client_addr(self.scope),
                    self.scope["method"],
                    get_path_with_query_string(self.scope),
                    self.scope["http_version"],
                    status_code,
                    extra={"status_code": status_code, "scope": self.scope},
                )
Run Code Online (Sandbox Code Playgroud)

x-forwarded-for例如,我对打印标题值感兴趣。解决这个问题的一种丑陋的方法是猴子修补并从传递给它的字典uvicorn.protocols.utils.get_client_addr中提取你想要的任何内容。scope它恰好有请求标头。注意:这可能会产生意想不到的后果,特别是如果uvicorn人们更改代码以get_client_addr用于打印值以外的其他用途。

也许有一种方法可以通过使用自定义记录器的自定义工作器类来执行此操作,但我还没有看到这样做。