假设有如下业务场景。一个生产系统的日志文件如下,还在不断增加...[ncms@UPZZGAP02logs]$pwd/home/ncms/ncms/logs[ncms@UPZZGAP02logs]$lltotalusage797020-rw-rw-r--1ncmsncms495465795Nov3017:10ansible.log-rw-rw-r--1ncmsncms2251937Nov3017:10celery_beat.log-rw-rw-r--1ncmsncms16003Nov1510:26celery_flower.log-rw-rw-r--1ncmsncms704211411月30日17:10celery_worker.log-rw-r--r--1ncmsncms2466587311月30日17:10db_error.log-rw-r--r--1ncmsncms52428571Nov2818:46db_error.log.1-rw-r--r--1ncmsncms52428691Nov2406:43db_error.log.2-rw-r--r--1ncmsncms22410652Nov1915:16db_error.log.3-rw-r--r--1ncmsncms2806498511Jan3017:10db_info.log-rw-r--r--1ncmsncms5242663011月28日13:29db_info.log.1-rw-r--r--1ncmsncms5242735711月24日03:48db_info.log.2-rw-r--r--1ncmsncms2427676711月19日15:16db_info.log.3-rw-rw-r--1ncmsncms4249011月30日13:06ncms_access。log-rw-rw-r--1ncmsncms2407210月30日15:33ncms_error.log-rw-rw-r--1ncmsncms1350318Nov3016:38nginx_access.log-rw-rw-r--1ncmsncms1685Nov718:15nginx_error.log-rw-rw-r--1ncmsncms24001Nov1510:27supervisord.log-rw-rw-r--1ncmsncms645742Nov3016:38uwsgi.log[ncms@UPZZGAP02logs]$du-sh*473Mansible.log2.2Mcelery_beat.log16Kcelery_flower.log6.8Mcelery_worker.log24Mdb_error.log51Mdb_error.log.151Mdb_error.log.222Mdb_error.log.327Mdb_info.log51Mdb_info.log.151Mdb_2info4Mlog344Kncms_access.log24Kncms_accessMerror.log1.3.0Knginx_error.log24Ksupervisord.log632Kuwsgi.log[ncms@UPZZGAP02logs]$有应用、数据库、Celery、Nginx、uwsgi、supervisord、Ansible的日志。日志473M,以后肯定会更大。现在我们需要使用某些关键字来搜索和分析日志。我们应该怎么做?最简单粗暴的方法就是使用grep之类的命令递归搜索所有.log文件,但是这样会消耗大量内存,影响机器性能。考虑使用数据管道(类似于Unix管道)来迭代处理数据。使用Python生成器函数是一种实现流水线机制#!/usr/bin/envpython#-*-coding:utf-8-*-#__author__='liaogaoxiang'importosimportfnmatchimportgzipimportbz2importre#问题,你呢想要在数据管道(类似于Unix管道)中迭代处理数据。比如你有大量的数据#需要处理,但是不能一次性全部放入内存。可以使用生成器“””实现数据处理管道文件格式如下foo/access-log-012007.gzaccess-log-022007.gzaccess-log-032007.gz...access-log-012008bar/access-log-092007.bz2...access-log-022008"""defgen_find(filepat,top):"""查找目录树下所有匹配shell正则模式的文件名:paramfilepat:shell正则模式:paramtop:目录路径:return:文件绝对路径生成器"""forpath,_,文件名inos.walk(top):forfileinfnmatch.filter(filenames,filepat):yieldos.path.join(path,file)defgen_opener(filenames):"""每次打开一个文件都会生成一个文件对象,在调用下一次迭代前关闭文件:paramfilenames:由多个文件绝对路径组成的可迭代对象:return:文件名中文件名的文件对象生成器“””:iffilename.endswith('.gz'):f=gzip.open(filename,'r',encoding='utf-8')eliffilename.endswith('.bz2'):f=bz2.open(filename,'r',encoding='utf-8')else:f=open(filename,'r',encoding='utf-8')yieldff.close()defgen_concatenate(iterators):"""将输入序列连接成一个很长的行序列。:paramiterators::return:returniterators中生成器"""为它产生的所有值:yieldfromitdefgen_grep(pattern,lines):"""使用正则模式匹配行:parampattern:正则模式匹配:param行:多行:返回:结果生成器“””pat=re.compile(pattern)forn,lineinenumerate(lines,start=1):ifpat.search(line):yieldn,lineif__name__=="__main__":filenames=gen_find('*.log','/home/ncms/ncms/logs')files=gen_opener(filenames)lines=gen_concatenate(files)user_action=gen_grep('(?i)liaogaoxiang_kd',lines)forn,lineinuser_action:print(line)查询所有包含用户liaogaoxiang_kd的记录,数据结果如下:[views:post]:2018-11-0718:13:09.841490-users-liaogaoxiang_kdsuccessfullyloggedin![views:get]:2018-11-0718:16:04.681519-users-liaogaoxiang_kd访问用户信息列表[views:post]:2018-11-0718:16:23.866700-users-liaogaoxiang_kd编辑用户信息[views:get]:2018-11-0718:16:23.878949-users-liaogaoxiang_kd访问过的用户信息列表[views:get]:2018-11-0718:16:25.641090-users-liaogaoxiang_kd访问了用户信息列表[views:post]:2018-11-0718:16:42.671377-users-liaogaoxiang_kd编辑了用户信息[views:get]:2018-11-0718:16:42.719873-users-liaogaoxiang_kd访问了用户信息列表[views:post]:2018-11-0811:17:42.627693-users-liaogaoxiang_kd成功登录!如果需要查询其他错误信息,只需替换gen_grep('(?i)liaogaoxiang_kd',lines)中的关键字即可!在管道中处理数据可以用来解决其他各种问题,包括解析、读取实时数据、定时轮询等。为了理解上面的代码,理解yield语句是数据的生产者是很重要的而for循环语句是数据By的消费者。当这些生成器链接在一起时,每个生成器都会将一个数据元素传递到迭代处理管道的下一阶段。这种方法的一个非常好的特性是每个生成器函数都很小且独立。这很容易编写和维护。在很多情况下,这些功能如果更通用的话,可以在其他场景中复用。而最终组合这些组件的代码看起来非常简单易懂。使用此方法非常节省内存。上面的代码即使在非常大的文件目录中也能正常工作。事实上,由于使用了迭代处理,代码在运行时只需要非常少量的内存。在调用gen_concatenate()函数时,您可能会有点困惑。此函数的目的是将输入序列连接成一长串行。itertools.chain()函数也有类似的功能,但需要传入所有可迭代对象作为参数。在上面的示例中,您可能会编写类似lines=itertools.chain(*files)的内容,这将导致gen_opener()生成器提前完全消耗。但是由于gen_opener()生成器每次都会生成一个打开的文件,该文件在下一次迭代时关闭,所以这里不能使用chain()。上述方案可以避免这种情况。yieldfrom语句出现在gen_concatenate()函数中,它将yield操作委托给父生成器。语句yieldfromit简单地返回生成器it产生的所有值。搜索并关注微信公众号:ID:bbcoins
