前言

因为在猪哥的微信公众号里面看到了一篇关于可视化的文章Flask + echarts 轻松搞定 nginx 日志可视化,于是也想做一个数据可视化的网页。由于我之前做了一个脚本,正式经过nginx代理的,所以想通过这个数据来进行可视化的小实验。

零、思路

nginx的访问日志记录了每次客户端的请求,包括ip、时间、客户端信息等。由于这些日志文件在服务器中,所以先用shell将这些数据上传到我的github仓库中。为了保证数据的时效性,可以采用定时收集并上传的方法。

数据文件在仓库中,可以通过xhr请求获得这些数据文件(刚好同源),然后将这些解析整理,得到干净的统计数据。再使用echarts将这些数据通过图表的形式展现出来。


理想很丰满,现实很骨感。
我发现用js来处理数据简直就是***(咳咳),再加上把nginx的访问日志直接上传到仓库也是不安全的。所以处理数据只能放在服务器上单独进行,然后再将处理好的、安全的数据上传到仓库中。

所以一共分为两个方面,一方面是对数据进行处理,一方面是对数据进行展示。

一、处理数据

a.数据的分析和处理

1、获取数据文件

从nginx的配置文件/etc/nginx/nginx.conf中可以看到日志文件的地址。
文件显示我的日志文件在/var/log/nginx/里面,部分文件目录如图。
nginx-logfile

取其中一行进行分析

14.215.177.39 - - [20/Sep/2020:13:11:31 +0800] "GET /sm/temp?taskId=403&questionId=5642 HTTP/1.1" 200 56 "https://www.baidu.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0"

一看就知道写的是啥,多的也不解释了。

2、解析

这里解析我选的是python来做,简单又方便。直接用正则提取每一项数据

import re

obj = re.compile(
    r'(?P<ip>.*?)- - \[(?P<time>.*?)\] "(?P<request>.*?)" (?P<status>.*?) (?P<bytes>.*?) "(?P<referer>.*?)" "(?P<ua>.*?)"')
)

result = obj.match(line)  # line 为一行日志信息

ip = result.group('ip')
time = result.group('time')
request = result.group('request')
...

3、统计省份

根据ip地址查询省级地址。可在网上找相应的api进行查询。
(由于最开始想用js查询,但是由于同源政策的限制,简单的xhr无法实现跨域请求。遂使用script标签实现跨域并回调,但是依然很麻烦,所以我还是选python)
为了不大量发起api的请求(尽管数据量并不大),将每次的ip地址对应的省份写入文件,在查询地址时,可以先从文件中查找。

ip_cache = {}

# 通过对文件的读取获得已查询过的ip地址并存为字典
with open('ip_cache.txt', 'rw', encoding='gbk')as f:
    data = f.readline()
    while data:
        ip, province = data.strip().split('\t')
        ip_cache[ip] = province
        data = f.readline()

def ip2province(ip):
    province = ip_cache.get(ip)
    if province is not None:
        return province
    try:
        # 到这说明没有记录该ip,所以发起请求
        ...

4、分析数据

数据已经解析出来了,但仍然是很粗糙的,不能直接扔到仓库,得先统计并处理一下。
将每一行的数据整理成字典,然后存入列表中,让数据处理大师Pandas来调教一下。

import pandas as pd

def analyse(data):
    # type(data)  // list
    df = pd.DataFrame(data)

    # 统计省份
    province_count_df = pd.value_counts(df['province']).reset_index().rename(
        columns={"index": "name", "province": "value"}
    )

    # 统计相同省份的数据量, 如:{"name": "四川", "value": 980}
    # 考虑到展示使用echarts,所以数据字典改为name、value形式

    ...

5、导出数据

将得到的数据直接转化为json语句,然后保存到相应的文件中,并按日期进行分类(不然数据文件太多太杂,查看很不方便)。

import pandas as pd

...

province_json = province_count_df.tp_json(orient='records')
with open('province.json', 'w', encoding='gbk')as f:
    f.write(province_json)
...

6、定时对数据进行更新和上传

在上面我们已经通过python将数据都处理得差不多了,接下来就要将这些生成的数据文件上传到git仓库中去了。
写个sh脚本来执行Python文件和上传数据文件

#! /bin/bash

message=`date +"%Y_%m_%d-%H_%M"`

