使用Python分析nginx日志

使用Python分析nginx日志

专栏作者:熊球

♚土木工程毕业,现从事web后端开发方面的工作,擅长python,flask框架等。

博客:codechat.cc

github地址:https://github.com/XiongQiuQiu/

相信各位web后端的小伙伴对Nginx并不陌生,它是是一款面向性能设计的HTTP服务器,具有占有内存少,稳定性高等优势。所以很多个人网站,或者公司都会选择使用nginx作为服务器。在使用nginx的时候,每一个http请求都会产生一条日志,通过python分析日志我们可以清楚的了解网站的pv,uv等一些重要数据。 在服务器上我们通常使用logrotate来分割当天日志进行分析, 假设我当天结束分割出的的日志名字为log20101001.gz, 我们使用python的gzip库来读取这个压缩文件所以我们可以直接使用gzip库来打开文件

class an_log(object):"""分析记录"""    def __init__(self, filename):        self.filename = filename        self.picid_value = {}  # 一个用于存储所有pv,uv的字典    def read_log(self):        f = gzip.open(self.filename, 'r')        for line in f:            all_line = line.split()

一般一条nginx数据是这样的:

  - 180.171.241.42 tb.lifehp.com - - [30/Oct/2016:23:58:03 +0800] "GET /index.php?m=newbar&a=operate&type=1&op=0&param=A000184&adsid=14&picid=141&time=1477842977818 HTTP/1.1" 200 0.000 342 31 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Mobile/14A456 MicroMessenger/6.3.29 NetType/WIFI Language/zh_CN" "-" -

看起来很杂乱无章,其实在分析pv,uv的时候我们着重需要的数据一共只有几个, 一个是ip:- 180.171.241.42, 一个是参数:GET/index.php?m=newbar&a=operate&type=1&op=0&param=A000184&adsid=14&picid=141&time=1477842977818 HTTP/1.1, 一个是参数:GET/index.php?m=newbar&a=operate&type=1&op=0&param=A000184&adsid=14&picid=141&time=1477842977818 HTTP/1.1

每一个pv是由这些参数共同决定的,如果这些参数全部一致那就是一个pv,如果在参数一致的情况下ip从没出现过那就是一个uv, 所以我们只需要解析这些参数就可以

def read_log(self):    f = gzip.open(self.filename, 'r')    for line in f:        all_line = line.split()        try:            a_line = dict(k.split('=') for k in all_line[8].split('&'))            # 把所有参数解析到一个字典中,key为参数名,value为参数值类似于{type:1,param:A00184}            ip = all_line[1]  # 获取ip            media_id = a_line['param']            op = a_line['op']        except:            continue        try:            adsid = a_line.get('adsid', 'null')            picid = a_line.get('picid', 'null')            area_name = a_line.get('area', 'null')            os = a_line.get('os', '0')        except:            continue

使用try,except语句是因为很多日志并不是我们想要的类似于GET /small/v10/css/tlbs.css HTTP/1.1,当碰到这种类型的日志的时候,生成字典后通过try如果提取不到我们需要的参数就说明这条日志不需要,通过continue直接分下一条日志 pic = picid + media_id + adsid + op + area_name + os # 把参数组合生成唯一键名

如果键名存在就直接对pv,uv数值操作

if pic in  self.picid_value.keys():    self.analysis_pv(pic)    self.analysis_uv(pic, ip)

pv += 1 uv通过判断ip是否存在来判断是否加一

def analysis_pv(self, pic,):    self.picid_value[pic]['pv'] += 1    return self.picid_valuedef analysis_uv(self, pic, ip):    if ip not in self.picid_value[pic]['ip']:        self.picid_value[pic]['uv'] += 1        self.picid_value[pic]['ip'].add(ip)    return self.picid_value

这里ip要使用set集合,如果使用列表 每次判断ip是否存在都要进行一次遍历时间复杂度为O(n),而集合判断是否存在时间复杂度为O(1)大大提高性能

如果键名不存在则进行初始化键

