Spark日志分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/bingoxubin/article/details/86671026

1. 概述

当浏览器请求服务器时,如果在服务器上设置了访问日志,就会记录下用户的访问记录。在日志里,通常包含大量的信息,但是这些信息不太容易被利用,这里我们通过对Apache的access.log日志进行分析,来进一步的学习Spark下的程序开发。

2. 假定需求

假设给我们提供一份apache的access.log文件,根据业务需求,我们需要分析得到以下几方面的需求:
1.统计每天的页面访问量
2.统计每种不同的HTTP状态对应的访问数
3.统计不同独立IP的访问量
4.统计不同页面的访问量

3. 准备工作

3.1 日志文件下载

下载指定分析的日志,当然也可以使用自己真实的Apache日志,在tomcat的logs目录中,为了让分析结果更加的直观明显,还是推荐使用下载日志。
Apache access.log日志下载地址:http://labfile.oss.aliyuncs.com/courses/456/access.log

3.2 日志文件格式

下载后的日志文件打开后类似这样:

在这里插入图片描述

Apache 日志内容从左到右依次包括如下内容:
远程IP地址
客户端记录
浏览者记录
请求的时间,包括日期,时间,时区
服务器收到的请求,包括请求方法(GET/POST),请求的目标链接地址,HTTP版本号
请求状态代码,表示请求是否成功
发送的字节数
发出请求时所在的URL
客户端的详细信息,操作系统及浏览器等

下面截取一条,对比一下:
在这里插入图片描述

4. 实验

4.1 上传日志文件

把下载的日志文件access.log,上传到linux虚拟机的/opt/spark/log目录,当然,在系列一文档中并没有创建log目录,这里需要手动创建一下。

在这里插入图片描述

4.2 启动Spark

在系列一中已涉及spark的安装启动,这里简单重提下命令。
启动主节点:

# cd /opt/spark/spark-1.5.1-bin-hadoop2.6/sbin/
# sh start-master.sh

在这里插入图片描述
启动从节点:

# sh start-slave.sh spark://xubincc:7077

在这里插入图片描述
通过浏览器访问spark,地址为http://ip:8080
在这里插入图片描述

4.3 启动spark shell或者pyspark

启动spark shell,准备使用scala方式解决,当然也可以使用pyspark的python,这里不涉及:
# MASTER=spark://xubincc:7077 spark-shell

在这里插入图片描述

4.4 引入日志文件

# val logFile = "/opt/spark/log/access.log"
# val logRDD = sc.textFile(logFile)

在这里插入图片描述

4.5 Apache日志解析

设计正则表达式对日志文件进行解析,并返回解析后的元组,正则表达式设计如下:

val logPattern = """^(\\S+) (\\S+) (\\S+) \\[([\\w/]+)([\\w:/]+)\s([+\\-]\\d{4})\\] \"(\\S+) (\\S+) (\\S+)\" (\\d{3}) (\\d+)""".r

针对该系列提出的需求场景,我们只需要下面几项数据:

1. 客户端IP地址:正则表达式解析的第1项
2. 请求日期:正则表达式解析的第4项
3. HTTP状态码:正则表达式解析的第8项
4. 请求的URL:正则表达式解析的第10项
为了避免一些杂乱的无法解析的数据干扰,我们使用正则表达式做两件事情,一个是过滤掉无法解析的日志,一个是解析获得需要的数据元组。

这里我们需要实现两个函数:

1. filterWithParse:过滤无法解析的日志记录
2. parseLog:解析日志记录,得到所需信息的元组
具体scala代码如下:

val logPattern = """^(\S+) (\S+) (\S+) \[([\w/]+)([\w:/]+)\s([+\-]\d{4})\] "(\S+) (\S+) (\S+)" (\d{3}) (\d+)""".r

 def filterWithParse(s: String) = { 
    logPattern.findFirstIn(s) match { 
        case Some(logPattern(_*)) => true
        case _ => false
    } 
}

def parseLog(s: String) = { 
    val m = logPattern.findAllIn(s) 
    if(m.hasNext){
        val clientIP = m.group(1).toString 
        val requestDate = m.group(4).toString 
        val requestURL = m.group(8).toString 
        val status = m.group(10).toString 
        (clientIP, requestDate, requestURL, status) 
    } else { 
        ("null", "null", "null", "null") // 为了便于分析数据,此处用一个内容为“null”的字符串来统计无效值 
    } 
}

在这里插入图片描述
执行第一次map操作,获得解析后的(客户端IP,请求日期,请求URL和HTTP状态码)的信息内容

# val logRDDv1 = logRDD.filter(filterWithParse).map(parseLog)

在这里插入图片描述
该元组中的元素为后面所有的分析提供基础。

5. 统计结果

5.1 统计每日页面访问量

首先我们统计日志文件中访问的总次数:
# logRDDv1.count

在这里插入图片描述

根据日期进行map及reduce操作,获得每日的页面访问数:
# val logRDDv2 = logRDDv1.map(x => (x._2, 1)).reduceByKey(_ + _) 
# logRDDv2.sortByKey().saveAsTextFile("/opt/spark/log/resultPV")

在这里插入图片描述

使用sortByKey根据日期进行升序排序,并把输出写入到/opt/spark/log/resultPV里。
查看结果如下图所示:

在这里插入图片描述

5.2 统计每种不同的HTTP状态对应的访问数

一次map获得(HTTP访问码,1)的元组RDD,然后直接reduce操作:
# val logRDDv6 = logRDDv1.map(x => (x._4, 1)).reduceByKey(_ + _)
# logRDDv6.sortByKey().collect()

在这里插入图片描述

列表中存储了日志中出现的HTTP状态码及对应的访问次数,并根据HTTP状态码做了排序,可以看到200是最常见的状态码。

5.3 统计不同独立IP的访问量

与上一节的统计HTTP状态码相同,先map到一个(IP地址,1)的RDD,然后进行reduce后排序输出,需要注意如果使用sortBy函数中需要指明用元组的第二项进行排序,并采用倒序排列:
# val logRDDv7 = logRDDv1.map(x => (x._1, 1)).reduceByKey(_ + _) 
# logRDDv7.sortBy(x => x._2, false).collect()

在这里插入图片描述

列表中记录了所有访问的IP地址及对应的访问次数,并按照访问次数从多到少进行了排序。

5.4 统计不同页面的访问量

与上一节的统计IP访问量相同,先map到一个(requestURL,1)的RDD,然后进行reduce后排序输出,需要注意如果使用sortBy函数中需要指明用元组的第二项进行排序,并采用倒序排列:
# val logRDDv8 = logRDDv1.map(x => (x._3, 1)).reduceByKey(_ + _) 
# logRDDv8.sortBy(x => x._2, false).collect()

在这里插入图片描述

列表中记录了所有访问的IP地址及对应的访问次数,并按照访问次数从多到少进行了排序。

猜你喜欢

转载自blog.csdn.net/bingoxubin/article/details/86671026