Rsyslog的全称是 rocket-fast system for log,它提供了高性能,高安全功能和模块化设计。rsyslog能够接受从各种各样的来源,将其输入,输出的结果到不同的目的地。rsyslog可以提供超过每秒一百万条消息给目标文件。
说明
rsyslog详解文章 可以参考 https://cloud.tencent.com/developer/article/1682996
里面写的很好 ,这篇文章基本照搬,便于自己在博客上记录
介绍
rsyslog,是Centos系统自带的很强大的一块系统日志工具。
/var/log/messages这个日志大家并不陌生,是我们出现问题经常去查看的一个日志记录文件,这个日志里面信息的填写是由rsyslog服务维护的。
以前旧版本Centos 5系列的时候,此服务叫做syslog,从Centos 6版本开始,更名为rsyslog服务。以前是两个进程,rsyslogd(系统日志包括用户空间的各应用程序相关的日志)进程,klogd(内核日志包括kernel相关的日志)进程但是此进程由rsyslogd代为管理。所以只看到了rsyslogd一个进程。
以前旧版本syslog不能进行并行数据存储、效率低,不能实现放在专用数据管理文件中,升级到rsyslog之后有了下面的优点:
(1). 多线程的服务,并发性能好。
(2). 可以使用udp、tcp、ssl、tls、relp等协议完成信息收集,可以集中存储在远程日志服务器中,早期的syslog仅支持简单的文本传输模式实现日志发送,不安全。
(3). 支持将日志放到mysql,pgsql,oracle等多种数据库中,这种行级锁的存储形式,不仅可以让多主机的信息写入更快,而且可以为web的界面展示提供很好的支持。
(4). 强大的过滤器,自定义过滤器,可实现过滤系统信息中的任意部分。
(5). 支持完整的自定义格式的输出格式配置。
rsyslog 详解实战
线上环境日志集中方案
- rsyslog发送端 + rsyslog接收端: 直接存在接收端的本地硬盘
- rsyslog发送端 + logstash接收端 + <后续第三方处理>: 接受到log更新行后,通过logstash简单处理后,可以继续往第三方处理,如放到ElasticSearch,或者放到消息队列Kfaka等
- rsyslog发送端 + Splunk: Splunk是商业软件,也是业内用的比较多的方式,价格不菲
配置文件介绍
配置文件介绍
执行文件: /sbin/rsyslogd
主配置文件: /etc/rsyslog.conf
自定义配置文件: /etc/rsyslog.d/*.conf
修改配置文件后,
重启服务: sudo /etc/init.d/rsyslog restart
一份配置文件主要包括以下几个部分:
- MODULES
- RULES
- 全局指令,模板,模块参数等
1 | rsyslog v5 configuration file |
模块 imfile
为了完成我们的任务,除了上面默认的模块,还需要加载 imfile
,,来指定监控哪些文件,参考文档。
1 | ModLoad imfile # Load the imfile input module |
该模块把标准的文本文件转换成syslog的message格式, 所谓标准文本是指:保护可打印的字符,每行以 LF
作为分隔符号。 支持文件正在在logrotate的时候,仍能正确处理。 它会把监控文件的读取到哪一个位置(类似游标cursor),存储在state文件里(由 $WorkDirectory
指定)。
该模块支持如下指令,一组如下设置,可以称为一个 listener:
1 | $InputFileName /path/to/file # 待监控的文件路径 |
接收端配置,注意tag里的逗号 ','
,稍后在接收端,会它来分隔:
1 | # For product, total 19 files. |
一条rule的语法格式如: <Facility>.<Severity> <Target>
例如:
1 | # Log cron stuff |
Facility
日志设备(可以理解为日志类型):
1 | auth #pam产生的日志,认证日志 |
Severity
1 | debug #有调式信息的,日志信息最多 |
Target
- 文件: /var/log/messages
- 用户: root,*(表示所有用户), 会发到/var/spool/mail/收件箱里
- 日志服务器: @192.168.56.10 或者 @@192.168.56.10
- 管道: | COMMAND
一个 @
表示 UDP
, 两个 @@
表示 TCP
协议。
常用指令
模板$template
模板 $template
, 最主要的一个指令,在 接收端 可用来定义消息格式、文件名。主要是在接收端使用。 可参考 官方文档Templates
语法:
1 | $template <name>,<内容>,<可选项> |
默认的消息格式 RSYSLOG_TraditionalFileFormat
1 | <接收内容的时间> <发送者的hostname> <$InputFileTag> <原始消息%msg%> |
如果只需要显示原始消息,可设置
1 | $template CleanMsgFormat,"%msg%\n" |
内置属性 Properties
模板里支持一些 内置的变量:
1 | %msg% |
属性处理 Property Replacer
Property Replacer 用来处理变量,支持一些简单的字符串处理,如大小写,substring等,类似jinja2的filter概念。 版本越新支持的处理函数越强大。 语法: %property:fromChar:toChar:options%
# For product $template productFileFormat,”/Data/logs/product/%fromhost-ip%/%syslogtag:F,44:2%-%$YEAR%%$MONTH%%$DAY%.log” if $syslogtag startswith ‘product’ then ?productFileFormat;CleanMsgFormat & ~
上面的例子,在接收端会保存为 /Data/logs/product/198.168.56.123/karltest_demo-20161218.log
解释下这个指令%syslogtag:F,44:2%
F,
- 代表自定义一个分隔符 44
- 是逗号 ,
的 ASCII 码值,如需要别的分隔符,需要查对应 ASCII 值 2
- 取分隔后的第二个字段
所以就是: 假设发送端自定义的tag为 $InputFileTag product,karltest_demo
, 如果tag以product开始,则取出逗号分隔的第二个字段作为保存的文件名,这也是为啥上面tag里要设置一个逗号的缘故。
另外,还支持一定的 regex 语法,可以进行更高级的控制。官方提供了一个在线的 regex 语法测试。 友情提醒:真的很难用。。。
停止指令
上面的接收端,在一条规则后,加上了如下的指令(也叫停止指令),代表如果log被当前的rule已经处理过了,则完成本次执行,跳过后续rule的处理。 类似 C++里 switch/case,如果忘记加break的穿透副作用。
1 | & ~ |
如果没有这个指令,则一条新来的消息可以被多个rule处理。 这里我们并不需要,只要命中就保存到接收端同名的文件里。
发送端配置
- 加载 imfile 模块
- 指定要监控的 log 文件路径,设置合适的tag
- 指定远端的接收端的地址
完整配置: /etc/rsyslog.conf 和 /etc/rsyslog.d/product.conf
接收端配置
- 加载 imtcp 模块
- 设置 message 格式
- 设置 文件存储路径,文件名格式
完整配置: /etc/rsyslog.conf
遇到的坑
UDP or TCP ?
一般来说选择TCP都是OK的,除非忍受部分丢失,在意影响性能,可以改用UDP。 但是注意:如果你的消息每行大小超过了4k,只能用TCP。这是因为UDP栈大小限制的。
引用官方有关 MaxMessageSize 的描述:
Note: testing showed that 4k seems to be the typical maximum for UDP based syslog. This is an IP stack restriction. Not always … but very often. If you go beyond that value, be sure to test that rsyslogd actually does what you think it should do ;) It is highly suggested to use a TCP based transport instead of UDP (plain TCP syslog, RELP). This resolves the UDP stack size restrictions.
如何测试: vim vs echo ?
配置好接收端、发送端rsyslog后,需要验证下是否能正确传送新的log行。
- echo追加:
echo "dummy message" >> /Data/logs/product/karltest.log
- vim编辑:
vim /Data/logs/product/karltest.log
用vim编辑后,保存会刷新整个文件,导致rsyslog在比较state file的时候,认为全部是新增的行,会在接收端出现重复的log行。 所以正确测试方法是用 echo 追加的方式。
Tips: 发送端:可以配合 watch 来测试:
watch -n 1 " echo $(date) dummy message >> /Data/logs/product/karltest.log "
接收端:tailf /Data/logs/karltest/karltest.log
接收端:log行太长,被截断了
默认大小是2k,大概可以保存1000个中文字符,参考官方说明 $MaxMessageSize, 最小也是2k。
在加载imtcp/imudp之前设置, 此配置包括发送和接收,所以rsyslog客户端、服务端都要设置:
1 | $MaxMessageSize 32k |
发送端:/var/log/messages 文件变大
log除了发送到了接收端,还在本地 /var/log/messages
里重复出现了,导致messages上G 罪魁祸首是默认的配置文件如下这行:
1 | # 修改前 |
因为前面有通配符 *.info
导致我们自定义的 local5.info
也会写到本地messages文件。 为了保险,请把接收端、发送端的配置文件都修改掉,忽略掉local5。
接收端:消息被多个rule处理
在命中某条rule后,直接break停止
1 | if $syslogtag startswith 'product' then ?productFileFormat;CleanMsgFormat |
接收端: 保存的文件路径不对
要注意自定义tag的前缀匹配,如果两个tag有共同的前缀,需要把长的放在前面,调整好顺序。
Fix 前:
1 | # For erp_wms |
Fix 后:
1 | # 这里注意下面的tag的顺序, 一定要让长的tag(erp_wms3)保持在上面,因为他们有共同的前缀(erp_wms) |
接收端: rsyslog 文件名太长后被截断
比如发送端原始文件名tag: product,cache_status_im_request.log
但是到了接收端就截断了: cache_status_im_re-20161218.log
因为本来名字就长,加上了时间后更长了,
个人理解,Linux中关于文件名(255),文件路径(4096)的限制如下,而在接收端,都没有超过这个长度。
1 | $ cat /usr/include/linux/limits.h |
进过一番艰难的测试复现,猜测是 $InputFileTag
这个 %syslogtag%
的原因。
官方解释Sending messages with tags larger than 32 characters。
发送端默认的模板为:
1 | # 忽略旧的5.8.6的语法 |
可以看到 %syslogtag:1:32%
,被截断到32字符,这个也与我测试的结果一致。
所以如果需要处理更长的tag,需要修改 发送端的template模板,去掉 :1:32
限制。 然后绑定这个模板到对应的target上。 注意:接收端可能也要相应处理,才能handle更长的tag名(未测试)。
免责声明:由于折腾这个rsyslog太累,最后一条暂时没有Fix,如有需要,请自行测试后再用。
UPDATED @ 2017-01-24 这个问题越来越严重,所以必须花时间解决掉。
如下的两个%syslogtag%,由于前32个字符都相同,导致最后日志写到同一个文件 databas.log
(按照逗号切割后)了。
1 | $InputFileName /Data/logs/mqorder/order-mq-aws/database-stat.log |
解决方法 : 在发送端的主配置文件 /etc/rsyslog.conf
里修改发送规则:
1 | # ### begin forwarding rule ### |
其中起作用的就是 LongTagForwardFormat
这个模板。
解决过程 :
TL;DR
参考了上面的链接: 官方解释Sending messages with tags larger than 32 characters 和 tag getting truncated。
官方解释里是 5.8.6 的旧的语法,而CentOS6.x Final上自带的是 5.8.10, 语法不一样。 从官网下载5.8.10的源码: http://www.rsyslog.com/files/download/rsyslog/rsyslog-5.8.10.tar.gz
全文搜索关键字 %syslogtag
, 最终在如下几个文件找到蛛丝马迹:
- rsyslog-5.8.10/tools/smfwd.c
- rsyslog-5.8.10/tools/smtradfile.c
- rsyslog-5.8.10/tools/smtradfwd.c
- rsyslog-5.8.10/tools/syslogd.c
全文搜索关键字 ForwardFormat
:
- rsyslog-5.8.10/tools/syslogd.c
1 | /* hardcoded standard templates (used for defaults) */ |
正如官网链接所说,源码里 hardcoded 里多个默认模板格式, 其中我们需要关注的就是 RSYSLOG_ForwardFormat
, 对应的文件即:rsyslog-5.8.10/tools/smfwd.c
。
1 | /* smfwd.c |
这里,就能找到我们需要的正确的默认格式了, 可以看到 %syslogtag% 截取了前面32个字符,把ForwardFormat规则替换成完整的 %syslogtag% 即可,注意:最大的长度是512。
接下来……
通过一番设置,我们已经能成功收集若干台线上机器的日志了:
1 | # tree -I "*gz|*log" /Data/logs/ |
那么问题来了:
上百台机器的上几百G的日志,怎么才能避免接收端硬盘爆掉?
得用另一个Linux自带的脚本 /usr/sbin/logrotate
, 来配合 rsyslog。