cd /usr/user/sm_question/smdisplay/data/

# 执行代码,生成数据文件
python3 date.py

sleep 5

cd ..

# 先同步一下git仓库
git pull

sleep 10

# 上传数据文件到仓库
git add *.json
git commit -m "$message"
git push

开启定时crontab -e ,每小时更新

00 * * * * /usr/user/sm_question/smdisplay/auto.sh >>/usr/user/sm_question/smdisplay/mycron.log 2>&1

后面语句是为了更方便的查看执行情况(其实是我没找到crontab的执行日志)

7、一些细节问题

  • 写入数据文件时,确保已经存在相应的日期文件夹
  • 路径上的问题
  • 编码问题

b、数据的获取和上传

1、截取数据

在上面的图片中,我们已经看到日志文件也有不少文件是被打包了的,也就是说,access.log中的日志文件可能达到一定大小就会自动被打包,而我们直接获取access.log中的数据,可能在日期上很混乱。所以每天进行分割打包是最方便的了。

所以,我们将access.log文件在每天凌晨进行处理,也就是说,access.log文件中,只有当天的日志数据,而以前的数据已经被打包到相应的压缩包中。
写个脚本,盘他:

#! /bin/bash

yesterday=`date -d '-1 day' +%Y%m%d`  # 获取昨天的日期

# 由于定时任务执行的路径不在sh文件路径上
# 所以要调整一下路径,避免不必要的麻烦
cd /var/log/nginx

# 将昨日的日志文件命名为当日日期
mv access.log $yesterday.access.log
mv error.log $yesterday.access.log

# 打包
gzip $yesterday.access.log
gzip $yesterday.error.log

# 执行此操作是为了让以后的日志写入access.log中
# 不执行此操作将无法获得日志数据
kill -USR1 `cat /run/nginx/pid`

2、定时

定时的方法,以前的文章也提到过。
crontab -e 打开文件,将上面的shell脚本加入定时任务中去。

00 01 * * */var/log/nginx/splitperday.sh >>/var/log/nginx/mycron.log 2>&1

二、数据展示

没有同源政策的限制,直接发起xhr请求获得数据,再进行展示即可。
鉴于自己写的页面,没有任何美感,所以数据展示部分就不再写下去了。以后有时间,会写一些echarts相关的东西,算是给这个部分补上一些。

省份地图

饼状图客户端情况.png

不放图了,丢人。。。。。

以后打算用一些框架来写页面,可能会比现在的页面美观一点吧!

正文结束了


三、感想

从开始做到第一版正式结束,总共花了三天时间,除了吃饭睡觉,都在弄这个,所以算起来花的时间也不算少。我的感觉仍然是效率低下以及思维上的不足。

我是周三(20200916)下午左右吧才开始有这个想法的,当时构思就是如下:

1.nginx获得数
2.上传到公用仓库
3.使用api或其他方法传递数据
4.建立图表
5.展示相关数据的访问量
6....

一想到自己要实现一个数据图形化的网页,还挺激动和兴奋的。迫不及待的都想马上开搞。我默认自己应该在周四周五这两天把这些东西大致做出来,以为这两天正好没课,全身心投入。

但后来实际做的时候,发现想的还是太简单了。

最开始,我直接将access.log文件中的部分数据复制到html文件中,然后用js进行数据处理。

当然,第一步是提取数据。可能是被兴奋感冲昏了头脑吧,我想的是通过正则直接提取这堆数据的全部ip、全部时间、全部……。

当我费尽九牛二虎之力,终于完整无误的将这些数据全部提取出来并以数组的形式保存为变量,即ip=['123.123.2.2', '114.231.412.3', ...], time=['16/Sep/2020:13:11:31', '16/Sep/2020:13:11:31', ...], ...的时候我突然发现其中有些我不需要的请求数据,因为我想统计的是某个路径下的数据,所以如果我删除某个数组的某个数据,那意味着我还得删除其他所有数组相对应的数据,这让我不知所措,我感到代码越来越复杂了。

这是其中之一,另外一个就是ip地址转省份的操作了。

js效率问题,处理数据方面等加之考虑到安全问题,所以将数据处理安排在服务器上,再说python有潘大师坐镇,何苦不能简单快速的处理数据呢!

写这篇文章居然写了3个多小时,哎,效率仍旧不行!