1、项目描述
通过对apache common日志进行分析
2、数据情况
每行记录有5部分组成:
1.访问ip
2.访问时间
3.访问资源【跟着两个访问的Url】
4.访问状态
5.本次流量
截取部分数据如下:
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/shy.gif HTTP/1.1" 200 2663
8.35.201.163 - - [30/May/2013:17:38:21 +0800] "GET /static/image/common/nv_a.png HTTP/1.1" 200 2076
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/titter.gif HTTP/1.1" 200 1398
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/sweat.gif HTTP/1.1" 200 1879
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/mad.gif HTTP/1.1" 200 2423
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/hug.gif HTTP/1.1" 200 1054
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/lol.gif HTTP/1.1" 200 1443
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/victory.gif HTTP/1.1" 200 1275
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/time.gif HTTP/1.1" 200 687
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/kiss.gif HTTP/1.1" 200 987
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/handshake.gif HTTP/1.1" 200 1322
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/loveliness.gif HTTP/1.1" 200 1579
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/call.gif HTTP/1.1" 200 603
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/funk.gif HTTP/1.1" 200 2928
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/curse.gif HTTP/1.1" 200 1543
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/dizzy.gif HTTP/1.1" 200 1859
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/shutup.gif HTTP/1.1" 200 2500
27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/sleepy.gif HTTP/1.1" 200 2375
8.35.201.164 - - [30/May/2013:17:38:21 +0800] "GET /static/image/common/pn.png HTTP/1.1" 200 592
8.35.201.165 - - [30/May/2013:17:38:21 +0800] "GET /uc_server/avatar.php?uid=56212&size=middle HTTP/1.1
3、关键指标
⊙浏览量PV
定义:页面浏览量即为PV(Page View),是指所有用户浏览页面的总和,一个独立用户每打开一个页面就被记录1 次。
分析:网站总浏览量,可以考核用户对于网站的兴趣,就像收视率对于电视剧一样。但是对于网站运营者来说,更重要的是,每个栏目下的浏览量。
计算公式:记录计数
⊙注册用户数
计算公式:对访问member.PHP?mod=register的url,计数
⊙IP数
定义:一天之内,访问网站的不同独立IP 个数加和。其中同一IP无论访问了几个页面,独立IP 数均为1。
分析:这是我们最熟悉的一个概念,无论同一个IP上有多少电脑,或者其他用户,从某种程度上来说,独立IP的多少,是衡量网站推广活动好坏最直接的数据。
公式:对不同ip,计数
⊙跳出率
定义:只浏览了一个页面便离开了网站的访问次数占总的访问次数的百分比,即只浏览了一个页面的访问次数 / 全部的访问次数汇总。
分析:跳出率是非常重要的访客黏性指标,它显示了访客对网站的兴趣程度:跳出率越低说明流量质量越好,访客对网站的内容越感兴趣,这些访客越可能是网站的有效用户、忠实用户。
该指标也可以衡量网络营销的效果,指出有多少访客被网络营销吸引到宣传产品页或网站上之后,又流失掉了,可以说就是煮熟的鸭子飞了。比如,网站在某媒体上打广告推广,分析从这个推广来源进入的访客指标,其跳出率可以反映出选择这个媒体是否合适,广告语的撰写是否优秀,以及网站入口页的设计是否用户体验良好。
计算公式:(1)统计一天内只出现一条记录的ip,称为跳出数
(2)跳出数/PV
⊙版块热度排行榜
定义:版块的访问情况排行。
分析:巩固热点版块成绩,加强冷清版块建设。同时对学科建设也有影响。
计算公式:按访问次数统计排序
4、项目开发步骤
1.使用flume把日志数据导入到hdfs中
2.对数据进行清洗、清洗后的数据易于我们使用
3.明细日志使用Hbase存储,能够利用ip、时间查询
4.使用Hive进行数据的多维分析
5.把hive分析结果使用sqoop导出到MySQL中
6.提供视图工具供用户使用
上面介绍了项目的一些基本情况,下面我们将详细介绍项目的开发过程:
1、把日志数据上传到HDFS中进行处理
如果是日志服务器数据较小、压力较小,可以直接使用shell命令把数据上传到HDFS中;
如果是日志服务器数据较大、压力较答,使用NFS在另一台服务器上上传数据;
如果日志服务器数据非常多、数据量大,使用flume进行数据处理;
[root@hadoop11 mydata]# hadoop fs -put access_2013_05_30.log /hmbbs_logs/
[root@hadoop11 mydata]# hadoop fs -lsr /hmbbs_logs/
lsr: DEPRECATED: Please use 'ls -R' instead.
-rw-r--r-- 3 root supergroup 61084192 2016-07-22 14:42 /hmbbs_logs/access_2013_05_30.log
2、使用MapReduce对数据进行清洗,清洗后的数据易于我们的使用
清洗标准:将Apache log日志清洗成我们熟悉的日志格式。
①
访问状态和本次流量字段去掉
②
过滤掉静态记录,只要动态记录;
GET /static 或者 GET /uc_server 开头的行文本舍弃
过滤掉了开头和结尾的标志信息;
GET / POST /
HTTP/1.1 HTTP/1.0
清洗日志的MapReduce源代码:
package Hmbbs;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.mapreduce.lib.partition.HashPartitioner;
;
//本程序的目的是通过MapReduce对Hmbbs中的日志数据进行清洗时
public class HmbbsCleaner
{
public static String path1="";//指定文件的输入路径
public static String path2="";//指定日志的输出路径
public static void main(String[] args) throws Exception
{
path1 = args[0];
path2 = args[1];
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://hadoop11:9000/");
FileSystem fileSystem = FileSystem.get(conf);
if(fileSystem.exists(new Path(path2)))
{
fileSystem.delete(new Path(path2), true);
}
Job job = Job.getInstance(conf, "HmbbsCleaner");
job.setJarByClass(HmbbsCleaner.class);//jar包
//编写驱动
FileInputFormat.setInputPaths(job, new Path(path1));
job.setInputFormatClass(TextInputFormat.class);
job.setMapperClass(MyMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(NullWritable.class);
job.setNumReduceTasks(1);//指定Reducer的任务数量为1
job.setPartitionerClass(HashPartitioner.class);
job.setReducerClass(MyReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(NullWritable.class);
FileOutputFormat.setOutputPath(job, new Path(path2));
job.setOutputFormatClass(TextOutputFormat.class);
//提交任务
job.waitForCompletion(true);
}
public static class MyMapper extends Mapper<LongWritable, Text, Text, NullWritable>
{
protected void map(LongWritable k1, Text v1,Context context)throws IOException, InterruptedException
{
String string = v1.toString();//获取待记录
Parselogs parselogs = new Parselogs();
try
{
String[] sub = parselogs.parseString(string);
if(sub[2].startsWith("GET /static")||sub[2].startsWith("GET /uc_server"))
return ;//对于静态的记录直接过滤掉,不进行任何处理
if(sub[2].startsWith("GET /"))
{
sub[2] = sub[2].substring("GET /".length());
}
if(sub[2].startsWith("POST /"))
{
sub[2] = sub[2].substring("POST /".length());
}//过滤掉了开头和结尾的标志信息
if(sub[2].endsWith(" HTTP/1.1"))
{
sub[2] = sub[2].substring(0, sub[2].length()-" HTTP/1.1".length());
}
if(sub[2].endsWith(" HTTP/1.0"))
{
sub[2] = sub[2].substring(0, sub[2].length()-" HTTP/1.0".length());
}
Text k2 = new Text();
k2.set(sub[0]+"\t"+sub[1]+"\t"+sub[2]);//三个字段之间以制表符进行分开
context.write(k2, NullWritable.get());
}
catch (ParseException e)
{
e.printStackTrace();
}
}
}
public static class MyReducer extends Reducer<Text, NullWritable, Text, NullWritable>
{
protected void reduce(Text k2, Iterable<NullWritable> v2s,Context context)throws IOException, InterruptedException
{
for (NullWritable v2 : v2s)
{
Text k3 = k2;
context.write(k3, NullWritable.get());
}
}
}
}
class Parselogs //Parselogs这个类用来对字符串进行解析
{
public String[] parseString(String str) throws ParseException
{
String str1 = parseIp(str);
String str2 = parseDate(str);
String str3 = parseUrl(str);
//String str4 = parseStatus(str);
//String str5 = parseFlow(str);
//String[] str66 = new String[]{str1,str2,str3,str4,str5};
String[] str66 = new String[]{str1,str2,str3};//在这里只获取与本次项目有关的数据
return str66;
}
public String parseIp(String str)//对ip地址进行解析的方法
{
String[] splited = str.split(" - - ");//用指定的正则表达式进行切分,获取我们需要的字段
return splited[0];
}
public String parseDate(String str) throws ParseException
{
String[] splited = str.split(" - - ");//用指定的正则表达式进行切分,获取我们需要的字段
int index1 = splited[1].indexOf("[");
int index2 = splited[1].indexOf("]");
String substring = splited[1].substring(index1+1, index2);//到此获取了时间字段30/May/2013:17:38:20 +0800
SimpleDateFormat simple1 = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss", Locale.ENGLISH);//匹配我们给定的字符串,并将其解析成对应的时间
SimpleDateFormat simple2 = new SimpleDateFormat("yyyyMMddHHmmss");//匹配我们给定的字符串
Date parse = simple1.parse(substring);
String format = simple2.format(parse);
return format;
}
public String parseUrl(String str)//获取访问的url
{
int index1 = str.indexOf("]");
int index2= str.lastIndexOf("\"");
String substring = str.substring(index1+3, index2);
return substring;
}
public String parseStatus(String str)//获取访问的状态
{
int index1= str.lastIndexOf("\"");
String str2 = str.substring(index1+1).trim();
String[] splited = str2.split(" ");
return splited[0];
}
public String parseFlow(String str)//获取访问的状态
{
int index1= str.lastIndexOf("\"");
String str2 = str.substring(index1+1).trim();
String[] splited = str2.split(" ");
return splited[1];
}
}
经过mapreduce清洗后的数据如下(截取部分)
60.10.5.65 20130530220737 source/plugin/wmff_wxyun/img/wmff_zk.css
60.10.5.65 20130530220738 source/plugin/study_nge/js/HoverLi.js
60.10.5.65 20130530220741 home.php?mod=misc&ac=sendmail&rand=1369922680
60.10.5.65 20130530220742 favicon.ico
60.10.5.65 20130530220742 forum.php
60.10.5.65 20130530220742 source/plugin/wmff_wxyun/img/wx_jqr.gif
60.10.5.65 20130530220742 template/newdefault/style/t5/bgimg.jpg
60.10.5.65 20130530220744 data/attachment/common/cf/104854ejrssrbbfsfv6cn5.jpg
60.10.5.65 20130530220744 source/plugin/wmff_wxyun/img/wx_jqr.gif
60.10.5.65 20130530220744 template/newdefault/style/t5/bgimg.jpg
60.10.5.65 20130530220744 template/newdefault/style/t5/nv.png
60.10.5.65 20130530220744 template/newdefault/style/t5/nv_a.png
60.10.5.65 20130530220745 data/attachment/common/cf/104950hio3tgww8tgpqtcz.jpg
60.10.5.65 20130530220745 data/attachment/common/cf/105041vvvi7pgez0w1mvxv.jpg
60.10.5.65 20130530220745 data/attachment/common/cf/180036e72352fq3reerq13.jpg
60.10.5.65 20130530220745 home.php?mod=misc&ac=sendmail&rand=1369922680
60.10.5.65 20130530220745 source/plugin/study_nge/images/list10.gif
60.10.5.65 20130530220746 source/plugin/study_nge/images/listbg.gif
60.10.5.65 20130530220747 api/connect/like.php
3、使用hive对清洗后的数据进行多维分析
(1)统计每日的pv(浏览量)
hive> create table hmbbs_pv
> as select count(1) as pv from hmbbs_table;
(2)统计每日的register(注册用户数)
hive> create table hmbbs_register
> as select count(1) as register
> from hmbbs_table
> where instr(urllog,'member.php?mod=register') > 0;
(3)统计每日的独立的ip
hive> create table hmbbs_ip as
> select count(distinct iplog) as ip
> from hmbbs_table;
(4)统计每日的独立的跳出率
hive> CREATE TABLE hmbbs_jumper AS SELECT COUNT(1) AS jumper FROM (SELECT COUNT(iplog) AS times FROM hmbbs_table GROUP BY iplog HAVING times=1) e ;
到此获得了各个参数的结果:
hive> show tables;
OK
hmbbs_ip
hmbbs_jumper
hmbbs_pv
hmbbs_register
hmbbs_table
Time taken: 0.081 seconds
hive> select * from hmbbs_ip;
OK
10411
Time taken: 0.111 seconds
hive> select * from hmbbs_jumper;
OK
3749
Time taken: 0.107 seconds
hive> select * from hmbbs_pv;
OK
169857
Time taken: 0.108 seconds
hive> select * from hmbbs_register;
OK
28
Time taken: 0.107 seconds
4、将hive分析的结果使用sqoop导出到mysql中
[root@hadoop11 mydata]# sqoop export --connect jdbc:mysql://hadoop11:3306/mydata --table hmresult --username root --password admin --export-dir /hmbbs_dir/ --fields-terminated-by '\t' -m 1
接下来我们在mysql中查看数据:
到此,基于Hadoop的日志就大致介绍完了