设为首页 收藏本站
查看: 1114|回复: 0

[经验分享] python实现磁盘日志清理

[复制链接]

尚未签到

发表于 2018-8-8 10:54:11 | 显示全部楼层 |阅读模式
#!/usr/bin/env python  
# -*- coding: utf-8 -*-
  

  
import commands
  
import os
  
import time
  
import re
  
import getopt
  
import sys
  

  
# commands.getstatusoutput 返回两个元素的元组tuple(status, result),status为int类型,result为string类型
  
def execute_local_shell_cmd(cmd):
  
    status, result = commands.getstatusoutput(cmd)
  

  
    result = result.split("\n")
  

  
    return status, result
  

  
def send_alert_mail():
  
    pass
  

  

  

  
'''
  
获取某一磁盘的空间使用率
  
'''
  
def get_disk_used(disk_name):
  
    status, result = execute_local_shell_cmd("df | grep %s | awk '{print $5}'" % disk_name)
  
    return status, result[0]
  

  
#print(get_disk_used('/data0'))
  

  

  
'''
  
判断文件是否在指定时间内修改过
  
'''
  

  
def file_modify_in(file_path,time_interval='1d'):
  
    current_time = time.time()
  
    # os.path.getmtime 返回最后修改时间。返回从unix纪元开始的跳秒数
  
    if current_time - os.path.getmtime(file_path) < translate_time_interval_to_second(time_interval):
  
        return True
  
    return False
  

  
def translate_file_size_to_kb(file_size):
  
    # 将字符串所有大写字符转为小写
  
    file_size = str(file_size.lower())
  
    # 创建匹配数字1次或多次的数字且小数点出现一次或者不出现的;小数点后数字重复0次或多次模式对象
  
    pattern = re.compile(r'\d+\.?\d*')
  
    match = pattern.match(file_size)
  
    file_size_number = None
  
    if match:
  
        # 使用Match获得分组信息
  
        #print(match.group())
  
        file_size_number = float(match.group())
  
    else:
  
        raise IOError("Input {0} can't translate to byte."
  
                      "Current support g(gb)/m(mb)/k(kb)/b(byte)".format(file_size))
  
    #  endswith() 方法用于判断字符串是否以指定后缀结尾,如果以指定后缀结尾返回True,否则返回False。
  
    # 可选参数"start"与"end"为检索字符串的开始与结束位置。
  
    if file_size.endswith("g") or file_size.endswith("gb"):
  
        return file_size_number * 1024 * 1024 * 1024
  
    elif file_size.endswith("m") or file_size.endswith("mb"):
  
        return file_size_number * 1024 * 1024
  
    elif file_size.endswith("k") or file_size.endswith("kb"):
  
        return file_size_number * 1024
  
    elif file_size.endswith("b") or file_size.endswith("byte"):
  
        return file_size_number
  
    else:
  
        raise  IOError("Input {0} can't translate to byte."
  
                       "Current support g(gb)/m(mb)/k(kb)/b(byte)".format(file_size))
  
#print(translate_file_size_to_kb('10g'))
  

  
def translate_time_interval_to_second(time_interval):
  
    date_interval = str(time_interval.lower())
  
    pattern = re.compile(r'\d+')
  
    match = pattern.match(date_interval)
  
    date_interval_number = None
  
    if match:
  
        date_interval_number = int(match.group())
  
    else:
  
        raise IOError("Input {0} can't translate to second."
  
                      "Current support d(day)/h(hour)/m(min)/s(sec)".format(date_interval))
  
    if date_interval.endswith('d') or date_interval.endswith('day'):
  
        return date_interval_number * 24 * 3600
  
    elif date_interval.endswith('h') or date_interval.endswith('hour'):
  
        return date_interval_number * 3600
  
    elif date_interval.endswith('m') or date_interval.endswith('min'):
  
        return date_interval_number * 60
  
    elif date_interval.endswith('s') or date_interval.endswith('sec'):
  
        return date_interval_number
  
    else:
  
        raise IOError("Input {0} cant't translate to second."
  
                      "Current support d(day)/h(hour)/m(min)/s(second)".format(date_interval))
  

  
#print(translate_time_interval_to_second('7d'))
  
'''
  
关断文件是否可能是当前log文件
  
1) 修改改时间1天内
  
2) 以pattern结尾
  
'''
  
def probable_current_log_file(file_path,pattern='log',modify_in='1d'):
  
    if file_modify_in(file_path,time_interval=modify_in):
  
        return True
  
    return str(file_path).endswith(pattern)
  

  