if pic in  self.picid_value.keys():    self.analysis_pv(pic)    self.analysis_uv(pic, ip)else:    self.picid_value[pic] = {}    self.picid_value[pic]['picid'] = picid    self.picid_value[pic]['adsid'] = adsid    self.picid_value[pic]['media_id'] = media_id    self.picid_value[pic]['op'] = op_name_dict[op]    self.picid_value[pic]['os'] = os_name_dict[os]    self.picid_value[pic]['pv'] = 0    self.picid_value[pic]['uv'] = 0    self.picid_value[pic]['ip'] =set()    self.picid_value[pic]['area']=area_name_dict[area_name]    self.analysis_pv(pic)    self.analysis_uv(pic, ip)

最后返回一个字典

return self.picid_value

然后把分析的数据写入excel,这里我使用的是xlwt库

def write_excel(self, excel_name):    workbook = xlwt.Workbook()    worksheet = workbook.add_sheet('toolbar')    cl_name = [u'媒体', 'op', 'os', 'adsid', 'picid', u'位置',  'pv', 'uv']    c = 0    for data in cl_name:        worksheet.write(0, c, data)        c += 1    row_list = ['media_id', 'op', 'os', 'adsid', 'picid', 'area',  'pv', 'uv']    r = 1    for pic_name in self.picid_value:        cl = 0        for data_name in row_list:            worksheet.write(r, cl, self.picid_value[pic_name][data_name])            cl += 1        r += 1    workbook.save(excel_name)

使用argparse添加一些参数说明,使用的时候直接 -h就能看到各种参数名字与作用

if __name__ == '__main__':    parser = argparse.ArgumentParser('log statistic')    parser.add_argument('-f', '--file', default=None, help='filename')    args = parser.parse_args()    log_value = an_log(args.file)    if log_value:        log_value.read_log()        file_name1 = 'example2.xls'        log_value.write_excel(file_name1)

也可以使用各种python数据分析库来进行分析,添加分析参数只需要在in_value方法中添加各种相应的参数字段就可以。水平有限,代码还有很多需要改进的地方,如果各位有什么好的想法和建议也欢迎反馈。

原文发布于微信公众号 - Python中文社区(python-china)

原文发表时间:2016-11-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏岑玉海

RavenDb学习(一)设计模式介绍

RavenDb是一个文档型的数据库,和芒果Db是一个类型的东西,但是公司选择了它,主要是因为它对事务的支持比较好,芒果Db在事务方面有问题。 下面有一个例子...

4097
来自专栏腾讯数据库技术

Linux调度原理介绍和应用(前篇)

3164
来自专栏Keegan小钢

App架构经验总结(一)

原文链接:http://keeganlee.me/post/architecture/20160303 版权声明:本文刊载在《程序员》杂志2016年3期,版权归...

2004
来自专栏编程一生

请一定记得升级java虚拟机

1024
来自专栏大闲人柴毛毛

使用Eclipse插件提高Java编码质量

代码质量概述 ? 怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行...

3247
来自专栏腾讯移动品质中心TMQ的专栏

基于 hook 和 gmock 开展单元测试

单元测试又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函...

7181
来自专栏.NET开发者社区

什么是ORM?为什么用ORM?浅析ORM的使用及利弊

什么是ORM ORM(Object-relational mapping),中文翻译为对象关系映射,是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术...

20110
来自专栏Spark学习技巧

深入理解Apache Flink核心技术

8043
来自专栏python开发者

python自动化测试(4)-使用第三方python库技术实现

python自动化测试(4)-使用第三方python库技术实现 1   概述 关于测试的方法论,都是建立在之前的文章里面提到的观点: 功能测试不建议做自动化 接...

2695
来自专栏大前端开发

从编程小白到全栈开发:理解异步

作为以JavaScript为主要开发语言的JS全栈开发者,是一定会碰上“异步(Asynchronous)”这个重要概念的,尽早的理解这个概念,会对你的JS编程生...

813

扫码关注云+社区