浅析python 定时拆分备份 nginx 日志的方法


Posted in Python onApril 27, 2020

一、背景:

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('^".+?"|^\[.+?\]')
 # 增量写备份文件
 outputFile = open(bacFileFullName, 'a+', encoding='utf-8')
 # 写备注
 outputFile.writelines("#备份时间\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))

到此这篇关于浅析python 定时拆分备份 nginx 日志的方法的文章就介绍到这了,更多相关python nginx 日志内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python k-近邻算法实例分享
Jun 11 Python
python异常和文件处理机制详解
Jul 19 Python
python3之微信文章爬虫实例讲解
Jul 12 Python
Python数据分析之双色球统计单个红和蓝球哪个比例高的方法
Feb 03 Python
python如何拆分含有多种分隔符的字符串
Mar 20 Python
PyQt打开保存对话框的方法和使用详解
Feb 27 Python
在PyCharm中遇到pip安装 失败问题及解决方案(pip失效时的解决方案)
Mar 10 Python
Django 删除upload_to文件的步骤
Mar 30 Python
Django模型中字段属性choice使用说明
Mar 30 Python
django实现模板中的字符串文字和自动转义
Mar 31 Python
PyQt5连接MySQL及QMYSQL driver not loaded错误解决
Apr 29 Python
利用Python实现Excel的文件间的数据匹配功能
Jun 16 Python
python异步Web框架sanic的实现
Apr 27 #Python
python库skimage给灰度图像染色的方法示例
Apr 27 #Python
python实现密度聚类(模板代码+sklearn代码)
Apr 27 #Python
Django中文件上传和文件访问微项目的方法
Apr 27 #Python
详解Python中namedtuple的使用
Apr 27 #Python
Python PyQt5运行程序把输出信息展示到GUI图形界面上
Apr 27 #Python
使用python实现微信小程序自动签到功能
Apr 27 #Python
You might like
php获取淘宝分类id示例
2014/01/16 PHP
Laravel 5框架学习之Eloquent 关系
2015/04/09 PHP
PHP数组中头部和尾部添加元素的方法(array_unshift,array_push)
2017/04/10 PHP
我遇到的参数传递中 双引号单引号嵌套问题
2010/02/11 Javascript
自写简单JS判断是否已经弹出页面
2010/10/20 Javascript
JavaScript数组去重的3种方法和代码实例
2015/07/01 Javascript
Javascript技术栈中的四种依赖注入小结
2016/02/27 Javascript
JavaScript导航脚本判断当前导航
2016/07/12 Javascript
利用js获取下拉框中所选的值
2016/12/01 Javascript
js处理层级数据结构的方法小结
2017/01/17 Javascript
用file标签实现多图文件上传预览
2017/02/14 Javascript
Angular1.x自定义指令实例详解
2017/03/01 Javascript
JavaScript变量类型以及变量作用域详解
2017/08/14 Javascript
JS实现手写parseInt的方法示例
2017/09/24 Javascript
Three.js中网格对象MESH的属性与方法详解
2017/09/27 Javascript
薪资那么高的Web前端必看书单
2017/10/13 Javascript
vue.js响应式原理解析与实现
2020/06/22 Javascript
nodejs中用npm初始化来创建package.json的实例讲解
2018/10/10 NodeJs
[01:07:15]DOTA2-DPC中国联赛 正赛 DLG vs XG BO3 第二场 1月25日
2021/03/11 DOTA
Python中正则表达式详解
2017/05/17 Python
python爬虫入门教程--正则表达式完全指南(五)
2017/05/25 Python
详解python3中socket套接字的编码问题解决
2017/07/01 Python
python 编码规范整理
2018/05/05 Python
解决python3 json数据包含中文的读写问题
2018/05/10 Python
Python  unittest单元测试框架的使用
2018/09/08 Python
python3 面向对象__类的内置属性与方法的实例代码
2018/11/09 Python
pytorch 实现在预训练模型的 input上增减通道
2020/01/06 Python
Python读取配置文件(config.ini)以及写入配置文件
2020/04/08 Python
澳大利亚制造的羊皮靴:Original UGG Boots
2017/11/13 全球购物
小学教师管理制度
2014/01/18 职场文书
党代会心得体会
2014/09/04 职场文书
加入学生会自荐书
2015/03/05 职场文书
确保工程质量承诺书
2015/04/29 职场文书
background-position百分比原理详解
2021/05/08 HTML / CSS
面试必问:圣杯布局和双飞翼布局的区别
2021/05/13 HTML / CSS
不想升级Win11?教你彻底锁定老版Windows系统的方法(附下载地址)
2022/09/23 数码科技