Python 日誌處理(三) 日誌狀態碼分析、瀏覽器分析

在企業中,從日誌中提取數據進行分析,能夠幫助企業更加了解用戶行爲,用戶最感興趣的產品或者內容,分析獲得數據後,能夠決定企業在從此的走向。html

從這些日誌數據中,比較重要的有:python

1. 用戶訪問最多的url,即用戶在企業網站最感興趣的產品或者內容瀏覽器

2. 用戶羣體的的主要線路是什麼?移動?聯通?電信?緩存

3. 用戶訪問的高峯期是何時?最高PV(訪問量)、UV(獨立訪客)、IP(獨立IP)。服務器

4. 各時段狀態碼數。好比304,表示靜態資源在沒有發生改變時,服務器要求客戶使用了瀏覽器本地的緩存,能夠下降服務器流量負載等。40三、404若是異常得出現不少,則要根據訪問得url來判斷是否有惡意用戶在對網站目錄進行掃描和探測。400、500等狀態碼不少的狀況就須要運維人員及時分析並排查緣由。多線程

5. 客戶瀏覽器的名稱、版本。統計出各類瀏覽器的分佈狀況,好比:若是手機瀏覽器、IE 6.0版本瀏覽器訪問記錄不少,則大概能夠判斷出用戶羣體大概的操做系統是winXP,win7以上版本,或是手機訪問。那就須要考慮是否要對特定版本瀏覽器進行頁面優化,或者若是客戶是手機瀏覽器,那是否要壓縮網站頁面大小,下降流量消耗,亦或是否要對手機端優化,提高用戶體驗,緊緊得抓住客戶。。app

 

這裏涉及部分SEO方面知識,僅做了解便可,若是企業真正須要了,再深刻學習。運維

 

下面的例子對訪問狀態碼和瀏覽器名稱、版本進行了統計,以引出日誌分析、數據挖掘的重要性:ide

 

import datetime
import re
from queue import Queue
import threading
from pathlib import Path
from user_agents import parse
from collections import defaultdict


# 正則,文件讀取,時間窗口,隊列,多線程,高階函數,分發器,嵌套函數

logline = '''183.60.212.153 - - [19/Feb/2013:10:23:29 +0800] "GET /o2o/media.html?menu=3 HTTP/1.1" 200 16691 "-" "Mozilla/5.0 (compatible; EasouSpider; +http://www.easou.com/search/spider.html)"'''

pattern = '''(?P<remote_addr>[\d\.]{7,}) - - (?:\[(?P<datetime>[^\[\]]+)\]) "(?P<request>[^"]+)" (?P<status>\d+) (?P<size>\d+) "[^"]+" "(?P<useragent>[^"]+)"'''

# 數據源處理
ops = {
    'datetime': lambda timestr: datetime.datetime.strptime(timestr, "%d/%b/%Y:%H:%M:%S %z"),
    'request': lambda request: dict(zip(('method', 'url', 'protocol'), request.split())),
    'status': int,
    'size': int,
    'useragent': lambda useragent: parse(useragent)
}

regex = re.compile(pattern)

def extract(line):
    matcher = regex.match(line)
    if matcher:
        return {k: ops.get(k, lambda x: x)(v) for k, v in matcher.groupdict().items()}


def openfile(path:str):
    with open(path) as f:
        for line in f:
            fields = extract(line)
            if fields:
                yield fields  # return generator objects,next(load(path))
            else:
                # TODO 不合格數據有哪些
                continue  # TODO 解析失敗就拋棄,或者打印日誌


def load(*paths):
    '''裝載日誌文件或路徑'''
    for item in paths:
        p = Path(item)
        if not p.exists():
            continue

        if p.is_dir():
            for file in p.iterdir():
                if file.is_file():
                    yield from openfile(str(file))
        elif p.is_file():
            yield from openfile(str(p))


def window(src:Queue, handler, width: int, interval: int):
    '''
    窗口函數
    :param src: 數據源,生成器,用來拿數據
    :param handler: 數據處理函數
    :param width: 時間窗口寬度,秒
    :param interval: 處理時間間隔,秒/ 時間偏移量,秒
    :return:
    '''

    start = datetime.datetime.strptime('1970/01/01 01:01:01 +0800', '%Y/%m/%d %H:%M:%S %z')
    current = datetime.datetime.strptime('1970/01/01 01:01:02 +0800', '%Y/%m/%d %H:%M:%S %z')
    delta = datetime.timedelta(seconds=width-interval)

    buffer = []  #窗口裏的待計算數據

    while True:  #while True方式迭代queue
        # 從數據源獲取數據
        data = src.get()   # block阻塞的

        if data:
            buffer.append(data)
            current = data['datetime']

        if (current - start).total_seconds() >= interval:
            ret = handler(buffer)      # 如何處理
            print("{}".format(ret))

            start = current

            buffer = [i for i in buffer if i['datetime'] > current - delta]


def donothing_handler(iterable:list):
    # print(iterable)
    return iterable


# 狀態碼時間段百分比分析
def status_handler(iterable:list):
    d = {}
    for item in iterable:
        key = item['status']
        if key not in d:
            d[key] = 0
        d[key] += 1

    total= sum(d.values())
    return {'{}: {:.2f}%'.format(k,v/total*100) for k,v in d.items()}


# 瀏覽器分析函數
ua_dict = defaultdict(lambda : 0)  # 做用域改成全局以後,字典遞增保存全部ua及其版本
def browser_handler(iterable):
    for item in iterable:
        ua = item['useragent']
        key = (ua.browser.family, ua.browser.version_string)
        ua_dict[key] += 1
    return ua_dict


# 分發器,嵌套函數
def dispatcher(src): 
    queues = []  # 隊列列表
    threads = []  # 線程管理

    def reg(handler, width, interval):
        q = Queue()    # 分配隊列
        queues.append(q)  # 方便調用

        t = threading.Thread(target=window,args=(q, handler, width, interval))
        threads.append(t)



    def run():
        for t in threads:
            t.start()

        for x in src:
            for q in queues:
                q.put(x)

    return reg,run

	
reg,run = dispatcher(load('test.log'))

# reg註冊 窗口
# reg(donothing_handler, 10, 5)    #註冊測試
# reg(status_handler, 10, 5)       # 註冊狀態碼處理函數
reg(browser_handler, 60, 60)       # 註冊useragent處理函數,注意時間窗口寬度

run()
相關文章
相關標籤/搜索