Dream4ever / Knowledge-Base

record every requirement and solution here
https://www.hewei.in/
36 stars 6 forks source link

IIS 日志统计分析 #177

Closed Dream4ever closed 2 years ago

Dream4ever commented 2 years ago

IIS 日志 vs. CNZZ 数据

自己在用 IIS 日志统计图书配套资源访问数的时候,统计的是每个 HTML 页面。而由于微信之类 APP 的缓存原因,有时用户打开页面时,不会向服务端请求 HTML,而是直接读取缓存中的 HTML。

但是 CNZZ 的统计脚本即使被缓存,也会照常向其服务器发送统计请求。

所以两相对比,用 IIS 日志统计出来的图书配套资源访问数,是比 CNZZ 要少的。但是 IIS 日志记录的请求信息更完整,比如用户原始 IP、浏览器 UserAgent 等等。

因此,在对外报告各业务访问数时,报告 CNZZ 统计出来的数据就行。对内统计访问数时,则报告 IIS 统计出来的数据。

方案对比:Node.js vs. Log Parser

相等查询

查询条件:HTTP 方法为 GET 请求,cs-uri-stem 值为 /xztl/index.htmlcs-uri-query 值为 subject=yy&grade=3&volume=a

# 格式说明见下面的基本语法一节
.\LogParser.exe `
"SELECT cs-uri-stem AS Stem, count(*) AS Hits `
FROM 'c:\Project\IIS Logs\W3SVC2_hxhz\log\u_ex*.log' `
WHERE cs-method = 'GET' `
AND Stem = '/xztl/index.html' `
AND cs-uri-query = 'subject=yy&grade=3&volume=a' `
GROUP BY Stem" `
-i:W3C

用好学好知 2021 年 4 月份的 IIS 日志进行查询,30 天的日志一共 17.6G,对比两种方案的速度。

可以看出来,在相等查询时,Node.js 比 Log Parser 快太多了。

相似查询

对于比较旧的图书配套资源,URL 是类似下面的格式:

www.xinlanzp.com/tspt/abcd/v(1).html

URL 中的 (1),代表着这里可以是任意数字,比如一本书制作了 10 个配套资源,这里就是 1~10。对于这种情况,自然就要用相似查询了。

.\LogParser.exe `
"SELECT DISTINCT cs-uri-stem AS Stem ` 
FROM 'c:\Project\IIS Logs\W3SVC8_xinlan\log\u_ex*.log' ` 
WHERE Stem LIKE '/tspt/abcd/v%.html'" `
-i:W3C

用新蓝网 2021 年 1~6 月 182 天的日志进行查询,共 3.7G。

经过对比,Node.js 再次完胜!以后做 IIS 日志统计就用 Node.js 了!

备用工具:Log Parser

参考资料:Log Parser Rocks! More than 50 Examples!

微软自己出的 Log Parser,查看前一节的对比数据,可知其效率比 Node.js 低很多。

日常使用 Log Parser 的话,可以用来查看某本书的配套资源都有哪些路径被访问到。

比如一本书被访问的静态资源包括 /tspt/abcd/v1.html,/tspt/abcd/video/1/1.ts。这样的话,就必须用 ' GET \/tspt\/abcd\/v\d+\.html/' 这个正则来匹配,如果用 ' GET \/tspt\/abcd\/v' 这个正则,就会将 HTML 页面之外的访问数也统计进来。

为了避免出现这种情况,就有必要先用 Log Parser 把 cs-uri-stem 字段先过一遍,看看 HTML 和其他静态资源都是什么格式的 URI,然后才好确定在 Node.js 中应该用怎样的方式处理。

!!重大问题!!

用下面的语句查询 2021 年 2 月某本图书的配套资源页面访问情况时,可以正常查询到结果。

# 格式说明见下面的基本语法一节
.\LogParser.exe `
"SELECT DISTINCT cs-uri-stem ` 
FROM 'c:\Project\IIS Logs\W3SVC8_xinlan\log\u_ex2102*.log' ` 
WHERE cs-uri-stem LIKE '/tspt/skjggyyzdbcjs/v%' ` 
ORDER BY cs-uri-stem" `
-i:W3C -rtp:-1

