我不想将标题写的太长,容易影响界面。
将日志放入mysql很简单的几步就可以,虽然网上所有的中英文教程都是错的……
但是当你真的这么干了,你会发觉做了这个事是个很鸡肋的事情,因为每一行日志都放入同一个字段内,这就没什么意义了,反而不如文本文件来得方便。我做了一些调整,将Nginx的日志按照字段放入自定义的数据表内,这样就爽多了。
先看效果:
(我不想给自己找不痛快,所以IP和域名都遮盖了,右边还有一些字段,超过一屏了看不到)
做到这种效果以后,就可以很方便的根据自己的需求去做一些统计、筛选什么的。
下面是正文了:
本文的所有系统都是Debian 11,通过交换网络直连
作为日志服务器的机器内网IP是172.16.0.1,用做Nginx的机器是其他IP
日志数据库名weblogdata,用户名weblogdata,密码1111
1,安装rsyslog-mysql
Debian和Ubuntu这类系统就一行:
apt-get install -y rsyslog-mysql
手敲的命令,应该没错。
安装过程中会问你要不要设置数据库,这里要选no。原因很奇葩,因为在这个地方设置数据库的话,需要root@localhost这个账号的密码是空的,因为这个安装程序是从/etc/mysql/debian.cnf里面找的登录信息,而我觉得正常人并不会把root密码写一个明文摆在这里。而非空密码我反复测试都没法通过,所以他娘的直接选no吧,他其实也就是创建用户、创建数据库,然后创建2个表,我们随后自己动手就是了。
而这个奇葩的情况,我没在任何一个网上文章中看到,换而言之所有网上流传的文章都无法正常运行,至少无法在基于Debian的系统上运行。
2,设置rsyslog-mysql
/etc/rsyslog.conf这个文件里面取消这两行的注释:
module(load="imudp") input(type="imudp" port="514")
/etc/rsyslog.d/mysql.conf这个文件内容要更改:
module (load="ommysql") *.* action(type="ommysql" server="localhost" db="weblogdata" uid="weblogdata" pwd="1111")
3,创建数据表
sql语句:
CREATE TABLE `systemevents` ( `ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `CustomerID` bigint(20) DEFAULT NULL, `ReceivedAt` datetime DEFAULT NULL, `DeviceReportedTime` datetime DEFAULT NULL, `Facility` smallint(6) DEFAULT NULL, `Priority` smallint(6) DEFAULT NULL, `FromHost` varchar(60) DEFAULT NULL, `Message` text DEFAULT NULL, `NTSeverity` int(11) DEFAULT NULL, `Importance` int(11) DEFAULT NULL, `EventSource` varchar(60) DEFAULT NULL, `EventUser` varchar(60) DEFAULT NULL, `EventCategory` int(11) DEFAULT NULL, `EventID` int(11) DEFAULT NULL, `EventBinaryData` text DEFAULT NULL, `MaxAvailable` int(11) DEFAULT NULL, `CurrUsage` int(11) DEFAULT NULL, `MinUsage` int(11) DEFAULT NULL, `MaxUsage` int(11) DEFAULT NULL, `InfoUnitID` int(11) DEFAULT NULL, `SysLogTag` varchar(60) DEFAULT NULL, `EventLogType` varchar(60) DEFAULT NULL, `GenericFileName` varchar(60) DEFAULT NULL, `SystemID` int(11) DEFAULT NULL, PRIMARY KEY (`ID`) ) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4; CREATE TABLE `systemeventsproperties` ( `ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `SystemEventID` int(11) DEFAULT NULL, `ParamName` varchar(255) DEFAULT NULL, `ParamValue` text DEFAULT NULL, PRIMARY KEY (`ID`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; CREATE TABLE `weblogs` ( `id` int(10) NOT NULL, `remote_addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `server_addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `fmt_localtime` datetime(0) DEFAULT NULL, `scheme` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `server_protocol` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `request_method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `host` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `status` int(4) DEFAULT NULL, `sent_http_content_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `body_bytes_sent` bigint(10) DEFAULT NULL, `request_uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `http_referer` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `http_user_agent` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = MyISAM CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
我这里做了3个表,前两个是程序自带的,weblogs是我自己的,也就是用来放详细日志的。
4,设置Nginx
首先,我用的日志格式是这样的:
log_format main '"$remote_addr","$server_addr","$fmt_localtime","$scheme","$server_protocol","$request_method","$host","$status","$sent_http_content_type","$body_bytes_sent","$request_uri","$http_referer","$http_user_agent"';
这个定义是放在http的。
然后,每个站点的日志都要改成这样:
access_log syslog:server=172.16.0.1:514,facility=local6,tag=nginx,severity=info main; error_log /dev/null;
这里说明一下,因为access已经包含了error的内容,所以我觉得没必要再重复记录了,当然这只是经验,如果你认为有必要,那就把error的内容改成和access一样就可以了。
5,启动
上面的全都做完以后,把nginx和rsyslog重启一下就行了,日志就会开始记录。
你认为有必要的话,可以像我这样把端口限制一下,免得被不怀好意的人把你数据库塞爆了:
iptables -A INPUT -p udp -m multiport --dport 514 -j DROP iptables -I INPUT -p udp -m multiport --dport 514 -m set --match-set mysvrip src -j ACCEPT
我是把各服务器ip放在mysvrip这个set里面,也就是ipset里面。
到了这一步,Nginx和rsyslog-mysql的结合体就完成了,但是你看到的会是这样:
和我上面的对比一下,是不是感觉这样很鸡肋?
好,这时候我们自己的表就可以用起来了。
6,分解日志,插入我们自己的表
因为rsyslog-mysql并不能自动的把日志内容进行分解,所以这个事情我们需要手工完成,也就是说当你需要对日志进行分析的时候就干一次。
我自用的sql:
replace into weblogs select id, SUBSTRING_INDEX(SUBSTRING_INDEX(message,'","',1),'"',-1) as remote_addr , SUBSTRING_INDEX(SUBSTRING_INDEX(message,'","',2),'","',-1) as server_addr , SUBSTRING_INDEX(SUBSTRING_INDEX(message,'","',3),'","',-1) as fmt_localtime , SUBSTRING_INDEX(SUBSTRING_INDEX(message,'","',4),'","',-1) as scheme , SUBSTRING_INDEX(SUBSTRING_INDEX(message,'","',5),'","',-1) as server_protocol , SUBSTRING_INDEX(SUBSTRING_INDEX(message,'","',6),'","',-1) as request_method , SUBSTRING_INDEX(SUBSTRING_INDEX(message,'","',7),'","',-1) as host , SUBSTRING_INDEX(SUBSTRING_INDEX(message,'","',8),'","',-1) as status , SUBSTRING_INDEX(SUBSTRING_INDEX(message,'","',9),'","',-1) as sent_http_content_type , SUBSTRING_INDEX(SUBSTRING_INDEX(message,'","',10),'","',-1) as body_bytes_sent , SUBSTRING_INDEX(SUBSTRING_INDEX(message,'","',11),'","',-1) as request_uri , SUBSTRING_INDEX(SUBSTRING_INDEX(message,'","',12),'","',-1) as http_referer , SUBSTRING_INDEX(SUBSTRING_INDEX(message,'","',-1),'"',1) as http_user_agent from systemevents where SysLogTag='nginx:' ; delete from systemevents where id <= (select max(id) from weblogs) and SysLogTag='nginx:' ; optimize table systemevents ;
一共是3行,或者说3段吧。那个as xxx实际上没用,我是调试的时候看着方便的,可以删掉。
SysLogTag这个界定条件是因为实际上原始表systemevents里面有很多其他的日志,用这个界定一下,才能确保我们提出来的都是nginx的web日志。
第一段是把message的内容带上id根据我的表格式进行插入。有兴趣的朋友可以研究一下我的语句,嵌套SUBSTRING_INDEX以及首尾字段的处理,这些都是很有讲究的。能用好一些小技巧可以事半功倍,用不好就麻烦大了,自己写程序慢慢分解数据吧,麻烦到死……
第二段是以id为依据,把已经插入的内容从原表中删除。如果以大家常用的delete from xxx where yyy in ()这个路子,超过1000条就会慢到死了,所以我在插入的时候就把id一起填充了,那么删除的时候也可以用ID为依据,这样就实现了瞬间完成。
第三段就是把表优化一下,释放空间。
最终效果上面已经放了图,这样看日志就清晰多了,可以很方便的查出来哪些是不怀好意的,也可以快速统计出那些肉鸡的行为规律。
然后做什么,就是你自己琢磨的事了,这个周末我要干的就是把肉鸡的行为做个归纳,然后通过日志来收集肉鸡的IP,并填充给我那个封肉鸡Ip的api。