【項目總結】android 日志收集


來新公司20天,完成了第一個任務,安卓端日志收集流程的開發,在這里總結一下。

 

1.場景介紹

    公司有多個產業,各產業產生若干app,現在需要收集app的日志信息,並做相關計算,例如流量統計、用戶畫像等。

    用戶的數量級目前不易確定,因為有大半app還在開發中,並沒有發布。因為我們是新成立的數據組,沒有人熟悉安卓相關的東西,所以老板讓我研究一下把這條線打通。

2.技術方案

    Android SDK(日志產生) -> Flume (日志收集) -> kafka(消息緩存) -> storm(日志解析) -> hbase (落地)

    每一個模塊我之前都沒有接觸過,所以都要從頭了解,好在flume之后的這些服務已經搭好,我只要擺正姿勢使用就可以。

3.技術細節

3.1 Android SDK

    安卓日志的統計對象是用戶的行為,即點擊、滑動、翻頁、跳轉等事件;統計內容主要包括設備信息、用戶信息、事件信息等。

3.1.1 接口設計

    SDK開發完后,最終會提供給app開發團隊,讓他們接入到app中,在需要產生日志的地方埋點;因此SDK的接口不能復雜,不能帶來太多的接入開發工作。考慮之后,第一階段只提供下面幾個接口

(1) luanchApp() // app啟動時調用,目的是初始化sdk
(2) onEvent()   // 各類按鈕相應事件中調用,用來統計普通事件
(3) onPageStart() // 頁面/activity的開始事件中調用,用來統計頁面訪問事件
(4) onPageEnd() // 頁面/activity的結束事件中調用,用來統計頁面訪問事件

3.1.2 Android Activity 生命周期

    頁面事件是比較重要的一部分,開始之前,對安卓activity的生命周期做了一下了解。下面是幾個相關的方法,

(1) onCreate(), onDestroy()   // activity對象的創建和銷毀
(2) onStart(), onStop() //activity的開始和停止事件
(3) onResume(), onPause() // activity的繼續和暫停事件

    通俗來講,當activity顯示出來,就會調用一次onStart,變得看不見,就會調用一次 onStop;當可以在activity上進行操作時,則會調用一次onResume,變得不能操作時,就會調用一次onPause。onResume和onStart的區別的一個例子:當一個activity A的上面,出現一個透明activity B將A覆蓋,那么會調用A的 onPause,而不會調用 onStop。因為A還看得見,但是不能操作了。

3.1.3 日志項

 

名稱 例子 類型 說明
userid abc 字符串 用戶id(由app提供)
appid 123 字符串 應用id(由app提供)
guid 787f7300-37e2-34d8-b101-c8ef415385ae 字符串 設備唯一id
imei 867831028457919 字符串 國際移動設備標識
ln zh 字符串 語言
density 3.0 浮點型 屏幕密度
tel 130128361936 11位整數 電話號碼
mac f4:8b:32:af:22:e9 字符串 設備mac地址
iscrack 1 0或1 是否root
timezone Asia/Shanghai 字符串 時區
nettype lte 字符串 網絡類型
longitude 39.001 浮點型 經度
os 4.4.4 字符串 os版本
platform android 字符串 os
module MI 4LTE 字符串 手機型號
sr 1080*1920 字符串 屏幕分辨率
sdkver 1.1.5 字符串 sdk版本
isp 46002 整型 運營商代號
appver 1 字符串 app版本
ip 10.0.2.15 字符串 ip地址
ismobile 1 0或1 是否為手機
requesttime 1451272912 整型 請求產生事件
netstatus 1 0或1 網絡狀態
sim 898600310115f0024716 字符串 sim卡id
channel 12 字符串 app渠道
latitude 120.123 浮點型 緯度
event.sessionid 112312341341341341 字符串 事件所屬的sessionid
event.eventtime 1451028244 整型 事件產生時間
event.duration 12 整型 事件持續事件
event.pagedur 12 整型 頁面停留時間
event.definedid abc 字符串 自定義事件id
event.prepageid red 字符串 前一個頁面id
event.currevent page 字符串 事件類型
event.pageid blue 字符串 當前頁面id

 

    這里面有幾個項比較糾結,不易獲取:

    (1) latitude 和 longitude,參考一篇帖子,http://stackoverflow.com/questions/20438627/getlastknownlocation-returns-null

    (2) IP,按照找到的方法總是取不到安卓的真實ip,索性不取了,在flume中的http請求頭中得到