但如果日志范围由之前的 u_ex2102*.log 扩大到 u_ex*.log,也就是查询 2021 年全部的日志,那么输出结果为 0。目前还找不到问题的原因在哪里,为了避免此问题,之后用 Log Parser 查询半年或一年的日志时,每次只查询一个月的,而不是一次性查询完。

主要语法

# 为避免命令过长影响阅读,在 PowerShell 中
# 可以像下面这样,在每行的末尾加上空格再加上 ` 这个字符
# 也可以直接将下面的代码复制到 PowerShell 中,会自动换行
.\LogParser.exe `
# 选择所需查看的字段
"SELECT cs(User-Agent) As UserAgent `
# 设置日志路径,星号 * 为通配符
FROM 'c:\Project\IIS Logs\W3SVC4-gyxq\logs\u_ex*.log' `
# 设置筛选条件,这里的百分号 % 用于匹配任意多个字符,搭配 LIKE 做相似查找
WHERE cs-uri-stem LIKE '/tspt/xztx%index.html' `
# 设置分组条件
GROUP BY cs(User-Agent) `
# 设置排序标准
ORDER BY UserAgent ASC" `
# 设置 IIS 日志格式为通用格式,可解析增加了额外字段的日志
-i:W3C `
# 输出结果中不显示最后的统计信息
-stats:OFF `
# 用于终端自动输出所有结果,而不是按任意键才输出后面的 10 条结果
-rtp:-1 `
# 输出统计结果至文件中
 -o:CSV >> "c:\Project\IIS Logs\W3SVC4-gyxq\stats\xztxyy3a.csv" `

统计页面访问数

下面的代码,统计的是 2020 年 6 月,5个行知天下答案页面的访问数。

6 月份的 IIS 日志文件总体积为 2.94GB,共有 718 万多条的记录,Log Parser 用 19 秒的时间,就统计出了 5 个页面的访问数,效率还是很高的。

.\LogParser.exe `
"SELECT cs-uri-stem AS Uri, COUNT(*) `
FROM 'c:\Project\IIS Logs\W3SVC4-gyxq\2020-06\u_ex*.log' `
WHERE Uri = '/tspt/xztx/android-preview-pdf.html' `
OR Uri = '/tspt/xztx/download.html' `
OR Uri = '/tspt/xztx/mobile-read.html' `
OR Uri = '/tspt/xztx/mobile-read-android.html' `
OR Uri = '/tspt/xztx/preview.html' ` 
GROUP BY Uri"

Uri                                 COUNT(ALL *)
----------------------------------- ------------
/tspt/xztx/download.html            23819
/tspt/xztx/preview.html             44839
/tspt/xztx/mobile-read.html         32796
/tspt/xztx/mobile-read-android.html 240
/tspt/xztx/android-preview-pdf.html 82

Statistics:
-----------
Elements processed: 7188238
Elements output:    5
Execution time:     18.74 seconds

统计 UserAgent

下面的代码,统计的是行知天下下册二维码页面,独立的访客 UserAgent。将数据进一步处理,即可得知 Android、iOS 不同版本系统的用户数量及占比。

如果将该数据以月份为单位进行观察,则可进一步得知用户更换新设备的情况。

SELECT distinct cs(User-Agent) `
FROM '[LOGFILEPATH]' `
WHERE cs-uri-stem='/tspt/xztxkx3b/index.html' `
   OR cs-uri-stem='/tspt/xztxkx4b/index.html' `
   OR cs-uri-stem='/tspt/xztxkx5b/index.html' `
   OR cs-uri-stem='/tspt/xztxkx6b/index.html' `
   OR cs-uri-stem='/tspt/xztxkxa3b/index.html' `
   OR cs-uri-stem='/tspt/xztxkxa4b/index.html' `
   OR cs-uri-stem='/tspt/xztxkxa5b/index.html' `
   OR cs-uri-stem='/tspt/xztxkxa6b/index.html' `
   OR cs-uri-stem='/tspt/xztxyy3b/index.html' `
   OR cs-uri-stem='/tspt/xztxyy4b/index.html' `
   OR cs-uri-stem='/tspt/xztxyy5b/index.html' `
   OR cs-uri-stem='/tspt/xztxyy6b/index.html' `

统计带宽流量

下面的命令,可统计每单位时间内(1 分钟)服务器的带宽流量:

