网络编程
位置:首页>> 网络编程>> Python编程>> 浅析python 定时拆分备份 nginx 日志的方法

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

作者:草青工作室  发布时间:2023-08-04 06:03:37 

标签: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('^".+?"|^\[.+?\]')
# 增量写备份文件
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))

来源:https://blog.csdn.net/xxj_jing/article/details/105762962

0
投稿

猜你喜欢

  • 有的时候,我们为了保持网页的美观,需要将较长的文字在一定长度时截断。比如我们希望在列表中显示文章标题的前15个字,那么一个这样的标题:“rs
  • 当你准备全面进军web标准时,有时候你是不是被表格的弄得焦头烂额呢?比如,原来使用“非法”的nobr现在要用什么来代替呢?今天,就让我来一个
  • 总结了一下自己工作中使用到的注释书写规范,没有什么技术含量,只是用于统一制作方式,方便维护。包含了“区域注释”、“单行注释”、“注释层级”和
  • 栅格就是你对页面版式的规划你日常所见的许多页面都有栅格存在。你可能注意不到,但它确实存在,并且支撑着设计内容,建立整体的架构,引导着页面的元
  • asp之家注:那么为什么要使用分页呢?当记录不多的时候,如10个或20个,我们可以也没必要使用分页来显示数据,但是数据是在不断增加的,当到了
  • “选项卡”经常会被混同为“滑动门”,滑动门只是一种背景自适应内容的实现方法,不只可以应用在导航菜单,一样也可以应用在其他地方。例如选项卡中就
  • 问题1:使用.net2005自带的SQL-Express连接不上。解决方法:1.网络防火墙阻止数据库连接;2.默认SQL-Express没有
  •   我想大多数的人在编写ASP程序的时候,都碰到过类似的错误信息:   Error Num
  • Installing mysql (2.8.1) with native extensions /usr/local/lib/ruby/si
  • 作用:用ASP程序将页面中的电话号码生成图片格式。以下是引用片段:<% Call Com_CreatValidCode(Request
  • 利用oracle的dbms_random包结合rownum来实现,示例如下,随机取499户:select * from ( select *
  • 1.怎么样查看数据库字符集 [A]数据库服务器字符集select * from nls_database_parameters,其来源于pr
  • ORA-00600:internal error code,arguments:[num],[?],[?],[?],[?] 产生原因:这种错
  • 用过QQ的人应该都知道软键盘,他可以增强我们密码的安全性,以保证我们的密码资料不被非法监听。现在软键盘也用在了很多的网站上,像早期银行的在线
  • 这里所谓的复杂表单,是指表单中包含多种不同的输入类型,比如下拉列表框、单行文本、多行文本、数值等。在经常需要更换这类表单的场合,需要有一个表
  • 最近在无忧脚本混了一阵子,回复了一些贴子,自己却没有做出什么东东让大家看看,心里有些不安,于是写了下边的一点东西,本来应该发在类封装区的,考
  • PHP asXML()函数实例格式化 XML(版本 1.0)中的 SimpleXML 对象的数据:<?php $note=<&l
  • PL/SQL单行函数和组函数详解 函数是一种有零个或多个参数并且有一个返回值的程序。在SQL中Oracle内建了一系列函数,这些函数都可被称
  • Javascript 选择器(selector engine)似乎从 jQuery 流行以来就大行其道,改变了原有 Javascript 选
手机版 网络编程 asp之家 www.aspxhome.com