海量WEB日誌分析

 

Hadoop家族系列文章,主要介紹Hadoop家族產品,經常使用的項目包括Hadoop, Hive, Pig, HBase, Sqoop, Mahout, Zookeeper, Avro, Ambari, Chukwa,新增長的項目包括,YARN, Hcatalog, Oozie, Cassandra, Hama, Whirr, Flume, Bigtop, Crunch, Hue等。java

從2011年開始,中國進入大數據風起雲涌的時代,以Hadoop爲表明的家族軟件,佔據了大數據處理的廣闊地盤。開源界及廠商,全部數據軟件,無一不向Hadoop靠攏。Hadoop也從小衆的高富帥領域,變成了大數據開發的標準。在Hadoop原有技術基礎之上,出現了Hadoop家族產品,經過「大數據」概念不斷創新,推出科技進步。python

做爲IT界的開發人員,咱們也要跟上節奏,抓住機遇,跟着Hadoop一塊兒雄起!nginx

關於做者:git

  • 張丹(Conan), 程序員Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

轉載請註明出處:
http://blog.fens.me/hadoop-mapreduce-log-kpi/程序員

hadoop-kpi-logo

前言angularjs

Web日誌包含着網站最重要的信息,經過日誌分析,咱們能夠知道網站的訪問量,哪一個網頁訪問人數最多,哪一個網頁最有價值等。通常中型的網站(10W的PV以上),天天會產生1G以上Web日誌文件。大型或超大型的網站,可能每小時就會產生10G的數據量。github

對於日誌的這種規模的數據,用Hadoop進行日誌分析,是最適合不過的了。web

目錄算法

  1. Web日誌分析概述
  2. 需求分析:KPI指標設計
  3. 算法模型:Hadoop並行算法
  4. 架構設計:日誌KPI系統架構
  5. 程序開發1:用Maven構建Hadoop項目
  6. 程序開發2:MapReduce程序實現

1. Web日誌分析概述

Web日誌由Web服務器產生,多是Nginx, Apache, Tomcat等。從Web日誌中,咱們能夠獲取網站每類頁面的PV值(PageView,頁面訪問量)、獨立IP數;稍微複雜一些的,能夠計算得出用戶所檢索的關鍵詞排行榜、用戶停留時間最高的頁面等;更復雜的,構建廣告點擊模型、分析用戶行爲特徵等等。數據庫

在Web日誌中,每條日誌一般表明着用戶的一次訪問行爲,例以下面就是一條nginx日誌:

222.68.172.190 - - [18/Sep/2013:06:49:57 +0000] "GET /images/my.jpg HTTP/1.1" 200 19939
 "http://www.angularjs.cn/A00n" "Mozilla/5.0 (Windows NT 6.1)
 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36"

拆解爲如下8個變量

  • remote_addr: 記錄客戶端的ip地址, 222.68.172.190
  • remote_user: 記錄客戶端用戶名稱, –
  • time_local: 記錄訪問時間與時區, [18/Sep/2013:06:49:57 +0000]
  • request: 記錄請求的url與http協議, 「GET /images/my.jpg HTTP/1.1″
  • status: 記錄請求狀態,成功是200, 200
  • body_bytes_sent: 記錄發送給客戶端文件主體內容大小, 19939
  • http_referer: 用來記錄從那個頁面連接訪問過來的, 「http://www.angularjs.cn/A00n」
  • http_user_agent: 記錄客戶瀏覽器的相關信息, 「Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36″

注:要更多的信息,則要用其它手段去獲取,經過js代碼單獨發送請求,使用cookies記錄用戶的訪問信息。

利用這些日誌信息,咱們能夠深刻挖掘網站的祕密了。

少許數據的狀況

少許數據的狀況(10Mb,100Mb,10G),在單機處理尚能忍受的時候,我能夠直接利用各類Unix/Linux工具,awk、grep、sort、join等都是日誌分析的利器,再配合perl, python,正則表達工,基本就能夠解決全部的問題。

例如,咱們想從上面提到的nginx日誌中獲得訪問量最高前10個IP,實現很簡單:

~ cat access.log.10 | awk '{a[$1]++} END {for(b in a) print b"\t"a[b]}' | sort -k2 -r | head -n 10
163.177.71.12   972
101.226.68.137  972
183.195.232.138 971
50.116.27.194   97
14.17.29.86     96
61.135.216.104  94
61.135.216.105  91
61.186.190.41   9
59.39.192.108   9
220.181.51.212  9

海量數據的狀況

當數據量天天以10G、100G增加的時候,單機處理能力已經不能知足需求。咱們就須要增長系統的複雜性,用計算機集羣,存儲陣列來解決。在Hadoop出現以前,海量數據存儲,和海量日誌分析都是很是困難的。只有少數一些公司,掌握着高效的並行計算,分步式計算,分步式存儲的核心技術。

Hadoop的出現,大幅度的下降了海量數據處理的門檻,讓小公司甚至是我的都能力,搞定海量數據。而且,Hadoop很是適用於日誌分析系統。

2.需求分析:KPI指標設計

下面咱們將從一個公司案例出發來全面的解釋,如何用進行海量Web日誌分析,提取KPI數據

案例介紹
某電子商務網站,在線團購業務。每日PV數100w,獨立IP數5w。用戶一般在工做日上午10:00-12:00和下午15:00-18:00訪問量最大。日間主要是經過PC端瀏覽器訪問,休息日及夜間經過移動設備訪問較多。網站搜索瀏量佔整個網站的80%,PC用戶不足1%的用戶會消費,移動用戶有5%會消費。

經過簡短的描述,咱們能夠粗略地看出,這家電商網站的經營情況,並認識到願意消費的用戶從哪裏來,有哪些潛在的用戶能夠挖掘,網站是否存在倒閉風險等。

KPI指標設計

  • PV(PageView): 頁面訪問量統計
  • IP: 頁面獨立IP的訪問量統計
  • Time: 用戶每小時PV的統計
  • Source: 用戶來源域名的統計
  • Browser: 用戶的訪問設備統計

注:商業保密限制,沒法提供電商網站的日誌。
下面的內容,將以個人我的網站爲例提取數據進行分析。

百度統計,對我我的網站作的統計!http://www.fens.me

基本統計指標:
hadoop-kpi-baidu

用戶的訪問設備統計指標:
hadoop-kpi-baidu2

從商業的角度,我的網站的特徵與電商網站不太同樣,沒有轉化率,同時跳出率也比較高。從技術的角度,一樣都關注KPI指標設計。

3.算法模型:Hadoop並行算法

hadoop-kpi-log

並行算法的設計:
注:找到第一節有定義的8個變量

PV(PageView): 頁面訪問量統計

  • Map過程{key:$request,value:1}
  • Reduce過程{key:$request,value:求和(sum)}

IP: 頁面獨立IP的訪問量統計

  • Map: {key:$request,value:$remote_addr}
  • Reduce: {key:$request,value:去重再求和(sum(unique))}

Time: 用戶每小時PV的統計

  • Map: {key:$time_local,value:1}
  • Reduce: {key:$time_local,value:求和(sum)}

Source: 用戶來源域名的統計

  • Map: {key:$http_referer,value:1}
  • Reduce: {key:$http_referer,value:求和(sum)}

Browser: 用戶的訪問設備統計

  • Map: {key:$http_user_agent,value:1}
  • Reduce: {key:$http_user_agent,value:求和(sum)}

4.架構設計:日誌KPI系統架構

hadoop-kpi-architect

上圖中,左邊是Application業務系統,右邊是Hadoop的HDFS, MapReduce。

  1. 日誌是由業務系統產生的,咱們能夠設置web服務器天天產生一個新的目錄,目錄下面會產生多個日誌文件,每一個日誌文件64M。
  2. 設置系統定時器CRON,夜間在0點後,向HDFS導入昨天的日誌文件。
  3. 完成導入後,設置系統定時器,啓動MapReduce程序,提取並計算統計指標。
  4. 完成計算後,設置系統定時器,從HDFS導出統計指標數據到數據庫,方便之後的即便查詢。

