pyzog:uWSGI logging rotate 的终极方案
文章目录
在上一篇文章 Flask+uWSGI logging rotate:重要补充 中,我仔细分析了 uWSGI 中处理 logging rotate 的各种限制和解决方案,在文章最后,我提到:
因此,我们需要一个终极解决方案来解决这个问题。这需要新开一篇文章来说明。
现在,终极解决方案 来了。
1. 测试支持的 logger 插件
上篇文章我们讨论过,uWGI 的 req-logger
和 logger
两个参数是插件化的 logger,它们不支持 logreopen
功能。但也正是因为它们的插件化功能,让它们可以支持多种 logger 数据目标。
在 官方文档 中,介绍了多种 logger 支持插件。例如 socket/rsyslog/mongodblog/zeromq
等等。
可以使用 uwsgi --logger-list
来查看当前的 uWSGI 版本到底支持哪些 logger 插件:
1uwsgi --logger-list
2*** uWSGI loaded loggers ***
3python
4syslog
5rsyslog
6socket
7redislog
8mongodblog
9file
10fd
11stdio
12--- end of loggers list ---
我对几个插件进行了测试。结果如下:
syslog
工作正常。python
插件可以让 uWSGI 使用一个来自于 python 标准库的 logger。但这个插件没有文档,阅读源码后进行相关配置,实例会崩溃且没有可读的错误日志。- 文档中提到的
zeromq
插件在 PyPI 最新 uWSGI 版本中没有提供,因此不可用。
我认为比较高效的方式,是使用 redislog
来分发日志。需要一个独立的日志接收服务,对发来的日志进行分类存储和集中化管理。
这个日志服务就是 pyzog 。
2. pyzog
pyzog 是一个独立的服务,用来支持从 Redis 或者 ZeroMQ 发来的日志信息。它的特点如下:
- 使用 python 的 logging 标准库来处理日志,方便扩展。
- 支持使用 Redis 的 Pub/Sub 机制来接收日志。
- 支持使用 ZeroMQ 的 PUB-SUB/DEALER-ROUTER 模式来接收日志。
- 使用 Python 标准库模块 logging.handlers.WatchedFileHandler 来支持文件 inode 改变的时候自动打开新的流,以实现自动 logrotate。
- 根据发来的信息提供的数据自动分类写入 log 文件。
- 提供命令行来启动服务。
- 使用 Supervisor 守护 pyzog 服务。
- 提供 systemd 配置文件的生成功能。
- 提供 program 配置文件的生成功能。
使用 pyzog 命令行可以查看其支持的子命令:
1$ pyzog --help
2
3Usage: pyzog [OPTIONS] COMMAND [ARGS]...
4
5 执行 pyzog 命令
6
7Options:
8 --help Show this message and exit.
9
10Commands:
11 genprog 生成 supervisord 的 program 配置文件
12 genpyzog 在当前文件夹下生成 pyzog.conf 配置文件
13 gensupe 在当前文件夹下生成 supervisord.conf 配置文件
14 gensys 在当前文件夹下生成 systemd 需要的 supervisord.service 配置文件
15 start 启动 pyzog。请先使用 pyzog genpyzog 生成配置文件。
3. 部署 pyzog
接下来讲述将 pyzog 部署到服务器的流程。
3.1 安装 pyzog 到虚拟环境
pyzog 没有提交的 PyPI。PyPI 上已有的那个 PyZog 与本项目无关。
可以直接使用 pip 从 github 上安装 pyzog。创建虚拟环境并使用 pip 安装:
1$ python3 -m venv ~/pyzog/venv
2$ source ~/pyzog/venv/bin/activate
3$ (venv) pip install git+https://github.com/zrong/pyzog
3.2 配置 Supervisor
pyzog 需要一个守护程序,保证进程 crash 的时候自动拉起。我选择使用 Supervisor。它已经和 pyzog 一起被安装了。
pyzog 可以生成 Supervisor 的配置文件。
1$ cd ~/pyzog
2$ (venv) pyzog gensupe --help
3
4 在当前文件夹下生成 supervisord.conf 配置文件
5
6Options:
7 -p, --path PATH 提供一个路径,配置中和路径相关的内容都放在这个路径下
8 --unix-http-server-file TEXT
9 --supervisord-logfile TEXT
10 --supervisord-pidfile TEXT
11 --supervisord-user TEXT
12 --supervisord-directory TEXT
13 --supervisorctl-serverurl TEXT
14 --include-files TEXT
15 --help Show this message and exit.
提供 --path
和 --superversord-user
参数,其他的参数会自动根据 --path
设置:
1# 这里采用 app 这个普通用户来执行 supervisord。如果不提供这个参数,那么将使用 root 来运行 supervisord
2$ (venv) pyzog gensupe --path ~/pyzog --supervisord-user app
3# 根据 --path 的设定,创建集中放置 program 配置的文件夹
4mkdir ~/pyzog/conf.d
命令执行成功之后,会在当前文件夹下生成一个 supervisord.conf
配置文件。
3.3 使用 systempd 守护 Supervisor
Supervisor 是一个进程管理器,需要在服务器操作系统启动的时候自动启动。在 Supervisor 的文档 Running supervisord automatically on startup 中可以找到对应操作系统的 initscripts。
目前,许多 Linux 发行版都已经使用 systemd 替换了标准的 initscript。我们这里使用 systemd 来代替老旧的 initscript。
pyzog 可以创建 systemd 配置文件。确保当前位于 ~/pyzog
文件夹中,虚拟环境激活状态:
1$ (venv) pyzog gensys --help
2Usage: pyzog gensys [OPTIONS]
3
4 在当前文件夹下生成 systemd 需要的 supervisord.service 配置文件
5
6Options:
7 --supervisord-exec TEXT
8 --supervisorctl-exec TEXT
9 --supervisord-conf TEXT
10 --help Show this message and exit.
提供以上三个参数的值,:
1$ (venv) pyzog gensys --supervisord-exec ~/pyzog/venv/bin/supervisord --supervisorctl-exec ~/pyzog/venv/binsupervisorctl --supervisord-conf ~/pyzog/supervisord.conf
命令执行成功之后,会在当前文件夹下生成一个 supervisord.service
配置文件。
继续配置 systemd:
1$ mkdir -p ~/.config/systemd/user
2$ mv ~/pyzog/supervisord.service ~/.config/systemd/user/
3$ systemctl --user enable supervisord
4$ systemctl --user start supervisord
至此, supervisord 已经被正常启动,可以通过 systemctl --user status supervisord
查看其状态。
3.4 创建 pyzog 运行需要的配置文件
pyzog 运行需要配置文件,pyzog 提供了子命令来生成它们:
1$ (venv) pyzog genpyzog --help
2Usage: pyzog genpyzog [OPTIONS]
3
4 在当前文件夹下生成 pyzog.conf 配置文件
5
6Options:
7 -n, --name TEXT pyzog 实例的名称 [required]
8 -l, --logpath PATH 提供一个路径,pyzog 接收到的日志将放在这里 [required]
9 -t, --type [redis|zmq] 指定服务器类型,可选值 redis/zmq [required]
10 -a, --addr TEXT 服务器地址,形如 tcp://127.0.0.1:5011 或者
11 password@127.0.0.1:6379/0 [required]
12 -m, --get-message-type [thread|block|listen]
13 -s, --sleep-time FLOAT 调用 redis.pubsub.get_message 时提供的 sleep_time
14 参数
15 -c, --channel TEXT 仅当 type 为 redis 的时候提供,允许指定多个 channel 名称
16 --help Show this message and exit.
创建一个名称为 pyzog1.conf
的配置。使用 redis 接收日志,支持两个 channel req.*
和 app.*
,log 文件写入 ~/pyzog/logs
目录。然后创建一个名为 conf 的文件夹,将所有的运行配置放入其中:
1$ (venv) pyzog genpyzog -n pyzog1 -l ~/pyzog/logs -t redis -a '127.0.0.1:6372' -c 'app.*' -c 'req.*'
2$ mkdir ~/pyzog/conf
3$ mv ~/pyzog/pyzog1.conf ~/pyzog/conf
3.5 配置 Supervisor 中的 program
上面的配置指定了 Supervisor 管理的 program 配置文件位于 ~/pyzog/conf.d
中。让我们来生成一个配置:
1$ (venv) pyzog genprog --help
2Usage: pyzog genprog [OPTIONS]
3
4 生成 supervisord 的 program 配置文件
5
6Options:
7 -n, --name TEXT Supervisor program 名称 [required]
8 -c, --config_file PATH [required]
9 -u, --user TEXT Supervisor program 的 user
10 --help Show this message and exit.
创建一个名称为 p1
的配置。使用刚才生成的 pyzog1.conf
配置文件运行,并将生成的 p1.conf
文件夹移动到 supervisord 的 program 配置文件夹中:
1$ (venv) pyzog genprog -n p1 -c ~/pyzog/conf/pyzog1.conf -u app
2$ mv ~/pyzog/p1.conf ~/pyzog/conf.d
3.6 使用 supervisor 启动 pyzog 进程
启动 uspervisorctl,调用 reread
命令读取配置文件:
1$ (venv) supervisorctl -c ~/pyzog/supervisord.conf
2supervisor> reread
3p1: available
启动这个 program:
1supervisor> start p1
2p1: started
查看状态:
1supervisor> status
2p1 RUNNING pid 12848, uptime 0:00:01
3.7 为什么不使用 systemd 直接管理 pyzog?
因为 Supervisor 在管理上更加直观,在分组管理 program 的时候更加方便。
4. 发送日志给 pyzog program
4.1 uWSGI 发送请求日志给 pyzog
在 Flask+uWSGI logging rotate:重要补充 中提到的 req-logger
无法进行 logrotate 的问题,可以使用 pyzog 解决了。只需要配置 uwsgi.ini
:
1[uwsgi]
2daemonize = true
3log-master = true
4req-logger = redislog:127.0.0.1:6379,publish req.mjp2401
uWSGI 会自动将 request log 内容使用 publish 命令发送给指定的 redis, 使用的 channel 名称为 req.mjp2401
。
上面配置了名为 p1 的 pyzog 进程用来接收 req.*
这个 channel 名称,当 pyzog 接收到 uWSGI 传来的 log 时,会自动创建 ~/pyzog/logs/req.mjp2401.log
这个日志文件。
如果采用不同的 channel(req 前缀),pyzog 会创建不同的日志文件。这就解决了数十个 uWSGI 实例分开处理日志的问题。
4.2 python logging 发送日志给 pyzog
在 Flask+uWSGI logging rotate:重要补充 中的 5. Flask 中的日志处理 这个章节,我封装的 get_logger
方法支持 stream/file/zmq
三种不同类型的 logger。
pyzog 提供了 RedisHandler,扩展了 get_logger
方法,增加了对 redis 的支持,详情参考 pyzog.logging 源码。
5. 设置 logrotate
增加一个 logrotate 配置文件 /etc/logrotate.d/pyzog
,内容如下:
1/home/app/pyzog/logs/*.log {
2 su app app
3 # WatchedFileHandler 会自动创建新文件
4 nocreate
5 daily
6 rotate 5
7 missingok
8 notifempty
9 compress
10 dateext
11 dateyesterday
12}
重启 logrotate 服务,终极方案完成。
完美!
6. 相关阅读
- 部署Flask + uWSGI + Nginx
- uWSGI+rsyslog 实现 rotating logging
- Flask+uWSGI 的 Logging 支持
- Flask+uWSGI logging rotate:重要补充
- 文章ID:2691
- 原文作者:zrong
- 原文链接:https://blog.zengrong.net/post/pyzog/
- 版权声明:本作品采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可,非商业转载请注明出处(原文作者,原文链接),商业转载请联系作者获得授权。