日志分析

生产中会出现大量的系统日志、应用程序日志,安全日志等,通过贵日志的分析可以了解服务器的负载,健康状况,可以分析客户的分布情况、客户的行为,甚至基于这些分析可以做出预测。
一般采集流程:

  • 日志产出->采集(logstash、Flumen、Scribe)->存储->分析->存储(数据库、NoSQL)->可视化

开源实时日志分析ELK平台
Logstash收集日志,并存放到ElasticSearch集群中,Kibana则从ES集群中查询数据生成图表,返回游览器端

数据提取

半结构化数据
日志是半结构化数据,是有组织的、有格式的数据。可以分割成行和列,就可以当作表理解和处理了,当然也可以分析里面的数据。
文本分析
日志是文本文件,需要依赖文件IO、字符串操作、正则表达式等技术
通过这些技术就可以将日志需要的数据提取出来了
使用正则表达式

import re
s='''123.125.71.36 - - [06/Apr/2017:18:09:25 +0800] \
"GET / HTTP/1.1" 200 8642 "-" "|Mozilla/5.0 (compatible; \
Baiduspider/2.0; +http://www.baidu.com/search/spider.html)\"'''
pattern='''([\d.]{3,}) - - \[(.*)\] "(\w+) (\S+) (.*)" (\d+ \d+) "-" "(.*)"'''
regex=re.compile(pattern)
def extract(log:str):
    m=regex.match(log)
    if m:
        print(m.groups())
extract(s)

python flet实时读取日志 python解析日志_数据


使用命名分组

import re
s='''123.125.71.36 - - [06/Apr/2017:18:09:25 +0800] \
"GET / HTTP/1.1" 200 8642 "-" "|Mozilla/5.0 (compatible; \
Baiduspider/2.0; +http://www.baidu.com/search/spider.html)\"'''
pattern='''(?P<ip>[\d.]{3,}) - - \[(?P<time>.*)\] "(\w+) (\S+) (.*)" (\d+ \d+) "-" "(?P<useragent>.*)"'''
regex=re.compile(pattern)
def extract(log:str):
    m=regex.match(log)
    if m:
        print(m.groups())
        print('ip :{}'.format(m.group('ip')),'time :{}'.format(m.group('time')),'useragent :{}'.format(m.group('useragent')),sep='    ')
extract(s)

python flet实时读取日志 python解析日志_HTTP_02


使用上面的分组就能提取想要的所有的组,也就是我们想要的数据,为了方便可以使用命名分组

映射

对每一个字段命名,然后与值和类型转换的方法对应

最简单的方式,就是使用正则表达式分组

import re
s='''123.125.71.36 - - [06/Apr/2017:18:09:25 +0800] \
"GET / HTTP/1.1" 200 8642 "-" "|Mozilla/5.0 (compatible; \
Baiduspider/2.0; +http://www.baidu.com/search/spider.html)\"'''
pattern='''(?P<ip>[\d.]{3,}) - - \[(?P<time>.*)\] "(\w+) (\S+) (.*)" (\d+ \d+) "-" "(?P<useragent>.*)"'''
regex=re.compile(pattern)
#使用映射将得到的结果转换成需要的类型
con={
    'datetime':lambda time:datetime.datetime.strptime(time,"%d/%b/%Y:%H:%M:%S %z"),
}#这里举例只转换了时间
def c(long:str):
    m=regex.match(long)
    if m:
        return {k:con.get(k,lambda x: x)(v) for k,v in m.groupdict().items()}
print(c(s))

python flet实时读取日志 python解析日志_数据_03

异常处理
日志中不免出现一些不匹配的行,需要处理,这里使用re.match方法,有可能匹配不上,需要加一个判断,采用抛出异常的方式,让调用者活的异常并自行处理

import re
s='''123.125.71.36 - - [06/Apr/2017:18:09:25 +0800] \
"GET / HTTP/1.1" 200 8642 "-" "|Mozilla/5.0 (compatible; \
Baiduspider/2.0; +http://www.baidu.com/search/spider.html)\"'''
pattern='''(?P<ip>[\d.]{3,}) - - \[(?P<time>.*)\] "(\w+) (\S+) (.*)" (\d+ \d+) "-" "(?P<useragent>.*)"'''
regex=re.compile(pattern)
con={
    'time':lambda time:datetime.datetime.strptime(time,"%d/%b/%Y:%H:%M:%S %z"),
}
def c(long:str):
    m=regex.match(long)
    if m:
        return {k:con.get(k,lambda x: x)(v) for k,v in m.groupdict().items()}
    #考虑匹配不到的情况
    else:
        #采用报错
        #raise Exception('No match. {}'.format(s))
        #采用返回特殊值
        return None
print(c(s))
def load(path):
    with open(path) as f:
        for line in f:

数据载入
对于本项目来说,数据就是日志的一行行记录,载入数据就是文件IO的读取,将获取的数据的防范封装成函数

def load(path):
    with open(path) as f:
        for line in f:
            fields=extract(line)
            if fields:
                yield fields
            else:
                continue

日志文件的加载
目前实现的代码中,只能接受一个路径,修改为接受一批路径。
可以约定一个路径下文件的存放形式:

  • 如果送来的是一批路径,就迭代其中路径。
  • 如果路径是一个普通的文件,就直接加载这个文件。
  • 如果路径是一个目录,就便利路径下所有指定类型的文件,每一个我呢见按照行处理,可以提供参数处理是否递归子目录
from pathlib import Path
def load(*paths,encoding='utf-8',ext='*.log',recursive=False):
    for x in paths:
        p=Path(x)
        #目录处理
        if p.is_dir():
            if isinstance(ext,str):
                ext=[ext]
            else:
                ext=list(ext)
        for e in ext:
        files=p.rglob(e)if recursiversive else p.glob(e)
            yield from loadfile(str(file.absolute()),encoding=encodingoding)
        elif p.is_file():
            yield from loadfile(str(file.absolute()),encoding=encodingoding)

完整代码

from pathlib import Path
import datetime
import re
pattern='''(?P<ip>[\d.]{3,}) - - \[(?P<time>.*)\] "(\w+) (\S+) (.*)" (\d+ \d+) "-" "(?P<useragent>.*)"'''
regex=re.compile(pattern)
conversion={
    "datetime":lambda timestr:datetime.datetime.strptime(timestr,'%d%b%Y:%H:%M:%S %z')
}
def extract(logline:str)->dict:
    """返回字段的字典,如果返回None说明匹配失败"""
    m=regex.match(logline)
    if m:
        return {k:conversion.get(k,lambda x :x)(v)for k,v in m.groupdict().items()}
    else:
        return None #或输出日志记录
def loadfile(filename:str,encoding='utf-8'):
    """装载日志文件"""
    with open(filename,encoding=encoding) as f :
        for line in f:
            fields=extract(lien)
            if fields:
                yield fields
            else:
                continue
from pathlib import Path
def load(*paths,encoding='utf-8',ext='*.log',recursive=False):
    """装载日志文件"""
    for x in paths:
        p=Path(x)
        #目录处理
        if p.is_dir(): #处理目录
            if isinstance(ext,str):
                ext=[ext]
            else:
                ext=list(ext)
        for e in ext:
        files=p.rglob(e)if recursiversive else p.glob(e)
            yield from loadfile(str(file.absolute()),encoding=encodingoding)
        elif p.is_file():
            yield from loadfile(str(file.absolute()),encoding=encodingoding)