hadoop-kpi-process

上面這幅圖,咱們能夠看得更清楚,數據是如何流動的。藍色背景的部分是在Hadoop中的,接下來咱們的任務就是完成MapReduce的程序實現。

5.程序開發1:用Maven構建Hadoop項目

請參考文章:用Maven構建Hadoop項目

win7的開發環境 和 Hadoop的運行環境 ,在上面文章中已經介紹過了。

咱們須要放日誌文件,上傳的HDFS裏/user/hdfs/log_kpi/目錄,參考下面的命令操做

~ hadoop fs -mkdir /user/hdfs/log_kpi
~ hadoop fs -copyFromLocal /home/conan/datafiles/access.log.10 /user/hdfs/log_kpi/

我已經把整個MapReduce的實現都放到了github上面:

https://github.com/bsspirit/maven_hadoop_template/releases/tag/kpi_v1

6.程序開發2:MapReduce程序實現

開發流程:

  1. 對日誌行的解析
  2. Map函數實現
  3. Reduce函數實現
  4. 啓動程序實現

1). 對日誌行的解析
新建文件:org.conan.myhadoop.mr.kpi.KPI.java

package org.conan.myhadoop.mr.kpi;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/*
 * KPI Object
 */
public class KPI {
    private String remote_addr;// 記錄客戶端的ip地址
    private String remote_user;// 記錄客戶端用戶名稱,忽略屬性"-"
    private String time_local;// 記錄訪問時間與時區
    private String request;// 記錄請求的url與http協議
    private String status;// 記錄請求狀態;成功是200
    private String body_bytes_sent;// 記錄發送給客戶端文件主體內容大小
    private String http_referer;// 用來記錄從那個頁面連接訪問過來的
    private String http_user_agent;// 記錄客戶瀏覽器的相關信息

