我有一个基于 Flask 的应用程序。当我在本地运行它时,我从命令行运行它,但是当我部署它时,我使用带有多个工作人员的gunicorn来启动它。
我想使用
logging
模块来记录到文件。我为此找到的文档是here和here。
当我的应用程序可能使用gunicorn启动时,我对使用日志记录的正确方法感到困惑。该文档解决了线程问题,但假设我可以控制主进程。困惑点:
logger = logging.getLogger('myapp')
会在不同的gunicorn工作线程中返回相同的记录器对象吗?
如果我将日志记录
FileHandler
附加到记录器以便记录到文件,如何避免在不同的工作人员中多次执行此操作?
我的理解(可能是错误的)是,如果我只是调用
logger.setLevel(logging.DEBUG)
,这将通过根记录器发送消息,根记录器可能具有更高的默认日志记录级别,并且可能会忽略调试消息,因此我also需要调用logging.basicConfig(logging.DEBUG)
为了让我的调试消息能够通过。但文档说不要从线程中调用 logging.basicConfig()
。使用gunicorn时如何正确设置根日志记录级别?还是我不需要?
这是我典型的 Flask/Gunicorn 设置。请注意,gunicorn 是通过主管运行的。
wsgi_web.py。注意
ProxyFix
从 Nginx 获取客户端的真实 IP 地址。
from werkzeug.contrib.fixers import ProxyFix
from app import create_app
import logging
gunicorn_logger = logging.getLogger('gunicorn.error')
application = create_app(logger_override=gunicorn_logger)
application.wsgi_app = ProxyFix(application.wsgi_app, num_proxies=1)
编辑 2020 年 2 月,对于较新版本的 werkzeug,请使用以下内容并根据需要将参数调整为
ProxyFix
:
from werkzeug.middleware.proxy_fix import ProxyFix
from app import create_app
import logging
gunicorn_logger = logging.getLogger('gunicorn.error')
application = create_app(logger_override=gunicorn_logger)
application.wsgi_app = ProxyFix(application.wsgi_app, x_for=1, x_host=1)
Flask应用工厂
create_app
def create_app(logger_override=None):
app = Flask(__name__)
if logger_override:
# working solely with the flask logger
app.logger.handlers = logger_override.handlers
app.logger.setLevel(logger_override.level)
# OR, working with multiple loggers
# for logger in (app.logger, logging.getLogger('sqlalchemy')):
# logger.handlers = logger_override.handlers
# logger.setLevel(logger_override.level)
# more
return app
supervisor conf 中的 Gunicorn 命令(第 4 行),请注意,在此实例中,
--log-level
参数已设置为 info
。注意 X-REAL-IP
已传递到访问 --access-logformat
[program:web]
directory = /home/paul/www/example
environment = APP_SETTINGS="app.config.ProductionConfig"
command = /home/paul/.virtualenvs/example/bin/gunicorn wsgi_web:application -b localhost:8000 --workers 3 --worker-class gevent --keep-alive 10 --log-level info --access-logfile /home/paul/www/logs/admin.gunicorn.access.log --error-logfile /home/paul/www/logs/admin.gunicorn.error.log --access-logformat '%%({X-REAL-IP}i)s %%(l)s %%(u)s %%(t)s "%%(r)s" %%(s)s %%(b)s "%%(f)s" "%%(a)s"'
user = paul
autostart=true
autorestart=true
每个工作人员都是一个独立的进程,拥有自己的内存,因此您无法在不同的工作人员之间真正共享相同的记录器。
您的代码在这些工作人员内部运行,因为主进程只关心管理工作人员。
主进程是一个简单的循环,用于监听各种进程 发出信号并做出相应反应。它管理正在运行的工人列表 通过监听 TTIN、TTOU 和 CHLD 等信号。 TTIN 和 TTOU 告诉 master增加或减少正在运行的worker的数量。
Gunicorn 本身有两种主要的运行模式
所以这与线程不同,这是多处理。
但是从 Gunicorn 19 开始,可以使用线程选项来处理请求 多线程。使用线程假定使用 gthread 工作线程。
考虑到这一点,日志记录代码将编写一次,并在每次创建新工作人员时调用多次。您可以使用 Singelton 模式来确保同一工作线程内使用相同的记录器实例。
要配置记录器本身,您只需遵循设置根记录器级别和不同记录器级别的正常过程即可。
basicConfig() 如果根处理程序已经设置,则不会影响它:
如果根记录器已为其配置了处理程序,则此函数不会执行任何操作。
要显式设置 root 的级别,请执行以下操作
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(name)
然后可以在处理程序或记录器级别上设置级别。
handler = logging.handlers.TimedRotatingFileHandler(log_path, when='midnight', backupCount=30)
handler.setLevel(min_level)
您可以检查此类似答案以获取记录相关详细信息 设置日志记录级别
更多资源:
默认情况下,Gunicorn 仅配置
gunicorn.error
记录器。您可以使用 logconfig_dict
选项或其等效项 来更改此设置。
创建文件
gunicorn.conf.py
:
logconfig_dict = {
"root": {"handlers": ["error_console"], "level": "INFO"},
"loggers": {},
}
gunicorn.glogging.CONFIG_DEFAULTS
中配置的默认处理程序。 loggers
必须指定为空字典,以避免在加载logconfig_dict
之前覆盖gunicorn的记录器。
如果该文件位于当前工作目录中,Gunicorn 将自动加载该文件。或者,使用
-c /path/to/gunicorn.conf.py
选择配置文件。
如果您想避免创建单独的配置文件,请尝试以下命令行参数:
gunicorn --log-config-json <(echo '{"root": {"handlers": ["error_console"], "level": "INFO"}, "loggers": {}}')
您需要空
loggers
键以避免来自 Gunicorn 本身的重复日志消息。