uWSGI 可以使用 --logto / --logto2 / --daemonize 这几个参数来指定把 log 写入普通文件。但普通文件管理起来比较麻烦,我们可以利用 Ubuntu/CentOS 中自带的 Rsyslog 来实现日志管理。本文以 Ubuntu 16.04 为例。

uWSGI 的外部 logging 支持

让我们先来看看 uWSGI 内置了哪些 logging 系统支持:

1
2
3
4
5
6
7
8
9
10
11
12
13
% uwsgi --logger-list
*** uWSGI loaded loggers ***
python
syslog
rsyslog
socket
redislog
mongodblog
file
fd
stdio
--- end of loggers list ---

看来 syslog 已经在内置支持中了。不过要注意的是,上面列表中的 rsyslog 指的是 Remote Syslog ,如果你的 syslog 服务器在远程,则使用这个值,否则应该使用 syslog 。

使用外部 logging 系统

uWSGI 有两个参数来设定使用 外部的 logging 系统req-logger 包含 uWSGI 的请求 log,而 logger 包含标准错误。

下面的设置将 req-loggerlogger 分别命名,并使用了 local5 这个 facility 。local5 是可选的,也可以仅包含名称。

1
2
3
[uwsgi]
req-logger = syslog:game-proxy-req,local5
logger = syslog:game-proxy,local5

设置 syslog

创建文件夹 /var/log/uwsgi 用来保存 uwsgi 的 log,并设置正确的权限:

1
sudo chown syslog:adm /var/log/uwsgi

加入一个文件: /etc/rsyslog.d/80-game.conf ,内容如下:

1
2
3
# local5.* /var/log/uwsgi/game-all.log
:programname,isequal,"game-proxy" /var/log/uwsgi/game-proxy.log
:programname,isequal,"game-proxy-req" /var/log/uwsgi/game-proxy-req.log

programname 就代表每条 log 的静态名称,在这里 Message Propertiesisequal 代表完全相等判断,在 Compare-Operations 这里可以找到所有可用的比较类型。

上面的配置根据 uWSGI 中配置的名称,将 log 分离到不同的文件。第一行 local5.* 则将所有使用 local5 的 log 全部置入 game-all.log,该配置并没有启用。

设置完成后重启 rsyslog 服务:

1
sudo service rsyslog restart

设置 logrotate

logrotate Ubuntu 自带的压缩、分隔 log 文件的工具,它被 crontab 调用。有了 logrotate ,我们就不必自己设计压缩和分割方案。

主 logrotate 配置文件在 /etc/logrotate.conf ,文件夹 /etc/logrotate.d/ 中的所有配置文件也会被载入。我们只需要在该文件夹中增加一个文件就可以实现对 /var/log/uwsgi 中 log 文件的压缩。下面是 /etc/logrotate.d/game 文件的内容:

1
2
3
4
5
6
7
8
9
10
11
/var/log/uwsgi/game-*.log {
create 0664 app app
daily
rotate 60
compress
delaycompress
missingok
notifempty
dateext
dateyesterday
}
  • create 分割后创建新文件,可以指定文件权限,owner 和 group 。
  • daily 每日分割。
  • rotate 60 保留60天的日志。
  • compress 压缩日志。
  • delaycompress 延迟压缩,在下一个日志分割的时候压缩上一个日志。这样同时有两个日志没有压缩:昨天的和今天的。这样可以方便查询昨天的内容。
  • missingok 没有日志文件的时候不报错。
  • notifempty 空日志不处理。
  • dateext 使用日期作为分割日志后缀名称。若不设置则使用序号。
  • dateyesterday 当设置了 dateext 的时候有效。在分割日志后面加上昨天的日期,这样文件后面的日期和文件中记录的时间戳时间是一致的。

更多详细的设置,可以看 logrotate 的 man page

logrotate 是被 crontab 自动调用的,详细的配置可以看文件: /etc/cron.daily/logrotate

如果等不及 crontab 执行,可以自行使用下面的命令测试:

1
logrotate -df /etc/logrotate.d/game
在使用 `-df` 的时候,仅仅是展示 rotate 效果,但并不会真正执行。如果要真正执行,可以使用 `-vf` 参数。

使用 python 的 logging 模块

Python 自带 logging 模块。我们也可以禁用 uWSGI 中的设置,改为使用 Python 语言的 logging 模块向 rsyslog 记录日志。之前我写过一篇这样的文章:rsyslog/Python/LogAnalyzer 记录和查看日志

查看日志可以使用 LogAnalyzer 这个 PHP 写成的前端,用法在 rsyslog/Python/LogAnalyzer 记录和查看日志 一文中也有提到。

单独使用 logrotate

当然,我们也可以不使用 Rsyslog ,直接对 --logto / --logto2 / --daemonize 这几个参数生成的 log 文件执行 logrotate。

但要注意一个问题,当 logrotate 生效后,log 依然写入旧的文件!

举例说明:

  1. 原始的 log 文件名称为 uwsgi.log
  2. 执行 logrotate -vf /etc/logrotate.d/game ,rotate 成功,文件名被修改为 uwsgi.log-20171012 ,并创建了新文件 uwsgi.log
  3. 此时发现新的 log 依然被写入 uwsgi.log-20171012 而非 uwsgi.log

这是因为在 linux 系统下,一个进程打开文件时使用的是文件系统的 inode 编号而非文件名。移动或者重命名一个文件,并不会修改它的 inode 编号。因此需要在进行 rotate 之后,通知 uwsgi 重新打开 log 文件。

或者,可以将 create 创建方式修改为 copytruncate 创建方式,后者的特点是复制一份现有的 log 为新文件,然后清空旧文件。这样就需要通知 uwsgi 重新打开 log 文件了。

copytruncate 的缺点就是,复制 log 文件和清空 log 文件之间有一段时间(若 log 文件较大就更明显),这段时间中的 log 文件可能丢失。

如果仍然希望使用 create 参数,那么可以参考 Flask+uWSGI 的 Logging 支持 中的“uWSGI 的 Logging 配置“一节。

参考

全文完

留言