FastAPI gunicorn 添加一个日志记录时间戳

FastAPI gunicorn add a logging timestamp

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

tiangolo/uvicorn-gunicorn-fastapi:python3.7

start.sh 看起来像:

exec gunicorn -k uvicorn.workers.UvicornWorker -c "$GUNICORN_CONF" "$APP_MODULE"

我的 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

好像在gunicorn_conf.py中使用

use_loglevel = os.getenv("LOG_LEVEL", "info")

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

您应该能够使用 uvicorn-gunicorn docker 图像中使用的 access_log_format variable in your gunicorn_conf.py file. You can base your gunicorn_conf.py file this one 修改访问日志的格式。

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

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

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__)

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

如果您真的想编辑 uvicorn 的访问日志格式,我不确定是否有“官方”方法可以这样做。在撰写本文时,它们似乎在 their code:

中具有硬编码格式
            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},
                )

我对打印 x-forwarded-for header 值感兴趣。一个丑陋的解决方法是猴子补丁 uvicorn.protocols.utils.get_client_addr 并从传递给它的 scope 字典中拉出你想要的任何东西。它恰好有请求headers。注意:这可能会产生意想不到的后果,特别是如果 uvicorn 的人将他们的代码更改为使用 get_client_addr 而不是打印值。

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