基于SparkSql的日志分析实战

本文介绍了如何使用SparkSql进行日志分析,包括数据清洗、日志解析、统计最受欢迎的视频及课程,以及通过ECharts和Zeppelin进行数据可视化展示。文章详细讲解了离线数据处理流程,从数据采集、清洗到结果入库,并讨论了统计过程中的优化技巧。
摘要由CSDN通过智能技术生成

目录

日志数据内容

用户行为日志分析的意义

离线数据处理流程

需求分析

数据清洗

解析访问日志

使用github上的开源项目

对日志进行统计分析 

统计最受欢迎的TOPN的视频访问次数

按照地市统计imooc主站最受欢迎的TOPN课程

按流量统计imooc主站最受欢迎的TOPN课程

调优点

EChart展示图形化界面

静态数据展示

动态数据展示


日志数据内容

  1. 访问系统属性,操作系统,浏览器
  2. 访问特征,点击的url,从那个url跳转过来的,页面的停留时间
  3. 访问信息,session_id,访问ip(访问城市)

用户行为日志分析的意义

  • 网站的眼睛
  • 网站的神经,在网站的布局是否合理,导航是否清晰,内容是否合理
  • 网站的大脑,分析的目标,要做哪些优化。

离线数据处理流程

  • 1)数据采集 flume来采集
  • 2)数据清洗,采集过来的数据有很多不符合日志规范的脏数据
  • 3)数据处理。 按照我们的需求进行相应业务的统计的分析
  • 4)处理结果入库  结果可以存放到RDBMS\noSQl
  • 5)数据可视化展示  ECharts\HUE\Zeppelin

需求分析

  1. 统计imooc主站最受欢迎的课程/手机的TOpN访问次数
  2. 按照地市统计imooc主站最受欢迎的TOPN课程
  3. 按流量统计imooc主站最受欢迎的TOPN课程

数据清洗

  1. 需要先将日志文件中的日期格式进行转换

simpleDataformat是线程不安全的。


 
 
 
  1. package com.rachel
  2. import java.text.SimpleDateFormat
  3. import java.util.{Date, Locale}
  4. import org.apache.commons.lang3.time.FastDateFormat
  5. object DateUtils {
  6. //输入文件日期格式
  7. val YYYYMMDDHHMM_TIME_FORMAT = FastDateFormat.getInstance( "dd/MMM/yyyy:HH:mm:ss Z",Locale.ENGLISH)
  8. //目标格式
  9. val TARGET_FORMAT = FastDateFormat.getInstance( "yyyy-MM-dd HH:mm:ss")
  10. def parse(time:String)={
  11. TARGET_FORMAT.format( new Date(getTime(time)))
  12. }
  13. def getTime(time:String) ={
  14. try{
  15. YYYYMMDDHHMM_TIME_FORMAT.parse(time.substring(time.indexOf( "[")+ 1,
  16. time.lastIndexOf( "]"))).getTime
  17. } catch {
  18. case e:Exception =>{
  19. 0l
  20. }
  21. }
  22. }
  23. def main(args: Array[String]): Unit = {
  24. println(parse( "[10/Nov/2016:00:01:02 +0800]"))
  25. }
  26. }

 
 
 
  1. package com.rachel
  2. import org.apache.spark.sql.SparkSession
  3. import org.apache.spark.SparkContext
  4. object SparkStatFormatJob {
  5. def main(args: Array[String]): Unit = {
  6. val spark = SparkSession.builder().appName( "SparkStatFormatJob").master( "local[2]").getOrCreate()
  7. val access = spark.sparkContext.textFile( "file:///f:/java/10000_access.log")
  8. //access.take(10).foreach(println)
  9. access.map(line => {
  10. val splits = line.split( " ")
  11. val ip = splits( 0)
  12. val time = splits( 3)+ " "+splits( 4)
  13. val url = splits( 11).replaceAll( "\"", "")
  14. val traffic = splits( 9)
  15. // (ip,DateUtils.parse(time),url)
  16. DateUtils.parse(time)+ "\t"+url+ "\t"+traffic+ "\t"+ip+ "\t"
  17. }).saveAsTextFile( "file:///f:/java/output_10000_access.log")
  18. spark.stop()
  19. }
  20. }

解析访问日志

  • 使用SparkSql解析访问日志
  • 解析出课程编号、类型    
  • 根据IP解析出城市信息 val city = IpUtils.getCity(ip)
  • 使用SparkSql将访问时间按天进行分区。 

 
 
 
  1. package com.rachel
  2. import org.apache.spark.sql.Row
  3. import org.apache.spark.sql.types.{LongType, StringType, StructField, StructType}
  4. object AccessConvertUtil {
  5. val struct = StructType {
  6. Array(
  7. StructField( "url", StringType),
  8. StructField( "cmsType", StringType),
  9. StructField( "cmsId", LongType),
  10. StructField( "traffic", LongType),
  11. StructField( "ip", StringType),
  12. StructField( "city", StringType),
  13. StructField( "time", StringType),
  14. StructField( "day", StringType)
  15. )
  16. }
  17. /**
  18. * 根据输入的每一行信息转换成输出的样式
  19. * @param log 输入的每一行记录信息
  20. */
  21. def parseLog(log:String) = {
  22. try{
  23. val splits = log.split( "\t")
  24. val url = splits( 1)
  25. val traffic = splits( 2).toLong
  26. val ip = splits( 3)
  27. val domain = "http://www.imooc.com/"
  28. val cms = url.substring(url.indexOf(domain) + domain.length)
  29. val cmsTypeId = cms.split( "/")
  30. var cmsType = ""
  31. var cmsId = 0l
  32. if(cmsTypeId.length > 1) {
  33. cmsType = cmsTypeId( 0)
  34. cmsId = cmsTypeId( 1).toLong
  35. }
  36. val city = IpUtils.getCity(ip)
  37. val time = splits( 0)
  38. val day = time.substring( 0, 10).replaceAll( "-", "")
  39. //这个row里面的字段要和struct中的字段对应上
  40. Row(url, cmsType, cmsId, traffic, ip, city, time, day)
  41. } catch {
  42. case e:Exception => Row( 0)
  43. }
  44. }
  45. }

 
 
 
  1. package com.rachel
  2. import org.apache.spark.sql.{SaveMode, SparkSession}
  3. object SparkStatCleanJob {
  4. def main(args: Array[String]): Unit = {
  5. val spark = SparkSession.builder().appName( "SparkStatCleanJob")
  6. .config( "spark.sql.parquet.compression.codec", "gzip")
  7. .master( "local[2]").getOrCreate()
  8. val accessRDD = spark.sparkContext.textFile( "file:///f:/java/access.log")
  9. val accessDF = spark.createDataFrame(accessRDD.map(x => AccessConvertUtil.parseLog(x)),
  10. AccessConvertUtil.struct)
  11. // accessDF.printSchema()
  12. // accessDF.show(false)
  13. accessDF.coalesce( 1).write.format( "parquet").mode(SaveMode.Overwrite)
  14. .partitionBy( "day").save( "file:///f:/java/imooc/clean2")
  15. }
  16. }

这一过程中使用了ipdatabase项目来解析ip地址。所以需要在自己的项目中使用别人的项目。

使用github上的开源项目

  1. git clone https://github.com/wzhe06/ipdatabase
       
       
       
  2. 
       
       
       
    1. #编译下载的项目(需要切换到项目目录下)
    2. mvn clean package -DskipTests
  3. 
       
       
       
    1. #将依赖的jar包导到自己的maven仓库
    2. mvn install:install-file -Dfile=F:/开发文档/ipdatabase/target/ipdatabase-1.0-SNAPSHOT.jar -DgroupId=com.ggstart -DartifactId=ipdatabase -Dversion=1.0 -Dpackaging=jar

对日志进行统计分析 

统计最受欢迎的TOPN的视频访问次数


 
 
 
  1. package com.rachel
  2. import org.apache.spark.sql.{DataFrame, SparkSession}
  3. import org.apache.spark.sql.functions._
  4. object TopNStatJob {
  5. def main(args: Array[String]): Unit = {
  6. val spark = SparkSession.builder()
  7. .config( "spark.sql.sources.partitionColumnTypeInference.enabled", "false")
  8. .appName( "TopNStatJob")
  9. .master( "local[2]").getOrCreate()
  10. val accessDF = spark.read.format( "parquet").load( "file:///f:/java/imooc/clean2")
  11. // accessDF.printSchema()
  12. // accessDF.show(false)
  13. videoAccessTopNStat(spark,accessDF)
  14. spark.stop()
  15. }
  16. /**
  17. * 最受欢迎的TOPN课程,
  18. * 这其中包含了两种方法操作DF,一种是函数,一种是以SQL的方式对DF进行统计分析
  19. * @param spark
  20. * @param accessDF
  21. */
  22. def videoAccessTopNStat(spark:SparkSession,accessDF:DataFrame): Unit ={
  23. import spark.implicits._
  24. // val videoAccessTopDF = accessDF.filter($"day" === "20170511" && $"cmsType" === "video")
  25. // .groupBy("day","cmsId").agg(count("cmsId").as("times")).orderBy($"times".desc)
  26. accessDF.createOrReplaceTempView( "access_logs")
  27. val videoAccessTopDF = spark.sql( "select day, cmsId, count(1) as times from access_logs " +
  28. "where day = '20170511' and cmsType = 'video'" +
  29. "group by day ,cmsId order by times desc")
  30. videoAccessTopDF.show( false)
  31. }
  32. }

统计结果展示如下:


 
 
 
  1. +--------+-----+------+
  2. |day |cmsId |times |
  3. +--------+-----+------+
  4. |20170511| 14540 |111027|
  5. |20170511| 4000 |55734 |
  6. |20170511| 14704 |55701 |
  7. |20170511| 14390 |55683 |
  8. |20170511| 14623 |55621 |
  9. |20170511| 4600 |55501 |
  10. |20170511| 4500 |55366 |
  11. |20170511| 14322 |55102 |
  12. +--------+-----+------+
  • 将统计结果写入Mysql中,这个过程是从底层往上层封装的

1;编写MySQL的连接工具类


 
 
 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值