想分析一下具体日志。
具体环境为前端Nginx进行反代,但是可能会因为某线路抽风而更换线路更换机房什么的,所以前端地址不固定,也就决定了访问和日志有可能在某个时段存在于多个节点。后端一般不动。所以如果用传统方法去捣鼓个awstats来每节点分析的话,容易产生日志数据不同步,各玩各的,以及较复杂等缘故。
吐槽一下,我看到有人说用shell来分析超过100G的日志,但我觉得太扯了,所谓关系型数据库,在数据之间有关联有关系才会发挥更有效的作用,一味的抱残守缺没什么意思,感觉就跟至今还抱着Windows XP不放的心态一样。
本文内容可以很容易的切换为redis等方式,但我想观察较长周期的细节数据,所以sql类的较合适。
本文环境为Debian 9,Nginx,数据库为远程MySQL或MariaDB,Python用的版本3.x
所有站点日志统一存放为/var/www/logs/
所有写入日志的站点都可以顺带入库,只需要指定日志位置以及文中定义的格式。因为sql库中单独列了主机名这个表,所以在入库的时候并不需要区分某个站点。在后续进行数据分析以及展现的时候再处理这个。
因为实际的访问发生在前端,所以后端是不需要分析日志的,本文所列的日志处理内容仅针对接受访问的节点进行。
默认日志的时间格式有点难受,如果自行用python处理则整个流程复杂度需要大幅度提升,同时需要考虑到要将数据集中处理等因素,细节都在文中提及。
日志格式:
首先要处理Nginx的日志格式问题,默认的日期时间格式无法更改,找到了一个挺爽的方法,用lua做一个变量,日志里面用这个变量即可:
http段:
log_format main '"$remote_addr","$server_addr","$fmt_localtime","$scheme","$server_protocol","$request_method","$server_name","$status","$sent_http_content_type","$body_bytes_sent","$request_uri","$http_referer","$http_user_agent"'; map $host $fmt_localtime { default ''; } log_by_lua_block { ngx.var.fmt_localtime = ngx.localtime(); }
server段的access日志加上这个main就可以了:
access_log /var/www/logs/www.tingtao.org_access.log main;
然后清除一次日志:
rm /var/www/logs/* kill -USR1 `cat /run/nginx.pid`
入库:
写了个简单的python文件logs.py:
import string, os, sys db_server='1.2.3.4' db_usr='数据库用户名' db_pwd='数据库密码' db_dbname='数据库名' flist=[] for root, dirs, files in os.walk("/var/www/logs"): for name in files: if name.find("access")>=0 : name2=name+'_tmp' flist.append(os.path.join(root, name2)) os.rename(os.path.join(root, name),os.path.join(root, name2)) os.system('kill -USR1 `cat /run/nginx.pid`') #入库 for sname in flist: cmd='mysql -h ' +db_server+ ' -u ' +db_usr+ ' -p' +db_pwd+ ' --database=' +db_dbname+ ' --local-infile=1 -e \"load data local infile \'' +sname+ '\' into table logs fields terminated by \',\' enclosed by \'\\\"\' (remote_addr,server_addr,fmt_localtime,scheme,server_protocol,request_method,server_name,status,sent_http_content_type,body_bytes_sent,request_uri,http_referer,http_user_agent)\"' os.system(cmd) os.remove(sname)
思路:首先将现有文件改名,然后让nginx重新打开日志文件,这个过程是瞬间完成的,我猜在极端情况下也许会有那么一两条丢失吧,面对海量数据,真就是丢那么一两个,没所谓的。目前流行的日志切割方法也都是这个路数。
crontab -e设定5分钟一次:
*/5 * * * * /usr/bin/python3 /root/works/logs.py
建库的sql代码:
CREATE TABLE `logs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `remote_addr` varchar(150) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `server_addr` varchar(150) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `fmt_localtime` datetime(6) DEFAULT NULL, `scheme` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `server_protocol` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `request_method` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `server_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `status` int(255) DEFAULT NULL, `sent_http_content_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `body_bytes_sent` int(255) DEFAULT NULL, `request_uri` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `http_referer` varchar(4000) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `http_user_agent` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
字段还没具体调整,可以优化一下的,先将就着用,而且字符编码也可以用gbk或者ascii之类的简单编码,因为这个库里面不可能出现双字节字符,所以这样可以提升效率。
后续的,当数据量巨大的时候,需要考虑数据库压力和性能问题,比如分区什么的也要搞了,我这是因为mysql是个单独的物理机,性能无忧,至少短期内不需要考虑这个事。而且我这应该不会很长时间来分析日志,只是想具体了解一下流量来源,但是免费的cdn不提供这个功能,才自己干的。
先这样吧,今天累了,回头再继续捣鼓。