'''
  
获取超过天数设置log,注意不会返回可能是当前正在修改的文件,查看probable_current_log_file
  
确定如何做该判断
  
'''
  
def get_clean_log_list_by_date(target_dir,before_days_remove='7d',pattern="log"):
  
    before_seconds_remove = translate_time_interval_to_second(before_days_remove)
  
    current_time = time.time()
  
    # os.listdir 返回指定文件夹包含文件或文件夹的名字列表
  
    for candidate_file in os.listdir(target_dir):
  
        candidate_file_fullpath = "%s/%s" %(target_dir,candidate_file)
  
        # 是否存在一个普通文件
  
        if os.path.isfile(candidate_file_fullpath):
  
            candidate_file_mtime = os.path.getmtime(candidate_file_fullpath)
  

  
            # find\(\)根据是否包含字符串,如果包含有,返回开始的索引值,否则返回-1
  
            if current_time - candidate_file_mtime > before_seconds_remove \
  
                and candidate_file.find(pattern) != -1 \
  
                and not probable_current_log_file(candidate_file_fullpath):
  
                #  yield 就是return一个值,并且记住这个返回值的位置,下次迭代就从这个位置后开始
  
                yield candidate_file_fullpath
  

  
'''
  
获取超过大小的日志文件(注意默认不会返回修改时间小于1天的文件)
  
'''
  
def get_clean_log_list_by_size(target_dir,file_size_limit='10g',pattern="log"):
  
    file_size_limit_byte = translate_file_size_to_kb(file_size_limit)
  
    for candidate_file in  os.listdir(target_dir):
  
        candidate_file_fullpath = "%s/%s" %(target_dir,candidate_file)
  
        if os.path.isfile(candidate_file_fullpath):
  
            # stat返回相关文件的系统状态信息
  
            file_stat = os.stat(candidate_file_fullpath)
  
            if candidate_file.find(pattern) != -1 and \
  
                            file_stat.st_size >= file_size_limit_byte:
  
                yield candidate_file_fullpath
  
            # 如果文件在modify_in之内修改过,则不返回
  
            #  if not (modify_in and file_modify_in(candidate_file_fullpath, time_interval=modify_in)) and \
  
            #      not probable_current_log_file(candidate_file_fullpath):
  
            #        yield candidate_file_fullpath
  

  
'''
  
remove文件列表
  
'''
  
def remove_file_list(file_list,pattern='log',roll_back=False):
  
    for file_item in file_list:
  
        if roll_back or probable_current_log_file(file_item,pattern=pattern,modify_in='1d'):
  
            print('roll back file %s' % file_item)
  
            execute_local_shell_cmd("cat /dev/null > {0}".format(file_item))
  
        else:
  
            print('remove file %s' % file_item)
  
            # os.remove 删除指定路径文件。如果指定的路径是一个目录,将抛出OSError
  
            os.remove(file_item)
  

  
'''
  
清理掉超过日期的日志文件
  
'''
  
def remove_files_by_date(target_dir,before_days_remove='7d',pattern='log'):
  
    file_list = get_clean_log_list_by_date(target_dir,before_days_remove,pattern)
  
    remove_file_list(file_list)
  

  
'''
  
清理掉超过大小的日志文件
  
'''
  
def remove_files_by_size(target_dir,file_size_limit='10g',pattern='log'):
  
    file_list = get_clean_log_list_by_size(target_dir,file_size_limit,pattern)
  
    remove_file_list(file_list)
  

  
'''
  
清空当前的日志文件,使用cat /dev/null > {log_file}方式
  
'''
  

  
def clean_curren_log_file(target_dir,file_size_limit='10g',pattern='log'):
  
    for candidate_file in os.listdir(target_dir):
  
        candidate_file_fullpath = '%s/%s' % (target_dir,candidate_file)
  
        if candidate_file.endswith(pattern) and os.path.isfile(candidate_file_fullpath):
  
            file_stat = os.stat(candidate_file_fullpath)
  
            if file_stat.st_size >= translate_file_size_to_kb(file_size_limit):
  
                remove_file_list([candidate_file_fullpath],roll_back=True)
  

  