3.1.4 日志產生流程

    為了讓后續實時分析避免對歷史數據的關聯,發送的日志數據,每條記錄都帶上全部字段信息。這樣就導致每條記錄至少在1K,如果實時發送,吃不消。因此,使用定時發送策略,暫定每60s發送一次,每次發送的數據中,設備相關的信息只保留一份,事件以數組的形式附在其后。這樣,經過壓縮之后,基本可以保證每分鍾日志產生流程在 2K 以內。另外,頁面事件的產生,是在 onPageEnd 中,也就是說,當離開這個頁面的時候,才產生這個頁面的對應事件,這樣做的目的是為了統計頁面停留時間。

 

    上圖是日志產生的流程圖。時鍾響應每1分鍾觸發一次,期間產生的事件,放入sqlite數據庫中。有一點需要注意:

    app如果退出,則會導致緩存的事件不能及時發出,因為我們平時從后台退出app的方法是會直接殺死進程的。為了避免這種情況,當app發生進入后台、屏幕鎖定這兩種動作時,不管時鍾相應是否觸發,直接發送一次緩存事件,因為這兩種動作之后,app進程很有可能被殺掉。另外,如果因為各種原因,app退出后還是留下沒有及時發出的事件,那么下次打開app時,第一件事就是把上次緩存的事件發送出去。

3.1.5 日志格式

    日志最終以json格式發送,例子如下。client部分是設備相關的信息,events是對應的事件信息。

{
  "client": {          
    "ln": "zh",        
    "density": "3.0",    
    "tel": "",            
    "userid": "",         
    "appid": "731224921",                
    "mac": "f4:8b:32:af:22:e9",         
    "iscrack": "0",                       
    "timezone": "Asia/Shanghai",         
    "nettype": "lte",                    
    "longitude": "",                     
    "os": "4.4.4",                       
    "platform": "android",               
    "module": "MI 4LTE",                
    "sr": "1080*1920",                   
    "sdkver": "1.1.5",                  
    "isp": "46002",                      
    "imei": "867831028457919",           
    "udid": "",                          
    "appver": "1.0",                     
    "ip": "10.0.2.15",                   
    "ismobile": "1",                     
    "guid": "787f7300-37e2-34d8-b101-c8ef415385ae",   
    "requesttime": "1451272912",                       
    "vendorid": "",                                    
    "netstatus": "1",                                  
    "advertid": "",                                    
    "sim": "898600310115f0024716",                     
    "latitude": "",                                    
    "channel": "12"                                    
  },
  "events": [         
    {
      "eventtime": "1451028244",
      "duration": "",
      "isfirst": "",
      "pagedur": "1",
      "defineid": "",
      "moduleid": "",
      "prepageid": "",
      "params": "",
      "modulecnt": "",
      "currevent": "page",
      "pageid": "MAIN"
    },
    {
      "eventtime": "1451028245",
      "duration": "",
      "isfirst": "",
      "pagedur": "1",
      "defineid": "",
      "moduleid": "",
      "prepageid": "MAIN",
      "params": "",
      "modulecnt": "",
      "currevent": "page",
      "pageid": "MAIN"
    }
  ]
}

