想分析一下具体日志。

 

具体环境为前端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不提供这个功能,才自己干的。

 

先这样吧,今天累了,回头再继续捣鼓。

作者 听涛

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注