浅析python 定时拆分备份 nginx 日志的方法
一、背景:
nginx 的log 不会自动按天备份,而且记录时间格式不统一,此程序专门解决这两个问题;
二、windows 部署方式
1.在 nginx 目录,创建一个 nginx_logs_backup.bat 文件;文件内容如下
python nginx_logs_splter.py --nginxConf=nginx.conf --nginxDir=xxxxx --logPrefixs=access,error
2.在定时任务中加一个定时任务,调用这个 bat 文件;
2.1 开始-程序-管理工具-任务计划程序;
2.2 新建基本任务;
2.3 注意的一点是,在"编辑操作"窗口,在"起始于(可选)"这一栏需要填入 bat 所在目录,否则 bat 不会执行;
三、执行逻辑
1.将指定前缀的 log 在同目录创建一个临时文件(对源文件重命名),如:access_200426.log;
2.使用 nginx -s 命令,从容重启 nginx,重新创建 log;
3.读 access_200426.log 文件,将记是 2020-04-26 产生的日志,转存至 ./bac/access_200426.log 文件中;
4.删除临时文件 access_200426.log ;
注:同一天可多次执行,转存的 log 将增量添加;
四、调用方式
python nginx_logs_splter.py --nginxConf=nginx.conf --nginxDir=xxxxx --logPrefixs=access,error
参数:
nginxConf=nginx 配置文件
nginxDir=nginx 目录
logPrefixs=log文件前缀(多个逗号分隔)
五、nginx_logs_splter.py 源码
#!/usr/bin/env python3 # coding=utf-8 import os import sys import argparse import codecs import time,datetime import re ''' 拆分 nginx access log 日志不会自动按天创建,需要辅助任务把日志按天拆分备份,统一日志时间格式; 作者:草青工作室 ''' _version='200426.1' _isDebug = True _isDebug = False def logSpliter(nginxDir, prefix): #今日 today = datetime.datetime.now(); yymmdd_today = today.strftime('%y%m%d') #昨日 yestoday = datetime.date.today()-datetime.timedelta(days=1) yymmdd_yestoday = yestoday.strftime('%y%m%d') # logFileFullName = os.path.join(nginxDir,"logs","%s.log"%prefix) tmpFileFullName = os.path.join(nginxDir,"logs","%s_%s.log"%(prefix,yymmdd_yestoday)) bacFileFullName = os.path.join(nginxDir,"logs","bac","%s-%s.log"%(prefix,yymmdd_yestoday)) print('%s\ntmpFileFullName=%s\nbacFileFullName=%s\n\n'%( '-'*60, tmpFileFullName, bacFileFullName)) start = datetime.datetime.now() totalCount = 0 with codecs.open(tmpFileFullName, 'r', 'utf-8') as f: for line in f.readlines(): totalCount += 1 print('总记录数\t%s\tfileName=%s' % (totalCount,tmpFileFullName)) # 针对 access log 的时间格式 dtAccess = re.compile('\d{1,2}/[a-zA-Z]+/\d{4}:\d{1,2}:\d{1,2}:\d{1,2}') # 针对 error log 的时间格式 dtError = re.compile('\d{4}/\d{1,2}/\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}') # 转换 access log 日期格式("24/Apr/2020:23:26:29 +0800" to 2020-04-24 23:26:29) dtReplace = re.compile('^".+"|^\[.+"#备份时间\t%s\n" % today.strftime('%Y-%m-%d %H:%M:%S')) outputFile.writelines("#版本号\t%s\n" % _version) #转存 tmp 文件 with open(tmpFileFullName, 'r', encoding='utf-8') as f: rows = 0 # 按行统计 while True: rows += 1 if rows % 10000 == 0: print('已分析\t%s/%s\t耗时\t%ss' % (rows ,totalCount ,(datetime.datetime.now() - start).seconds)) # ------ if _isDebug and rows>=35000: print('_isDebug = ',_isDebug) break # ------ line = f.readline() if not line: #等价于if line == "": break if line.startswith('#'): print("跳过注释内容=>",line) continue #时间格式适配 dt = None if 'access' in prefix: #获取时间 "24/Apr/2020:14:43:38 +0800" arr = dtAccess.findall(line) if len(arr) == 0: continue dt = datetime.datetime.strptime(arr[0],'%d/%b/%Y:%H:%M:%S') #转换时间格式 line = dtReplace.sub('"%s"'%dt.strftime('%Y-%m-%d %H:%M:%S'),line) elif 'error' in prefix: #获取时间 2020/04/24 23:37:46 arr = dtError.findall(line) if len(arr) == 0: continue dt = datetime.datetime.strptime(arr[0],'%Y/%m/%d %H:%M:%S') if not dt: print('日期转换失败 dt is none') continue yymmdd_log = dt.strftime('%y%m%d') #小于昨天继续 if yymmdd_log<yymmdd_yestoday: #print('跳过,小于 %s'%yymmdd_yestoday) continue #大于昨天退出 if yymmdd_log>yymmdd_yestoday: print('退出,大于 %s'%yymmdd_yestoday) break #print(line) outputFile.writelines("%s"%line) #关闭输出文件流 if outputFile: outputFile.close() #分离后删除 tmp 文件 if os.path.exists(bacFileFullName): os.remove(tmpFileFullName) print('删除临时文件,%s\t%s'%(tmpFileFullName ,not os.path.exists(tmpFileFullName))) print('\n\n%s\n拆分完成,耗时 %s 秒 \nlog=%s' % ('*' * 30 , (datetime.datetime.now() - start).seconds , bacFileFullName)) pass ''' > f = open('test.txt', 'w') # 若是'wb'就表示写二进制文件 > f.write('Hello, world!') > f.close() python文件对象提供了两个“写”方法: write() 和 writelines()。 write()方法和read()、readline()方法对应,是将字符串写入到文件中。 writelines()方法和readlines()方法对应,也是针对列表的操作。它接收一个字符串列表作为参数,将他们写入到文件中,换行符不会自动的加入,因此,需要显式的加入换行符。 关于open()的mode参数: 'r':读 'w':写 'a':追加 'r+' == r+w(可读可写,文件若不存在就报错(IOError)) 'w+' == w+r(可读可写,文件若不存在就创建) 'a+' ==a+r(可追加可写,文件若不存在就创建) 对应的,如果是二进制文件,就都加一个b就好啦: 'rb' 'wb' 'ab' 'rb+' 'wb+' 'ab+' ''' def test(): # "24/Apr/2020:14:43:38 +0800" dt =time.time() print(time.strftime('%Y-%m-%d %H:%M:%S [%Z]',time.localtime(dt))) print(time.strftime('%y-%m-%d %I:%M:%S [%Z]',time.localtime(dt))) print(time.strftime('%d/%b/%Y %H:%M:%S [%Z]',time.localtime(dt))) print('-'*30) str = '24/Apr/2020:14:43:38' dt = datetime.datetime.strptime(str,'%d/%b/%Y:%H:%M:%S') print("%s[%s] => %s[%s]" % (str,type(str),dt,type(dt))) str = dt.strftime('%Y-%m-%d %H:%M:%S') print("%s [%s]" % (str,type(str))) pass ''' python中时间日期格式化符号: %y 两位数的年份表示(00-99) %Y 四位数的年份表示(000-9999) %m 月份(01-12) %d 月内中的一天(0-31) %H 24小时制小时数(0-23) %I 12小时制小时数(01-12) %M 分钟数(00=59) %S 秒(00-59) %a 本地简化星期名称 %A 本地完整星期名称 %b 本地简化的月份名称 %B 本地完整的月份名称 %c 本地相应的日期表示和时间表示 %j 年内的一天(001-366) %p 本地A.M.或P.M.的等价符 %U 一年中的星期数(00-53)星期天为星期的开始 %w 星期(0-6),星期天为星期的开始 %W 一年中的星期数(00-53)星期一为星期的开始 %x 本地相应的日期表示 %X 本地相应的时间表示 %Z 当前时区的名称 ''' def createTempFile(nginxConf,nginxDir,prefixArr): yestoday = datetime.date.today()-datetime.timedelta(days=1) yymmdd = yestoday.strftime('%y%m%d') for prefix in prefixArr: logFileFullName = os.path.join(nginxDir,"logs","%s.log"%prefix) tmpFileullName = os.path.join(nginxDir,"logs","%s_%s.log"%(prefix,yymmdd)) if not os.path.exists(logFileFullName): print('log 文件不已存在:%s'%tmpFileullName) continue if os.path.exists(tmpFileullName): print('tmp 文件已存在:%s'%tmpFileullName) continue #备份log os.rename(logFileFullName,tmpFileullName) if not os.path.exists(tmpFileullName): print('log 重命名失败:%s'%logFileFullName) continue print('%s rename %s'%(tmpFileullName,os.path.exists(tmpFileullName))) #重启 nginx cmd = 'nginx -p %s -c %s -s reload'%(nginxDir,nginxConf) print('%s\n执行 nginx reload 命令\n\t%s\n\n'%('-'*60,cmd)) #os.system() 将导致进程阻塞 os.system(cmd) #等待重启 time.sleep(3) #判断文件是否存在 print('rolad 命令已触发,验证log 是否新建') for prefix in prefixArr: log = os.path.join(nginxDir,"logs",'%s.log'%prefix) print('\t%s rename %s'%(log,os.path.exists(log))) print('\n') def main(nginxConf,nginxDir, logPrefixs): if not nginxDir or not logPrefixs: print("参数为空:--nginxDir={} --logPrefixs={}".format(nginxDir, logPrefixs)) return if not os.path.exists(nginxDir): print("文件不存在:--nginxDir={} ".format(nginxDir)) return conf = os.path.join(nginxDir,nginxConf) if not os.path.exists(conf): print("nginx config 不存在:--nginxConf={} ".format(conf)) return prefixArr = logPrefixs.split(',') #备份+重新加载 nginx createTempFile(nginxConf,nginxDir,prefixArr) #分离当天的log for prefix in prefixArr: try: print("备份 %s 文件"%prefix) logSpliter(nginxDir, prefix) except Exception as ex: print("备份 %s 异常"%prefix,ex) pass if __name__ == '__main__': parser = argparse.ArgumentParser(description='manual to this script') parser.add_argument('--nginxConf', type=str, default = None) parser.add_argument('--nginxDir', type=str, default = None) parser.add_argument('--logPrefixs', type=str, default= None) args = parser.parse_args() #test() ''' 功能: 备份执行时间-1天(昨天)的 nginx log,需要指定 log 的前缀,多个文件名逗号分隔; 运行逻辑: 1.将指定前缀的 log 在同目录创建一个临时文件(对源文件重命名),如:access_200426.log; 2.使用 nginx -s 命令,从容重启 nginx,重新创建 log; 3.读 access_200426.log 文件,将记是 2020-04-26 产生的日志,转存至 ./bac/access_200426.log 文件中; 4.删除临时文件 access_200426.log ; 注:同一天可多次执行,转存的 log 将增量添加; 调用方式: python nginx_logs_splter.py --nginxConf=nginx.conf --nginxDir=xxxxx --logPrefixs=access,error 参数: nginxConf=nginx 配置文件 nginxDir=nginx 目录 logPrefixs=log文件前缀(多个逗号分隔) windows 部署: 1.在 nginx 目录,创建一个 nginx_logs_backup.bat 文件;文件内容如下 python nginx_logs_splter.py --nginxConf=nginx.conf --nginxDir=xxxxx --logPrefixs=access,error 2.在定时任务中加一个定时任务,调用这个 bat 文件; 2.1 开始-程序-管理工具-任务计划程序; 2.2 新建基本任务; 2.3 注意的一点是,在"编辑操作"窗口,在"起始于(可选)"这一栏需要填入 bat 所在目录,否则 bat 不会执行; ''' sys.exit(main(args.nginxConf,args.nginxDir,args.logPrefixs))
下一篇:如何使用PyCharm将代码上传到GitHub上(图文详解)