3.1.6 日志壓縮方法

    為了節省流程,日志在傳輸之前需要進行壓縮。壓縮使用 gzip 方法。直接對 json 字符串進行 gzip,然后輸出的數據進行 base64,最后為了用get方法請求,需要再進行一次 UrlEncode。壓縮后的數據形式如下。經測試,在包含50個左右的event時,壓縮后大小在2k以內。

 data=H4sIAAAAAAAAAM1STW%2FUMBD9KyhH1A3%2BSuLkxgHQSgUOIHGgHBx7sms1cYLtVGpX%2Fe%2BMk91qKwFSqx6qXObNeGbee5NDpnsLLmbNIetd1mR3%2B%2BwiM%2BCCjbcIeU4QR%2BgxxmAO4K1ZYzVNS1hxypioGcXcoDRmOtHItuGsUV3DWAM1VmzQXulrrC4D7QB3owOE74NVV%2B%2B%2B7ZXb7ZXFmoMYb6dU%2BrH9uMVEP7qdjbNJKUp5zhmticTCGDAjcvwQTL2K3egHTCln%2FIjckM9o5j71fd6%2BEZffP2Aq%2BDSGSPKW1ixxCeb6BpZkTvNioTqluSUhLKEBLEJZVpJTwqQoqpomRbM5c%2BI0IQ20qZ1WLKdVzoTMGV0NGMbWLmSSU7t5NU9WXcUJ2fAK2IYLIzctJXSjJXSCFlwWCvC1h98zhJhsS%2F2iYKREHmnbDTgzPhwFzQtRxTkc1yiDzOKpGmyyR9YStaEYSosORYqKlslmFU8uM55TVvMipTVexi33pyy7v8gAF0Yc%2F%2FOwho858SJpNbPHYaNbt9rQWR%2FiCia1Aywf6RnorIMTvfVaJzR5SI8foPJqCOcPtTvO1LP3CxeEqeW4Zum8%2FPpp%2BwV5%2F5VttfxG%2F2JLHtF9PtsAIeD4pcQ1iEK3oCtetoSrVtNO1x2pO1Hymsv%2Fi8PT%2Bniu7nnCXuoMLyfsaVeTr%2FMf%2B3X%2FB5yw2A5OBQAA

3.1.7 接入flume測試

   flume是一個常用的日志收集工具,下一節做具體說明。flume提供了很多日志的接入方式,http就是其中一種。只需要在app中向flume指定的服務器端口發送http請求,即可完成日志的收集。

3.1.8 部分參考資料

    http://stackoverflow.com/questions/5586197/android-user-agent              --- 安卓獲取 user agent

    http://wingjang.blog.163.com/blog/static/479134422013107111424348/      --- base64 編碼的換行問題

    http://blog.csdn.net/liuhe688/article/details/6733407                             ---  activity 的生命周期

    http://blog.csdn.net/gouguofei/article/details/7775752                            --- 安卓監聽程序進入后台

    http://blog.csdn.net/m_changgong/article/details/7608911                      ---  安卓監聽手機鎖屏

 

 

3.2 Flume

      flume 的簡介參考這篇帖子,http://shiyanjun.cn/archives/915.html。

      在這個項目中,配置了一個http source,兩個memory channel,兩個 sink;一個是 file roll sink,用來把日志寫到本地文件,另一個是 kafka sink,用來將日志推到 kafka 上,以便后續處理。

3.3 Kafka

3.4 Storm

     storm 的簡介參考,http://www.searchtb.com/2012/09/introduction-to-storm.html。

     在這個項目中,topology 中一共有一個spout 和 三個 bolt;spout 是從kafka中取數據,然后emit;第一個 bolt 是日志的解析,通過 UrlDecode -> 反base64 -> gunzip,可以恢復出日志json字符串;再將json拆成若干個events數據行,然后以list的形式發送;第二個bolt是將數據寫入hbase,每個event對應一行數據,每行數據都帶有全部采集項信息;第三個bolt是創建hbase表,日志表目前按照日期每天新建一個。當第二個bolt插入時發現沒有建表時,才會執行第三個bolt。

3.5 HBase

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM