zoukankan      html  css  js  c++  java
  • python logging配置时间或大小轮转

    python中的很多模块是非常牛X的,之前提到过logging模块(其功能类似于java下的Log4j ),由于最近一个涉及网络排障的脚本需要日志输出,这里就使用了python的logging模块去实现。日志全部写到一个文件中时,随着时间的推移文件会越来越来,这里可以利用TimedRotatingFileHandler方法或RotatingFileHandler方法来进行处理。

    在日志输出不涉及轮转的时候,可以通过logging.basicConfig 方法自定义输出的日志格式,这部分可以参考我之前的博文记录---python日志模块logging 。在涉及到按日期轮转时,再使用之前的logging.basicConfig格式处理,无法正常输出,这里在咨询之前的python大牛同事后,最终写出的代码内容如下:

    #!/usr/bin/env python
    # coding=utf-8
    # site: www.361way.com   
    # mail: itybku@139.com
    # desc: Rotating logfile by times or size
    
    
    import re
    import subprocess
    import logging
    import socket,time
    from logging.handlers import TimedRotatingFileHandler
    
    LOG_FILE = "/var/log/ping/ping.log"
    
    #logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',datefmt='%Y-%m-%d %I:%M:%S',filemode='w')   #for term print
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    fh = TimedRotatingFileHandler(LOG_FILE,when='M',interval=1,backupCount=30)
    datefmt = '%Y-%m-%d %H:%M:%S'
    format_str = '%(asctime)s %(levelname)s %(message)s '
    #formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
    formatter = logging.Formatter(format_str, datefmt)
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    #logging.info(msg)
    #hdlr.flush()
    
    #----------------------------------------------------------------------
    def pinghost(host):
        ping = subprocess.Popen(["ping", "-c", "1",host],stdout = subprocess.PIPE,stderr = subprocess.PIPE)
        out, error = ping.communicate()
        if "icmp_seq" in  out:
            icmp_line = re.findall(r'd+sbytes(.*?)ms',out)
            logging.info('ping ' + host + str(icmp_line))
        else:
            logging.info('ping ' + host + ' fail')
            
            
    def tcping(server, port):
        ''' Check if a server accepts connections on a specific TCP port '''
        try:
            start = time.time()
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect((server, port))
            s.close()
            #print server + ':' + str(port) + '/tcp - ' +  str(port) + ' port is open' + ' - time=' + str(round((time.time()-start)*10000)/10) + 'ms'
            msg = server + ':' + str(port) + '/tcp - ' +  str(port) + ' port is open' + ' - time=' + str((time.time()-start)*1000) + 'ms'
            logging.info(msg)
        except socket.error:
            msg = server + ':' + str(port) + ' port not open'
            logging.info(msg)
    
    while 1:
        pinghost('passport.migu.cn')
        pinghost('112.17.9.72')
        tcping('passport.migu.cn',8443)
        tcping('112.17.9.72',8443)
        #time.sleep(0.5)

    代码已上传到我的github上

    1、轮询函数语法

    TimedRotatingFileHandler的构造函数定义如下:

    1. TimedRotatingFileHandler(filename [,when [,interval [,backupCount]]])

    filename 是输出日志文件名的前缀

    when 是一个字符串的定义如下:

    "S": Seconds
    "M": Minutes
    "H": Hours
    "D": Days
    "W": Week day (0=Monday)
    "midnight": Roll over at midnight

    interval 是指等待多少个单位when的时间后,Logger会自动重建文件,当然,这个文件的创建取决于filename+suffix,若这个文件跟之前的文件有重名,则会自动覆盖掉以前的文件,所以有些情况suffix要定义的不能因为when而重复。

    backupCount 是保留日志个数。默认的0是不会自动删除掉日志。若设10,则在文件的创建过程中库会判断是否有超过这个10,若超过,则会从最先创建的开始删除。

    2、轮询使用示例

    RotatingFileHandler(按照文件大小分割)、TimedRotatingFileHandler(按照时间间隔分割)使用的示例如下

    1. hdlr = logging.handlers.RotatingFileHandler(LOG_FILE,maxBytes=1024*1024,backupCount=40)或
    2. hdlr = logging.handlers.TimedRotatingFileHandler(LOG_FILE,when='M',interval=1,backupCount=40)

    其中maxBytes指定每个日志文件的大小,如果文件超过1024比特就分割该日志文件,最大的备份文件个数是40个。到LOG_FILE所在目录下查看,发现除了debug.log文件外,还多了debug.log.1,debug.log.2等文件。

    3、logging模块运行流程

    logging和handler模块运行的流程如下:

    import logging
    # 创建一个logger
    logger = logging.getLogger('mylogger')
    logger.setLevel(logging.DEBUG)
    # 创建一个handler,用于写入日志文件
    fh = logging.FileHandler('test.log')
    fh.setLevel(logging.DEBUG)
    # 再创建一个handler,用于输出到控制台
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    # 定义handler的输出格式
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    fh.setFormatter(formatter)
    ch.setFormatter(formatter)
    # 给logger添加handler
    logger.addHandler(fh)
    logger.addHandler(ch)
    # 记录一条日志
    logger.info('foorbar')

    4、logging Level

    logger.setLevel方法可以设置日志显示的级别,级别由高至低的顺序是:NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL ,如果把looger的级别设置为INFO, 那么小于INFO级别的日志都不输出, 大于等于INFO级别的日志都输出 。

    5、logging的父子关系

    如下图所示,logger日志之间是存在父子关系的。 root logger就是处于最顶层的logger 。

    如果不创建logger实例, 直接调用logging.debug()、logging.info()logging.warning()、logging.error()、logging.critical()这些函数,那么使用的logger就是 root logger, 它可以自动创建,也是单实例的。通过logging.getLogger()或者logging.getLogger("")得到root logger实例。root logger默认的level是logging.WARNING 。

    logger的name的命名方式可以表示logger之间的父子关系. 比如:

    1. parent_logger = logging.getLogger('foo')
    2. child_logger = logging.getLogger('foo.bar')

    effective level:logger有一个概念,叫effective level。 如果一个logger没有显示地设置level,那么它就用父亲的level。如果父亲也没有显示地设置level, 就用父亲的父亲的level,以此推最后到达root logger,一定设置过level。默认为logging.WARNING child loggers得到消息后,既把消息分发给它的handler处理,也会传递给所有祖先logger处理,

    父子测试代码如下:

    import logging
    # 设置root logger
    r = logging.getLogger()
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    ch.setFormatter(formatter)
    r.addHandler(ch)
    # 创建一个logger作为父亲
    p = logging.getLogger('foo')
    p.setLevel(logging.DEBUG)
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(message)s')
    ch.setFormatter(formatter)
    p.addHandler(ch)
    # 创建一个子logger
    c = logging.getLogger('foo.bar')
    c.debug('foo')

     

    输出如下:

    1. 2016-03-10 21:04:29,893 - foo
    2. 2016-03-10 21:04:29,893 - DEBUG - foo

    子logger没有任何handler,所以对消息不做处理。但是它把消息转发给了它的父logger以及root logger。最后输出两条日志。

    6、其他

    根据在网上看到信息,该模块在多进程环境下使用可能会出现Windows Error 32 报错的情况,这需要重写下 doRollover 函数。

    另外涉及到logging模块的配置部分,我们还可以通过指定一个配置文件---使用logging.config.fileConfig('logging.conf')方法,读取该自定义配置的方法生效,具体可以参看官方相关文档

  • 相关阅读:
    RabbitMQ知识梳理
    feign.FeignException: status 400 reading
    rabbitmq监控之消息确认ack
    Docker 安装redis mysql rabbitmq
    linux搭建GitLab
    杀死服务进程并重新启动,同时监听日志打印
    MySQL数据库连接报错
    idea: unable to import maven project
    线程池ThreadPool实战
    【2-SAT】URAL
  • 原文地址:https://www.cnblogs.com/linkenpark/p/10289118.html
Copyright © 2011-2022 走看看