文章目录
- 三、日志分析
- 1.统计Web服务器返回的内容大小
- 2.统计不同的状态代码出现的次数
- 3.查看频繁访问的客户端主机(访问大于10次)
- 4.查看被访问最多(前10)的资源标识符
- 5.查询状态码不是200中访问次数最多的资源标识符(前10)
- 6.统计独立主机数
Apache日志分析
一、日志格式
这里用到的服务器日志格式是 Apache Common Log Format (CLF)。
简单数来,你看到的每一行都是如下的样式:
字段说明如下:
- 127.0.0.12
第一项 ,发起请求的客户端IP地址。
- -
第二项 ,空白,用占位符“-
”替代,表示所请求的信息(来自远程机器的用户身份)。
- -
第三项,空白,表示所请求的信息(来自本地登录的用户身份)。
- [01/Aug/1995:00:00:01 -0400]
第四项,服务器端处理完请求的时间,具体细节如下:
- [day/month/year:hour:minute:second timezone]
day = 2 digits
month = 3 letters
year = 4 digits
hour = 2 digits
minute = 2 digits
second = 2 digits
zone = (+ | -) 4 digits
- "POST /images/launch-logo.gif12 HTTP/1.0"
第五项,客户端请求字符串的第一行,包含3个部分。
- 1)请求方式 (e.g., GET, POST,HEAD 等)
- 2)资源;
- 3)客户端协议版本,通常是HTTP,后面再加上版本号。
- 200
第六项,服务器发回给客户端的状态码,这个信息非常有用,它告诉我们这个请求成功得到response(以2开头的状态码),重定向(以3开头的状态码),客户端引起的错误(以4开头的状态码),服务器引起的错误(以5开头的状态码)。
- 1850
第七项,这个数据表明了服务器返回的数据大小(不包括response headers),当然,如果没有返回任何内容,这个值会是”-” (也有时候会是0)。
返回顶部
二、日志解析
1.主要步骤
1.通过观察数据的格式,可以发现一行的数据是按照空格进行分隔的,并且在时间、客户请求两个字段中均包含有空格分隔符,所以我们就不能够单纯的以空格为分隔符进行拆分
。
2.对于数据第七项服务器返回的数据大小中出现的“-
”,我们可以全用0
替代
3.对于时间字段,我们需要进行调整。主要是两步:更换月份信息、调整时间格式
4.对于客户请求字段,我们需要进一步进行拆分,将其分为请求、站点、协议三个字段
返回顶部
2.数据清洗代码实现解析
2.1 环境准备
- 这里我们首先配置好
SparkContext
和SparkSession
,以及导入隐式转换
和SparkSQL的函数集
。 - 隐式转换就是:当Scala编译器进行类型匹配时,如果找不到合适的候选,那么隐式转化提供了另外一种途径来告诉编译器如何将当前的类型转换成预期类型。
- 这里主要用途就是将
scala的Row
对象转为DataFrame[]
类型的数据。
- org.apache.spark.sql.functions是一个Object,提供了约两百多个函数。除UDF函数,均可在spark-sql中直接使用。经过
import org.apache.spark.sql.functions._
,也可以用于Dataframe
,Dataset
。
返回顶部
2.2 数据清洗解析
2.3.1 整体框架
整体的处理分为:读取数据、解析数据、输出数据三大部分
- 读取数据:利用
sparkContext的textFile()方法
将数据文本获取为RDD[Row]
类型 - 解析数据:针对
RDD[Row]
类型处理,每次处理的是RDD中的单个Row对象数据,也就是原数据集中的每一行封装成了Row对象;解析式利用map()
进行逐行清洗。 - 输出数据:处理好的数据最终依然为
RDD(Row)
类型,我们利用隐式转换
和toDF()方法
将其转为DataFrame
类型(表结构),并指定表头字段,通过csv()
方法最终将结果以.csv
的文件存储。
返回顶部
2.3.2 解析数据
- 在处理过程中,处理方式首先是将数据中的 “-“ 进行 0 替换,然后对 [] 进行 ” “ 替换,这样的替换主要是利用之前的正则(保留” “中的分割符不被拆分)
- 在处理时间数据的时候,定义了一个方法,对原有数据进行拆分,依次截取时间分段数据;其中利用Map对月份数据进行了替换。最后整合以字符串的方式返回。后续如果用到可以使用SparkSQL进行实时转换。
- 接着针对客户请求数据进行拆分
利用map函数进行整体数据的清洗处理:
- 首先定义变量
line
获取到单行数据(也就是data.item
),格式:127.0.0.1 - - [01/Aug/1995:00:00:01 -0400] “GET /images/launch-logo.gif HTTP/1.0” 200 1839 - 初步进行字符替换
[ ]
转换为 “ ”
,格式:127.0.0.1 - - “01/Aug/1995:00:00:01 -0400” “GET /images/launch-logo.gif HTTP/1.0” 200 1839 - 然后利用正则匹配拆分是以空格拆分并且保留
“ ”
中的空格,格式:
0:127.0.0.1
1:-
2:-
3:“01/Aug/1995:00:00:01 -0400”
4:“GET /images/launch-logo.gif HTTP/1.0”
5:200
6:1839 - 处理
“01/Aug/1995:00:00:01 -0400”
部分的数据,利用substring截取
引号中的数据 :01/Aug/1995:00:00:01 -0400 - 通过自定义方法,进行日期格式的转换;主要就是利用字符截取拼接,其中月份需要进行匹配转换, 最终转换为:1995-01-08 00:00:01 进行返回
- 接着同样提取POST /images/launch-logo.gif HTTP/1.0 进行拆分提取字段,这里要进行判断,原始数据中这一部分会有脏数据的出现,提取后针对
protocol
进一步处理,将 “-”
换成 “0”
- 最后返回
(line(0), line(1), line(2), date, method, endpoint, protocol, line(5), line(6))
分别对应 ("host", "client_id", "user_id", "date_time", "method", "endpoint", "protocol", "response_code", "size")
- 有注意到,开头定义了两个变量
count_valid
、count_invalid
用来计数,分别统计合规数据与非法数据,非法数据处理直接返回(line(0), line(1), line(2), date, "null", "null", "null", line(5), line(6))
toDF()转换处理后的数据成DataFrame类型(表结构),同时指定对应字段名称:
这里我们去除非法数据集,保留有效数据:
注意以下特殊的不合规记录:
返回顶部
三、日志分析
接下来通过以上处理的数据进行进一步分析,主要采用SparkSQL的方式~
1.统计Web服务器返回的内容大小
查询size列信息,通过cast()进行类型转换后使用聚合函数agg()进行max()、min()、avg()的聚合操作。
返回顶部
2.统计不同的状态代码出现的次数
查询response_code列,按照response_code分组聚合,count()求出每组的数量,不同的组别对应的就是不同的response_code。
返回顶部
3.查看频繁访问的客户端主机(访问大于10次)
查询host列信息,并进行groupBy分组agg(count())聚合,求得每组(每个host)的频数,然后进行orderBy排序,查出where条件频数大于10的记录。
返回顶部
4.查看被访问最多(前10)的资源标识符
查询endpoint列信息,分组聚合后降序排序,limit取出前10。
返回顶部
5.查询状态码不是200中访问次数最多的资源标识符(前10)
查询response_code、endpoint列信息,查询response_code不为200的记录,按照endpoint分组聚合计数,再按照count倒序排序,limit取出前10。
返回顶部
6.统计独立主机数
针对host列进行去重计数,可以直接使用 DataFrame.dropDuplicates() 方法。
返回顶部
参考1:Spark大数据分析实验(三)—— Spark网络日志分析
参考2:Apache Web服务器日志格式