实际工作中用到了rsyslog来做本地日志的记录工具。但在实际使用过程中,偶尔会出现日志写着写着,就不记日志的情况。具体原因是因为,rsyslog监听的/dev/log的端口变了,但是使用记录日志的程序,对应的域socket监听的端口没有变。具体的可见以下的分析内容。
以下是第一阶段的分析报告。
glibc版本:glibc-2.17-222.el7.x86_64
rsyslog版本:8.24.0
rsyslog相关的涉及到crond、logrotate、函数库等,这里只介绍讨论和当前不记日志问题相关的内容。
文件
预先知道两个文件
[root@m-trunk ~]# ll /run/systemd/journal/syslog
srw-rw-rw- 1 root root 0 Apr 23 11:32 /run/systemd/journal/syslog
[root@m-trunk ~]# ll /dev/log
srw-rw-rw- 1 root root 0 Apr 23 11:34 /dev/log
“s”类型的文件表明是Unix域套接字文件,即socket文件。
rsyslog原理简介
以下是rsyslog的原理图:
rsyslog日志系统采用C/S模式。S端是守护进程rsyslogd,它从socket文件(/dev/log或前面提到的journa文件)或者是UDP或TCP报文的514端口获取日志内容,然后再根据rsyslog.conf文件中的配置,做进一步的日志过滤等处理,然后将日志写入到对应的文件中。C端即各个需要通过rsyslog记录日志的用户程序。如我们核心网的mme,sgw,pgw网元。
对于核心网系统而言:
C端将需要记录的日志通过系统提供的API接口syslog函数,将日志内容写到socket文件中,然后rsyslog再从socket文件中去读取日志内容,在根据其配置,写入对应文件中。
syslog函数由Linux函数库glibc提供,同时提供的还有openlog和closelog两个函数,此两个函数非必须使用,仅调用syslog也可以,当目标日志文件不存在时,syslog会自动打开该文件。
日志相关的配置
rsyslog.conf
路径:/etc/rsyslog.conf
rsyslog.conf中的配置:
#### MODULES ####
# The imjournal module bellow is now used as a message source instead of imuxsock.
$ModLoad imuxsock # provides support for local system logging (e.g. via logger command)
#$ModLoad imjournal # provides access to the systemd journal
#$ModLoad imklog # reads kernel messages (the same are read from journald)
#$ModLoad immark # provides --MARK-- message capability
以上配置项,配置rsyslog的日志源:
$ModLoad imuxsock:加载imuxsock模块,提供对本地系统日志的支持。对应的是/dev/log设备,打开的是Unix socket。
#$ModLoad imjournal:加载imjournal模块,提供对systemd日志的访问。对应的是/run/systemd/journal/syslog设备,打开的是Unix socket。
The imjournal module bellow is now used as a message source instead of imuxsock.:表明,虽然imjournal在配置文件中是被注释的,但其仍然是rsyslog日志源的默认配置。
以上配置逻辑表明:当前rsyslog的日志源有两种,一种是从/dev/log中获取,一种是从/run/systemd/journal/syslog中获取,默认是/run/systemd/journal/syslog。
listen.conf
路径:/etc/rsyslog.d/listen.conf
配置内容:
$SystemLogSocketName /run/systemd/journal/syslog
和rsyslog.conf中的$ModLoad imjournal配置对应,rsyslog会打开该文件创建Unix socket。
journald.conf
systemd命令的配置文件
路径:systemd/journald.conf
配置内容:
[Journal]
#Storage=auto
#Compress=yes
#Seal=yes
#SplitMode=uid
#SyncIntervalSec=5m
#RateLimitInterval=30s
#RateLimitBurst=1000
#SystemMaxUse=
#SystemKeepFree=
#SystemMaxFileSize=
#RuntimeMaxUse=
#RuntimeKeepFree=
#RuntimeMaxFileSize=
#MaxRetentionSec=
#MaxFileSec=1month
#ForwardToSyslog=yes
#ForwardToKMsg=no
#ForwardToConsole=no
#ForwardToWall=yes
#TTYPath=/dev/console
#MaxLevelStore=debug
#MaxLevelSyslog=debug
#MaxLevelKMsg=notice
#MaxLevelConsole=info
#MaxLevelWall=emerg
#LineMax=48K
通过man journald.conf查看journald.conf的配置说明,获取到如下信息:
ForwardToSyslog=, ForwardToKMsg=, ForwardToConsole=, ForwardToWall=
Control whether log messages received by the journal daemon shall be forwarded to a traditional syslog daemon, to the kernel log buffer (kmsg), to the system console, or sent as wall messages to all logged-in users. These options take boolean arguments.
If forwarding to syslog is enabled but nothing reads messages from the socket, forwarding to syslog has no effect. By default, only forwarding to syslog and wall is enabled. These settings may be overridden at boot time with the kernel command line options "systemd.journald.forward_to_syslog=", "systemd.journald.forward_to_kmsg=", "systemd.journald.forward_to_console=", and "systemd.journald.forward_to_wall=". When forwarding to the console, the TTY to log to can be changed with TTYPath=, described below.
……
FORWARDING TO TRADITIONAL SYSLOG DAEMONS
Journal events can be transferred to a different logging daemon in two different ways. In the first method, messages are immediately forwarded to a socket (/run/systemd/journal/syslog), where the traditional syslog daemon can read them. This method is controlled by ForwardToSyslog= option.
In a second method, a syslog daemon behaves like a normal journal client, and reads messages from the journal files, similarly to journalctl(1). In this method, messages do not have
to be read immediately, which allows a logging daemon which is only started late in boot to access all messages since the start of the system. In addition, full structured meta-data is available to it. This method of course is available only if the messages are stored in a journal file at all. So it will not work if Storage=none is set. It should be noted that usually the second method is used by syslog daemons, so the Storage= option, and not the ForwardToSyslog= option, is relevant for them.
以上说明提供以下几点重要信息:
- ForwardToSyslog:控制journal产生的日志是否转发给rsyslog,默认是启用的。
- Journal中的日志转发给rsyslog有两种方式:
- 日志被立即发送到socket文件/run/systemd/journal/syslog,rsyslog可以从这里读取。就是说,这种情况下rsyslog能立即从journal的socket文件中立即读取到日志内容。
- rsyslog作为一个客户端,从journal文件中读取日志。此种方式下,允许rsyslog在系统启动时去读取所有的日志消息,而并不需要一有日志消息就立即被读取。就是说,rsyslog会在系统启动时去读一次所有的日志消息,而之后并不需要实时读取,什么时候再读,时间并不确定。
- 第二种方式是当前采用的转发方式。此种方式相关的配置是Storage=和ForwardToSyslog=两个配置项。
结论:
- 当前系统中,rsyslog使用了两种获取日志的方式,一种是imuxsock,通过/dev/log文件传输日志内容;另外一种是imjournal方式,通过/run/systemd/journal/syslog传输日志内容,且默认是该种方式。
- /dev/log是实时获取日志。journal当前配置下并非实时,仅在系统启动时读取一次。
现象分析
未删除listen.conf的情况下
日志正常
[root@trunk-s QuickInstall]# ll /proc/357701/fd
total 0
lr-x------ 1 root root 64 Apr 16 08:21 0 -> /dev/null
l-wx------ 1 root root 64 Apr 16 08:21 1 -> /dev/null
l-wx------ 1 root root 64 Apr 16 08:21 10 -> /opt/lte/mme/log/mme.log
l-wx------ 1 root root 64 Apr 16 08:21 11 -> /var/log/cron
l-wx------ 1 root root 64 Apr 16 08:21 2 -> /dev/null
lrwx------ 1 root root 64 Apr 16 08:21 3 -> socket:[65707869]
lrwx------ 1 root root 64 Apr 16 08:21 4 -> socket:[65707871]
l-wx------ 1 root root 64 Apr 16 08:21 5 -> /var/log/messages
l-wx------ 1 root root 64 Apr 16 08:21 6 -> /var/log/secure
l-wx------ 1 root root 64 Apr 16 08:21 7 -> /opt/lte/esgw/log/esgw.log
l-wx------ 1 root root 64 Apr 16 08:21 8 -> /opt/lte/epgw/log/epgw.log
l-wx------ 1 root root 64 Apr 16 08:21 9 -> /opt/lte/epgw/log/epgw_subs_log.log
[root@trunk-s QuickInstall]# ss -apxs | grep -E 'syslog|mme|sgw|pgw'
u_dgr UNCONN 0 0 /run/systemd/journal/syslog 65707869 * 0 users:(("rsyslogd",pid=357701,fd=3))
u_dgr UNCONN 0 0 * 65707871 * 60650984 users:(("rsyslogd",pid=357701,fd=4))
u_dgr UNCONN 0 0 * 65708008 * 60650984 users:(("sgw",pid=357874,fd=4))
u_dgr UNCONN 0 0 * 65711184 * 60650984 users:(("mme",pid=357925,fd=7))
u_dgr UNCONN 0 0 * 65713382 * 60650984 users:(("pgw",pid=357878,fd=7))
[root@trunk-s QuickInstall]# netstat -apn | grep 60650984
unix 20 [ ] DGRAM 60650984 1/init /dev/log
[root@trunk-s QuickInstall]# netstat -apn | grep /dev/log
unix 20 [ ] DGRAM 60650984 1/init /dev/log
结论:由以上信息分析:rsyslog打开了一个域socket--/run/systemd/journal/syslog,监听了任意地址和任意端口;rsyslog同时还监听了另外一个端口60650984,而该端口恰好是/dev/log打开的端口。所以日志往域socket的读写都正常。
日志异常
异常1:
[root@trunk etc]# ll /proc/402950/fd
total 0
lr-x------ 1 root root 64 Apr 16 07:21 0 -> /dev/null
l-wx------ 1 root root 64 Apr 16 07:21 1 -> /dev/null
l-wx------ 1 root root 64 Apr 16 07:21 2 -> /dev/null
lrwx------ 1 root root 64 Apr 16 07:21 3 -> socket:[64508563]
lrwx------ 1 root root 64 Apr 16 07:21 4 -> socket:[64508565]
l-wx------ 1 root root 64 Apr 16 07:21 5 -> /var/log/messages
[root@trunk etc]# ss -apxs | grep -E 'syslog|mme|sgw|pgw'
u_dgr UNCONN 0 0 /run/systemd/journal/syslog 64508563 * 0 users:(("rsyslogd",pid=402950,fd=3))
u_dgr UNCONN 0 0 * 64404037 * 60650984 users:(("mme",pid=391052,fd=7))
u_dgr UNCONN 0 0 * 64453587 * 60650984 users:(("pgw",pid=403336,fd=7))
u_dgr UNCONN 0 0 * 64514313 * 60650984 users:(("sgw",pid=403330,fd=4))
u_dgr UNCONN 0 0 * 64508565 * 60650984 users:(("rsyslogd",pid=402950,fd=4))
[root@trunk etc]# netstat -pan | grep 60650984
[root@trunk etc]# netstat -pan | grep /dev/log
unix 4 [ ] DGRAM 59910035 1/init /dev/log
[root@trunk etc]#
异常2
[root@m-trunk ~]# ss -apxs | grep -E 'syslog|mme|sgw|pgw'
u_dgr UNCONN 0 0 /run/systemd/journal/syslog 74043686 * 0 users:(("rsyslogd",pid=106404,fd=3))
u_dgr UNCONN 0 0 * 73988377 * 74033711 users:(("pgw",pid=25606,fd=7))
u_dgr UNCONN 0 0 * 73998005 * 74033711 users:(("sgw",pid=25593,fd=13))
u_dgr UNCONN 0 0 * 74000663 * 74033711 users:(("mme",pid=107221,fd=7))
u_dgr UNCONN 0 0 * 74043688 * 74033711 users:(("rsyslogd",pid=106404,fd=4))
[root@m-trunk ~]#
[root@m-trunk ~]#
[root@m-trunk ~]# netstat -apn | grep 74033711
[root@m-trunk ~]# netstat -apn | grep /dev/log
unix 2 [ ] DGRAM 9083062 1/init /dev/log
[root@m-trunk ~]#
结论:由以上两个异常信息可以看出,当日志不能正常打印时,均出现了各网元及rsyslog监听的/dev/log端口错误的情形。虽然/run/systemd/journal/syslog被rsyslog打开了,但由日志相关配置的出的结论知道,通过此种方式日志并不能保证被实时记录。由此可以确定,实际导致日志不能正常记录的根本原因是,各个网元写日志的Unix socket端口错误。
查看journal的日志信息,也可以发现有日志被打印到journal 文件中了,通过journalctl -xe命令可以查看
删除listen.conf的情况下
正常记日志
[root@EPC3102P03-1 ~]# ll /proc/8957/fd
total 0
lr-x------ 1 root root 64 Apr 16 15:35 0 -> /dev/null
l-wx------ 1 root root 64 Apr 16 15:35 1 -> /dev/null
l-wx------ 1 root root 64 Apr 16 15:35 2 -> /dev/null
lrwx------ 1 root root 64 Apr 16 15:35 3 -> socket:[1315697952]
lrwx------ 1 root root 64 Apr 16 15:35 4 -> socket:[1315697954]
l-wx------ 1 root root 64 Apr 16 15:35 5 -> /opt/lte/esgw/log/esgw.log
l-wx------ 1 root root 64 Apr 16 15:35 6 -> /opt/lte/mme/log/mme.log
l-wx------ 1 root root 64 Apr 16 15:35 7 -> /opt/lte/epgw/log/epgw.log
l-wx------ 1 root root 64 Apr 16 15:35 8 -> /var/log/cron
[root@EPC3102P03-1 ~]# ss -apxs | grep -E 'syslog|mme|sgw|pgw'
u_dgr UNCONN 0 0 /dev/log 1315697952 * 0 users:(("rsyslogd",pid=8957,fd=3))
u_dgr UNCONN 0 0 * 1315668940 * 1315697952 users:(("sgw",pid=37157,fd=4))
u_dgr UNCONN 0 0 * 1315674477 * 1315697952 users:(("pgw",pid=37162,fd=7))
u_dgr UNCONN 0 0 * 1315690410 * 1315697952 users:(("mme",pid=9194,fd=7))
u_dgr UNCONN 0 0 * 1315697954 * 1315697952 users:(("rsyslogd",pid=8957,fd=4))
[root@EPC3102P03-1 ~]# ls /etc/rsyslog.d/listen.conf.bak
[root@EPC3102P03-1 ~]# netstat -apn | grep /dev/log
unix 8 [ ] DGRAM 1315697952 8957/rsyslogd /dev/log
unix 3 [ ] DGRAM 630250579 1/init /dev/log
[root@EPC3102P03-1 ~]# netstat -apn | grep 1315697952
unix 8 [ ] DGRAM 1315697952 8957/rsyslogd /dev/log
[root@EPC3102P03-1 ~]#
结论:由以上信息分析可见,rsyslog读取的Unix socket端口正确,各网元写的Unix socket也正确,最终日志能被正确记录。
解决办法
使用传统的日志读写文件方式/dev/log
- 删除listen.conf文件。
- journald.conf文件中去使能日志自动转发给syslog进程功能,使用默认的/dev/log模式
- rsyslog.conf中去使能imjournal模块,暂时没找到去使能的办法,但从删除listen.conf文件后的端口打开情况来看,rsyslog已经不会再打开journal的端口了。
临时解决办法:
删除listen.conf文件,重启网元,通过“ss -apxs | grep -E 'syslog|mme|sgw|pgw'”和“netstat -apn | grep /dev/log”查看网元监听的/dev/log端口是否正确
rsyslog调试方法
修改/etc/systemd/system/multi-user.target.wants/rsyslog.service文件,让rsyslog以debug模式运行:ExecStart=/usr/sbin/rsyslogd -n -d -D $SYSLOGD_OPTIONS
修改rsyslog.conf文件,在“GLOBAL DIRECTIVES”配置模块下,增加
$DebugFile /root/rsyslog.log
$DebugLevel 1 → 配置rsyslog日志级别0关闭,1按需打印,2打印所有
两行。
然后重启rsyslog进程。
执行“kill -USR1 $(cat /var/run/syslogd.pid)”开启debug日志,rsyslog的运行日志将在/root/rsyslog.log看到,再执行一次“kill -USR1 $(cat /var/run/syslogd.pid)”就关闭rsyslog的日志。
网元启动会重启rsyslog进程
mme,epgw,esgw,egw
mme,epgw,esgw,egw每次执行start(包含HA和非HA)操作时都会调用lte/mme/etc/cfg_log_mgmt脚本,该脚本的的269行会执行“/sbin/service $SYSLOG_SERVICE restart > /dev/null 2>&1”,会重启rsyslog进程。与此同时,在该文件的270行,会执行“/usr/sbin/logrotate -f /etc/logrotateMme.conf 2>/dev/null &”,该命令会根据logrotateXXX.conf配置文件强行将对应网元相关的日志进行转储。所以也就出现了一些大小为0或者没到转储条件就被转储的日志文件。
参考文献
man rsyslog.conf
man journalctl
man 8 rsyslogd
man journald.conf