Python3.11
Gunicorn + Flask + logging
Debian 12
我在开发一个小型的 Python Web 应用,选用的是 Gunicorn + Flask 的方案,日志采用了官方自带的 logging 库。
业务及其简单,但接口请求量比较大,日志记录比较多,我看到官方提供了一个logging.handlers.TimedRotatingFileHandler
的日志轮转处理器,就直接用了。
但是过了一段时间,我发现了日志丢失的问题:
假设 Gunicorn 启动了 3 个 worker 进程,进程号分别是 1001 、1002 和 1003 ,一开始启动 Gunicorn 时,3 个进程的日志都能正确的写入到 app.log 中,但是一旦发生了日志轮转,最终只有一个进程(比如 1001 )能够写入到新的 app.log 中,另外的 1002 和 1003 的日志就再也没有写入成功了。
我猜应该是和多进程日志处理和日志轮转相关的问题,轮转的时候,只有一个进程在切换 app.log ,其他进程找不到文件了,日志就丢失了?(我的猜测很粗糙,我不太理解原理)
当然,官方文档也提到了这点:
文档的建议是,使用 SocketHandler 或者 QueueHandler ,总之是单独使用一个进程处理日志。
生产环境下,有什么好的解决方案?
刚刚上面的轮转日志丢失,更加具体的,本质的原理是什么?
日志配置文件 logging.yaml 如下:
version:
1
formatters:
brief:
format: '%(asctime)s - %(levelname)s - %(name)s - %(message)s'
detail:
format: '%(asctime)s - %(levelname)s - %(process)d - %(processName)s - %(name)s - %(filename)s - %(funcName)s - %(message)s'
handlers:
console_handler:
class: logging.StreamHandler
level: DEBUG
formatter: brief
stream: ext://sys.stdout
info_handler:
class: logging.handlers.TimedRotatingFileHandler
level: INFO
formatter: detail
filename: logs/app.log
when: midnight
backupCount: 2
encoding: utf-8
error_handler:
class: logging.handlers.TimedRotatingFileHandler
level: ERROR
formatter: detail
filename: logs/error.log
when: midnight
backupCount: 2
encoding: utf-8
loggers:
study-flask:
level: DEBUG
handlers: [console_handler, info_handler, error_handler]
propagate: False
root:
level: DEBUG
handlers: [console_handler]
app.py 中关于日志配置的代码:
def log_config(log_config_file):
dict_config = yaml.load(
open(log_config_file, encoding='utf-8'),
Loader=yaml.FullLoader
)
Path.mkdir(Path.cwd().joinpath("logs"), parents=True, exist_ok=True)
logging.config.dictConfig(dict_config)
def create_app(config_mode):
app = Flask(__name__)
log_config('./logging.yaml')
# ... 省略其他代码
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.