.\LogParser.exe `
"SELECT TO_LOCALTIME(QUANTIZE(time, 60)) as Minute, ` 
DIV(DIV(MUL(1.0, SUM(sc-bytes)), 131072), 60) as Mb ` 
FROM 'c:\Project\IIS Logs\temp\5.log' ` 
GROUP BY Minute ` 
ORDER BY Minute" `
-o:CSV >> "c:\Project\IIS Logs\temp\5.csv"

如果要更改单位时间为小时或者秒,把上面代码中的 60 改成 3600 或者 1 就行。

而上面的 131072,是因为 1048576 bytes 等于 1Mbytes 又等于 8Mbits,所以 bytes 换算成 Mbits 就是 1:131072 的关系。

参考资料:Average bandwidth per second by half hour

解析带 X-Forwarded-For 字段的日志

在用 Log Parser 解析 IIS 日志的时候,发现遇到带 X-Forwarded-For 字段的日志会解析失败,报错信息为:Unknown field X-Forwarded-For found in #Fields directive。

Google 了一下 logparser Unknown field X-Forwarded-For found in #Fields directive,在 Query IIS logs with extra fields using LogParser 中给出的建议是设置日志输入格式为 -i:W3C,试了一下果然解决问题了,示例代码如下:

.\LogParser.exe `
"SELECT cs-uri-stem AS Uri, COUNT(*) ` 
FROM 'c:\Project\IIS Logs\W3SVC6-AI\u_ex*.log' ` 
GROUP BY Uri" `
-i:W3C

IIS module | Elasticsearch

这个模块可以将 IIS 导入并进行分析,还会处理成能够在 Kibana 中进行可视化展示的数据。

参考链接:https://www.elastic.co/guide/en/beats/filebeat/master/filebeat-module-iis.html


无效数据

各种爬虫

含阿里云爬虫 UserAgent 特征值

cs(User-Agent) 字段包含 YisouSpider 的,都是阿里云的爬虫,这样的数据没有统计价值,在用 LogParser 处理时需要去除。

含阿里云 IP 的数据

还有的访问数据,IP 来源为阿里云,但 cs(User-Agent) 是下面这种看起来正常的类型,也需要排除。

这就需要将阿里云的 IP 从统计过程中去掉。

关键字:阿里云的IP段

参考链接:谁有阿里云所有的IP段

Mozilla/5.0+(Linux/Android/android/iphone;)

Mozilla/5.0+(Linux;+Android+4.1.1;+Nexus+7+Build/JRO03D)

Mozilla/5.0+(X11;+Ubuntu;+Linux+x86_64;+rv:36.0)+Gecko/20100101+Firefox/36.0

Mozilla/5.0+chrome(windows+nt/Windows+NT;)Chrome

校内 IP

石大(华东)网络 IP 地址

可用工具

CIDR 与 IPv4 地址间的转换


自研系统实现思路

事件模型设计思路(案例)

parse 不用关心数据接口,client 端随意传入相关结构即可。

但是,建议 client 要为每一种类型的数据设计一个数据模型。

也可以把每条数据称为事件模型。

事件模型用来描述操作行为,事件模型包括事件(Event)核心实体。

Event 实体

一个 Event 描述内容为:一个用户在某个时间点,某个地方,以某种方式完成了某个具体的事情。从这可以看出,一个完整的 Event,包含了如下关键因素。

Who: 用户 ID
When: 操作 api 的时间
Where: ip
How: 设备型号(例如:iphone6/chrome)
What: 描述用户所做的时间的具体内容

代码中对应的字段如下:

{
  app_id: 'xxxxx', // string ,required
  operate_user_id: user_id, // string, required
  operate_user_name: user_name, // string, required
  time: new Date().toString() // string, required
  ip: '192.168.1.1' // string, required
  model: 'iphone6', // string, required
  env: 'dev', // string, required
  api: '/updateCarInfo', // string, required
  event: {  // required
    action: '修改用户信息',  // string, required
    comment: '' //string, required
    order: 'xx', // string
    car: 'xx',  // string
    user_id: '' //string
    user_name: '' // string
  },
}

required 字段必须强制带上。

你也可以扩展可选字段。拓展可选字段根据具体需求。

comment 字段作为描述字符串存在,尽可能表述出前后数据的差异。