    private boolean valid = true;// 判斷數據是否合法
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("valid:" + this.valid);
        sb.append("\nremote_addr:" + this.remote_addr);
        sb.append("\nremote_user:" + this.remote_user);
        sb.append("\ntime_local:" + this.time_local);
        sb.append("\nrequest:" + this.request);
        sb.append("\nstatus:" + this.status);
        sb.append("\nbody_bytes_sent:" + this.body_bytes_sent);
        sb.append("\nhttp_referer:" + this.http_referer);
        sb.append("\nhttp_user_agent:" + this.http_user_agent);
        return sb.toString();
    }

    public String getRemote_addr() {
        return remote_addr;
    }

    public void setRemote_addr(String remote_addr) {
        this.remote_addr = remote_addr;
    }

    public String getRemote_user() {
        return remote_user;
    }

    public void setRemote_user(String remote_user) {
        this.remote_user = remote_user;
    }

    public String getTime_local() {
        return time_local;
    }

    public Date getTime_local_Date() throws ParseException {
        SimpleDateFormat df = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss", Locale.US);
        return df.parse(this.time_local);
    }
    
    public String getTime_local_Date_hour() throws ParseException{
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHH");
        return df.format(this.getTime_local_Date());
    }

    public void setTime_local(String time_local) {
        this.time_local = time_local;
    }

    public String getRequest() {
        return request;
    }

    public void setRequest(String request) {
        this.request = request;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getBody_bytes_sent() {
        return body_bytes_sent;
    }

    public void setBody_bytes_sent(String body_bytes_sent) {
        this.body_bytes_sent = body_bytes_sent;
    }

    public String getHttp_referer() {
        return http_referer;
    }
    
    public String getHttp_referer_domain(){
        if(http_referer.length()<8){ 
            return http_referer;
        }
        
        String str=this.http_referer.replace("\"", "").replace("http://", "").replace("https://", "");
        return str.indexOf("/")>0?str.substring(0, str.indexOf("/")):str;
    }

    public void setHttp_referer(String http_referer) {
        this.http_referer = http_referer;
    }

    public String getHttp_user_agent() {
        return http_user_agent;
    }

    public void setHttp_user_agent(String http_user_agent) {
        this.http_user_agent = http_user_agent;
    }

    public boolean isValid() {
        return valid;
    }

    public void setValid(boolean valid) {
        this.valid = valid;
    }

    public static void main(String args[]) {
        String line = "222.68.172.190 - - [18/Sep/2013:06:49:57 +0000] \"GET /images/my.jpg HTTP/1.1\" 200 19939 \"http://www.angularjs.cn/A00n\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36\"";
        System.out.println(line);
        KPI kpi = new KPI();
        String[] arr = line.split(" ");

        kpi.setRemote_addr(arr[0]);
        kpi.setRemote_user(arr[1]);
        kpi.setTime_local(arr[3].substring(1));
        kpi.setRequest(arr[6]);
        kpi.setStatus(arr[8]);
        kpi.setBody_bytes_sent(arr[9]);
        kpi.setHttp_referer(arr[10]);
        kpi.setHttp_user_agent(arr[11] + " " + arr[12]);
        System.out.println(kpi);

        try {
            SimpleDateFormat df = new SimpleDateFormat("yyyy.MM.dd:HH:mm:ss", Locale.US);
            System.out.println(df.format(kpi.getTime_local_Date()));
            System.out.println(kpi.getTime_local_Date_hour());
            System.out.println(kpi.getHttp_referer_domain());
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

}

從日誌文件中,取一行經過main函數寫一個簡單的解析測試。

控制檯輸出:

222.68.172.190 - - [18/Sep/2013:06:49:57 +0000] "GET /images/my.jpg HTTP/1.1" 200 19939 "http://www.angularjs.cn/A00n" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36"
valid:true
remote_addr:222.68.172.190
remote_user:-
time_local:18/Sep/2013:06:49:57
request:/images/my.jpg
status:200
body_bytes_sent:19939
http_referer:"http://www.angularjs.cn/A00n"
http_user_agent:"Mozilla/5.0 (Windows
2013.09.18:06:49:57
2013091806
www.angularjs.cn

咱們看到日誌行,被正確的解析成了kpi對象的屬性。咱們把解析過程,單獨封裝成一個方法。

private static KPI parser(String line) {
        System.out.println(line);
        KPI kpi = new KPI();
        String[] arr = line.split(" ");
        if (arr.length > 11) {
            kpi.setRemote_addr(arr[0]);
            kpi.setRemote_user(arr[1]);
            kpi.setTime_local(arr[3].substring(1));
            kpi.setRequest(arr[6]);
            kpi.setStatus(arr[8]);
            kpi.setBody_bytes_sent(arr[9]);
            kpi.setHttp_referer(arr[10]);
            
            if (arr.length > 12) {
                kpi.setHttp_user_agent(arr[11] + " " + arr[12]);
            } else {
                kpi.setHttp_user_agent(arr[11]);
            }

            if (Integer.parseInt(kpi.getStatus()) >= 400) {// 大於400,HTTP錯誤
                kpi.setValid(false);
            }
        } else {
            kpi.setValid(false);
        }
        return kpi;
    }

對map方法,reduce方法,啓動方法,咱們單獨寫一個類來實現

下面將分別介紹MapReduce的實現類:

  • PV:org.conan.myhadoop.mr.kpi.KPIPV.java
  • IP: org.conan.myhadoop.mr.kpi.KPIIP.java
  • Time: org.conan.myhadoop.mr.kpi.KPITime.java
  • Browser: org.conan.myhadoop.mr.kpi.KPIBrowser.java

1). PV:org.conan.myhadoop.mr.kpi.KPIPV.java

package org.conan.myhadoop.mr.kpi;

import java.io.IOException;
import java.util.Iterator;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
import org.apache.hadoop.mapred.TextInputFormat;
import org.apache.hadoop.mapred.TextOutputFormat;

public class KPIPV { 

    public static class KPIPVMapper extends MapReduceBase implements Mapper<object, text,="" intwritable=""> {
        private IntWritable one = new IntWritable(1);
        private Text word = new Text();

        @Override
        public void map(Object key, Text value, OutputCollector<text, intwritable=""> output, Reporter reporter) throws IOException {
            KPI kpi = KPI.filterPVs(value.toString());
            if (kpi.isValid()) {
                word.set(kpi.getRequest());
                output.collect(word, one);
            }
        }
    }

    public static class KPIPVReducer extends MapReduceBase implements Reducer<text, intwritable,="" text,="" intwritable=""> {
        private IntWritable result = new IntWritable();

        @Override
        public void reduce(Text key, Iterator values, OutputCollector<text, intwritable=""> output, Reporter reporter) throws IOException {
            int sum = 0;
            while (values.hasNext()) {
                sum += values.next().get();
            }
            result.set(sum);
            output.collect(key, result);
        }
    }

    public static void main(String[] args) throws Exception {
        String input = "hdfs://192.168.1.210:9000/user/hdfs/log_kpi/";
        String output = "hdfs://192.168.1.210:9000/user/hdfs/log_kpi/pv";

        JobConf conf = new JobConf(KPIPV.class);
        conf.setJobName("KPIPV");
        conf.addResource("classpath:/hadoop/core-site.xml");
        conf.addResource("classpath:/hadoop/hdfs-site.xml");
        conf.addResource("classpath:/hadoop/mapred-site.xml");

        conf.setMapOutputKeyClass(Text.class);
        conf.setMapOutputValueClass(IntWritable.class);

        conf.setOutputKeyClass(Text.class);
        conf.setOutputValueClass(IntWritable.class);

        conf.setMapperClass(KPIPVMapper.class);
        conf.setCombinerClass(KPIPVReducer.class);
        conf.setReducerClass(KPIPVReducer.class);

        conf.setInputFormat(TextInputFormat.class);
        conf.setOutputFormat(TextOutputFormat.class);

        FileInputFormat.setInputPaths(conf, new Path(input));
        FileOutputFormat.setOutputPath(conf, new Path(output));

        JobClient.runJob(conf);
        System.exit(0);
    }
}

在程序中會調用KPI類的方法

KPI kpi = KPI.filterPVs(value.toString());

經過filterPVs方法,咱們能夠實現對PV,更多的控制。

在KPK.java中,增長filterPVs方法

/**
     * 按page的pv分類
     */
    public static KPI filterPVs(String line) {
        KPI kpi = parser(line);
        Set pages = new HashSet();
        pages.add("/about");
        pages.add("/black-ip-list/");
        pages.add("/cassandra-clustor/");
        pages.add("/finance-rhive-repurchase/");
        pages.add("/hadoop-family-roadmap/");
        pages.add("/hadoop-hive-intro/");
        pages.add("/hadoop-zookeeper-intro/");
        pages.add("/hadoop-mahout-roadmap/");

        if (!pages.contains(kpi.getRequest())) {
            kpi.setValid(false);
        }
        return kpi;
    }

在filterPVs方法,咱們定義了一個pages的過濾,就是隻對這個頁面進行PV統計。

咱們運行一下KPIPV.java

2013-10-9 11:53:28 org.apache.hadoop.mapred.MapTask$MapOutputBuffer flush
信息: Starting flush of map output
2013-10-9 11:53:28 org.apache.hadoop.mapred.MapTask$MapOutputBuffer sortAndSpill
信息: Finished spill 0
2013-10-9 11:53:28 org.apache.hadoop.mapred.Task done
信息: Task:attempt_local_0001_m_000000_0 is done. And is in the process of commiting
2013-10-9 11:53:30 org.apache.hadoop.mapred.LocalJobRunner$Job statusUpdate
信息: hdfs://192.168.1.210:9000/user/hdfs/log_kpi/access.log.10:0+3025757
2013-10-9 11:53:30 org.apache.hadoop.mapred.LocalJobRunner$Job statusUpdate
信息: hdfs://192.168.1.210:9000/user/hdfs/log_kpi/access.log.10:0+3025757
2013-10-9 11:53:30 org.apache.hadoop.mapred.Task sendDone
信息: Task 'attempt_local_0001_m_000000_0' done.
2013-10-9 11:53:30 org.apache.hadoop.mapred.Task initialize
信息:  Using ResourceCalculatorPlugin : null
2013-10-9 11:53:30 org.apache.hadoop.mapred.LocalJobRunner$Job statusUpdate
信息: 
2013-10-9 11:53:30 org.apache.hadoop.mapred.Merger$MergeQueue merge
信息: Merging 1 sorted segments
2013-10-9 11:53:30 org.apache.hadoop.mapred.Merger$MergeQueue merge
信息: Down to the last merge-pass, with 1 segments left of total size: 213 bytes
2013-10-9 11:53:30 org.apache.hadoop.mapred.LocalJobRunner$Job statusUpdate
信息: 
2013-10-9 11:53:30 org.apache.hadoop.mapred.Task done
信息: Task:attempt_local_0001_r_000000_0 is done. And is in the process of commiting
2013-10-9 11:53:30 org.apache.hadoop.mapred.LocalJobRunner$Job statusUpdate
信息: 
2013-10-9 11:53:30 org.apache.hadoop.mapred.Task commit
信息: Task attempt_local_0001_r_000000_0 is allowed to commit now
2013-10-9 11:53:30 org.apache.hadoop.mapred.FileOutputCommitter commitTask
信息: Saved output of task 'attempt_local_0001_r_000000_0' to hdfs://192.168.1.210:9000/user/hdfs/log_kpi/pv
2013-10-9 11:53:31 org.apache.hadoop.mapred.JobClient monitorAndPrintJob
信息:  map 100% reduce 0%
2013-10-9 11:53:33 org.apache.hadoop.mapred.LocalJobRunner$Job statusUpdate
信息: reduce > reduce
2013-10-9 11:53:33 org.apache.hadoop.mapred.Task sendDone
信息: Task 'attempt_local_0001_r_000000_0' done.
2013-10-9 11:53:34 org.apache.hadoop.mapred.JobClient monitorAndPrintJob
信息:  map 100% reduce 100%
2013-10-9 11:53:34 org.apache.hadoop.mapred.JobClient monitorAndPrintJob
信息: Job complete: job_local_0001
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息: Counters: 20
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:   File Input Format Counters 
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Bytes Read=3025757
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:   File Output Format Counters 
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Bytes Written=183
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:   FileSystemCounters
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     FILE_BYTES_READ=545
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     HDFS_BYTES_READ=6051514
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     FILE_BYTES_WRITTEN=83472
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     HDFS_BYTES_WRITTEN=183
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:   Map-Reduce Framework
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Map output materialized bytes=217
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Map input records=14619
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Reduce shuffle bytes=0
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Spilled Records=16
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Map output bytes=2004
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Total committed heap usage (bytes)=376569856
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Map input bytes=3025757
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     SPLIT_RAW_BYTES=110
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Combine input records=76
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Reduce input records=8
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Reduce input groups=8
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Combine output records=8
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Reduce output records=8
2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log
信息:     Map output records=76

用hadoop命令查看HDFS文件

~ hadoop fs -cat /user/hdfs/log_kpi/pv/part-00000
/about  5
/black-ip-list/ 2
/cassandra-clustor/     3
/finance-rhive-repurchase/      13
/hadoop-family-roadmap/ 13
/hadoop-hive-intro/     14
/hadoop-mahout-roadmap/ 20
/hadoop-zookeeper-intro/        6

這樣咱們就獲得了,剛剛日誌文件中的,指定頁面的PV值。

指定頁面,就像網站的站點地圖同樣,若是沒有指定全部訪問連接都會被找出來,經過「站點地圖」的指定,咱們能夠更容易地找到,咱們所須要的信息。

後面,其餘的統計指標的提取思路,和PV的實現過程都是相似的,你們能夠直接下載源代碼,運行看到結果!!

######################################################
看文字不過癮,做者視頻講解,請訪問網站:http://onbook.me/video######################################################

相關文章
相關標籤/搜索