def clean_data_release_disk(disk_name, target_dir, disk_used_limit='80%', before_days_remove='7d',
  
                            file_size_limit='10g', pattern='log'):
  
    disk_used_limit = disk_used_limit.replace('%', '')
  
    # 第一步执行按时间的日志清理
  
    print('Step one remove files {0} ago.'.format(before_days_remove))
  
    remove_files_by_date(target_dir, before_days_remove=before_days_remove, pattern=pattern)
  

  
    # 如果磁盘空间还是没有充分释放,则执行按大小的日志清理
  
    current_disk_used = int(get_disk_used(disk_name)[1].replace('%', ''))
  
    if current_disk_used > int(disk_used_limit):
  
        print("Disk {0}'s current used {1}% great than input used limit {2}%,"
  
              "so we will remove files bigger than {3}".
  
              format(disk_name, current_disk_used, disk_used_limit, file_size_limit))
  
        remove_files_by_size(target_dir, file_size_limit=file_size_limit, pattern=pattern)
  

  
    # 如果磁盘空间开没有释放,清空当前正在写的log文件,并alert
  
    current_disk_used = int(get_disk_used(disk_name)[1].replace('%', ''))
  
    if current_disk_used > int(disk_used_limit):
  
        print("Disk {0}'s current used {1}% great than input used limit {2}%,"
  
              "so we will roll back current log file".
  
              format(disk_name, current_disk_used, disk_used_limit, file_size_limit))
  
        clean_curren_log_file(target_dir, file_size_limit=file_size_limit, pattern=pattern)
  

  
    # 如果还是没有,alert mail
  
    if int(get_disk_used(disk_name)[1].replace('%', '')) > int(disk_used_limit):
  
        send_alert_mail()
  

  
def usage():
  
    print('clean.py -d <target_disk> -r <target_dirctory -u <diskUsedLimit(default 80%)> '
  
          '-f <fileSizeLimit(default 10gb,gb/mb/kb)> -p <filePattern(default log)> '
  
          '-t <beforeDaysRemove(default 7d,d)> ')
  
if __name__ == "__main__":
  
    target_disk_input = '/data0'
  
    target_dir_input = '/data0/hadoop2/logs'
  
    disk_used_limit_input = '80%'
  
    file_size_limit_input = '10g'
  
    pattern_input = 'log'
  
    before_days_remove_input = '7d'
  
    try:
  
        # getopt 命令解析,有短选项和长选项
  
        # getopt 返回两人个参数:一个对应参数选项和value元组,另一个一般为空
  
        opts,args = getopt.getopt(sys.argv[1:], 'hd:r:u:f:p:t:', ['help' 'disk=', 'directory=',
  
                                                                   'diskUsedLimit=', 'fileSizeLimit=',
  
                                                                   'filePattern=', 'beforeDaysRemove='])
  
    # getopt模块函数异常错误,捕获异常并打印错误
  
    except getopt.GetoptError as err:
  
        print err
  
        usage()
  
        sys.exit(2)
  

  
    if len(opts) < 6:
  
        usage()
  
        sys.exit(2)
  

  
    for opt,arg in opts:
  
        if opt == '-h':
  
            usage()
  
            sys.exit()
  
        elif opt in ("-d","--disk"):
  
            target_disk_input = arg.replace('/','')
  
        elif opt in ("-r","--directory"):
  
            target_dir_input = arg
  
        elif opt in ("-u","--diskUsedLimit"):
  
            disk_used_limit_input = arg
  
        elif opt in ("-f","--fileSizeLimit"):
  
            file_size_limit_input = arg
  
            translate_file_size_to_kb(file_size_limit_input)
  
        elif opt in ("-p","filePattern"):
  
            pattern_input = arg
  
        elif opt in ("-t","--beforeDaysRemove"):
  
            before_days_remove_input = arg
  
            translate_time_interval_to_second(before_days_remove_input)
  

  
    print ("{0} Start clean job.target_disk:{1},target_directory:{2},disk_used_limit:{3},"
  
           "file_size_limit:{4},pattern:{5},before_days_remove:{6}".format(time.ctime(time.time()),
  
                                                                           target_disk_input, target_dir_input,
  
                                                                           disk_used_limit_input, file_size_limit_input,
  
                                                                           pattern_input, before_days_remove_input))
  
    clean_data_release_disk(target_disk_input, target_dir_input,
  
                            disk_used_limit=disk_used_limit_input, file_size_limit=file_size_limit_input,
  
                            pattern=pattern_input, before_days_remove=before_days_remove_input)

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-548577-1-1.html 上篇帖子: Easticsearch 数据迁移至influxdb【python】 下篇帖子: python实现简单购物筛选
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表