• python Log使用


    程序内配置

    import logging
    logger = logging.getLogger('mylogger')
    logger.setLevel(logging.DEBUG)
    
    # print(os.path.join(results.output_dir, 'debug.log'))
    fh = logging.FileHandler(os.path.join(results.output_dir, 'debug.log'))
    fh.setLevel(logging.INFO)
    
    sh = logging.StreamHandler()
    sh.setLevel(logging.DEBUG)
    
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    fh.setFormatter(formatter)
    sh.setFormatter(formatter)
    
    logger.addHandler(fh)
    logger.addHandler(sh)
    
    logger.debug(results.output_dir + 'debug.log')
    

    config 配置

    # 定义logger模块,root是父类,必需存在的,其它的是自定义。
    # logging.getLogger(NAME)便相当于向logging模块注册了一种日志打印
    # name 中用 . 表示 log 的继承关系
    [loggers]
    keys=root,infoLogger,errorLogger
    
    # 定义handler
    [handlers]
    keys=infoHandler,errorHandler
    
    # 定义格式化输出
    [formatters]
    keys=infoFmt,errorFmt
    
    #--------------------------------------------------
    # 实现上面定义的logger模块,必需是[logger_xxxx]这样的形式
    #--------------------------------------------------
    # [logger_xxxx] logger_模块名称
    # level     级别,级别有DEBUG、INFO、WARNING、ERROR、CRITICAL
    # handlers  处理类,可以有多个,用逗号分开
    # qualname  logger名称,应用程序通过 logging.getLogger获取。对于不能获取的名称,则记录到root模块。
    # propagate 是否继承父类的log信息,0:否 1:是
    [logger_root]
    level=INFO
    handlers=errorHandler
    
    [logger_errorLogger]
    level=ERROR
    handlers=errorHandler
    propagate=0
    qualname=errorLogger
    
    [logger_infoLogger]
    level=INFO
    handlers=infoHandler
    propagate=0
    qualname=infoLogger
    
    #--------------------------------------------------
    # handler
    #--------------------------------------------------
    # [handler_xxxx]
    # class handler类名
    # level 日志级别
    # formatter,上面定义的formatter
    # args handler初始化函数参数
    
    [handler_infoHandler]
    class=StreamHandler
    level=INFO
    formatter=infoFmt
    args=(sys.stdout,)
    
    [handler_errorHandler]
    class=logging.handlers.TimedRotatingFileHandler
    level=ERROR
    formatter=errorFmt
    # When computing the next rollover time for the first time (when the handler is created),
    # the last modification time of an existing log file, or else the current time,
    # is used to compute when the next rotation will occur.
    # 这个功能太鸡肋了,是从handler被创建的时间算起,不能按自然时间 rotation 切分,除非程序一直运行,否则这个功能会有问题
    # 临时解决方案参考下面的链接:Python 多进程日志记录
    # http://blogread.cn/it/article/4175?f=wb2
    args=('error.log', 'M', 1, 5)
    
    #--------------------------------------------------
    # 日志格式
    #--------------------------------------------------
    # %(asctime)s       年-月-日 时-分-秒,毫秒 2013-04-26 20:10:43,745
    # %(filename)s      文件名,不含目录
    # %(pathname)s      目录名,完整路径
    # %(funcName)s      函数名
    # %(levelname)s     级别名
    # %(lineno)d        行号
    # %(module)s        模块名
    # %(message)s       消息体
    # %(name)s          日志模块名
    # %(process)d       进程id
    # %(processName)s   进程名
    # %(thread)d        线程id
    # %(threadName)s    线程名
    
    [formatter_infoFmt]
    format=%(asctime)s %(levelname)s %(message)s
    datefmt=%Y-%m-%d %H:%M:%S
    class=logging.Formatter
    
    [formatter_errorFmt]
    format=%(asctime)s %(levelname)s %(message)s
    datefmt=%Y-%m-%d %H:%M:%S
    class=logging.Formatter
    

    使用

    • logconfig.ini
    [loggers]
    keys=root,infoLogger,errorLogger
    
    
    [handlers]
    keys=infoHandler,errorHandler
    
    [formatters]
    keys=infoFmt,errorFmt
    
    [logger_root]
    level=INFO
    handlers=errorHandler,infoHandler
    
    [logger_errorLogger]
    level=ERROR
    handlers=errorHandler
    propagate=0
    qualname=errorLogger
    
    [logger_infoLogger]
    level=INFO
    handlers=infoHandler
    propagate=0
    qualname=infoLogger
    
    
    [handler_infoHandler]
    class=StreamHandler
    level=INFO
    formatter=infoFmt
    args=(sys.stdout,)
    
    [handler_errorHandler]
    class=logging.handlers.TimedRotatingFileHandler
    level=ERROR
    formatter=errorFmt
    args=('error.log', 'M', 1, 5)
    
    
    [formatter_infoFmt]
    format=%(asctime)s %(levelname)s %(message)s
    datefmt=%Y-%m-%d %H:%M:%S
    class=logging.Formatter
    
    [formatter_errorFmt]
    format=%(asctime)s %(levelname)s %(message)s
    datefmt=%Y-%m-%d %H:%M:%S
    class=logging.Formatter
    
    • test.py
    import logging
    import logging.config
    
    logging.config.fileConfig("logconfig.ini")
    log = logging.getLogger("test")
    
    log.info("info")
    log.error("error")
    log.warning("warning")
    

    程序内使用字典

    import logging
    import logging.config
    
    DEBUG = True
    
    
    # 给过滤器使用的判断
    class RequireDebugTrue(logging.Filter):
        # 实现filter方法
        def filter(self, record):
            return DEBUG
    
    
    log_config_dict = {
        "version": 1,
        'disable_existing_loggers': False,  # 是否禁用现有的记录器
    
        # 日志管理器集合
        'loggers': {
            # 管理器
            'default': {
                'handlers': ['console', 'log'],
                'level': 'INFO',
                'propagate': True,  # 是否传递给父记录器
            },
        },
    
        # 处理器集合
        'handlers': {
            # 输出到控制台
            'console': {
                'level': 'INFO',  # 输出信息的最低级别
                'class': 'logging.StreamHandler',
                'formatter': 'standard',  # 使用standard格式
                'filters': ['require_debug_true', ],  # 仅当 DEBUG = True 该处理器才生效
            },
            # 输出到文件
            'log': {
                'level': 'INFO',
                'class': 'logging.handlers.RotatingFileHandler',
                'formatter': 'standard',
                'filename': "test.log",  # 输出位置
                'maxBytes': 1024 * 1024 * 5,  # 文件大小 5M
                'backupCount': 5,  # 备份份数
                'encoding': 'utf8',  # 文件编码
            },
        },
        # 过滤器
        'filters': {
            'require_debug_true': {
                '()': RequireDebugTrue,
            }
        },
    
        # 日志格式集合
        'formatters': {
            # 标准输出格式
            'standard': {
                # [具体时间][线程名:线程ID][日志名字:日志级别名称(日志级别ID)] [输出的模块:输出的函数]:日志内容
                'format': '[%(asctime)s][%(threadName)s:%(thread)d][%(name)s:%(levelname)s(%(lineno)d)]--[%(module)s:%(funcName)s]:%(message)s'
            }
        }
    }
    
    logging.config.dictConfig(log_config_dict)
    logger_info = logging.getLogger("default")
    

    YMAL 配置

    • logconfig.yaml
    version: 1
    disable_existing_loggers: False
    formatters:
      simple:
      format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    handlers:
      console:
        class: logging.StreamHandler
        level: DEBUG
        formatter: simple
        stream: ext://sys.stdout
      info_file_handler:
        class: logging.handlers.TimedRotatingFileHandler
        level: INFO
        formatter: simple
        filename: info.log
        when: M
        interval: 1
        backupCount: 10
        encoding: utf8
      error_file_handler:
        class: logging.handlers.RotatingFileHandler
        level: ERROR
        formatter: simple
        filename: errors.log
        maxBytes: 10485760
        backupCount: 20
        encoding: utf8
    loggers:
      my_module:
        level: ERROR
        handlers: [info_file_handler]
        propagate: no
    root:
      level: INFO
      handlers: [console,info_file_handler,error_file_handler]
    
    • test.py
    import logging
    import logging.config
    import yaml
    
    with open("logconfig.yaml", "r", encoding="utf-8") as f:
        logging.config.dictConfig(yaml.safe_load(f.read()))
    
    log = logging.getLogger("test")
    
    log.info("info")
    log.warning("warning")
    log.error("error")
    

    Handler

    作为 Handler 基类的补充,提供了很多有用的子类:

    1. StreamHandler 实例发送消息到流(类似文件对象)。
    2. FileHandler 实例将消息发送到硬盘文件。
    3. BaseRotatingHandler 是轮换日志文件的处理程序的基类。它并不应该直接实例化。而应该使用 RotatingFileHandlerTimedRotatingFileHandler 代替它。
    4. RotatingFileHandler 实例将消息发送到硬盘文件,支持最大日志文件大小和日志文件轮换。
    5. TimedRotatingFileHandler 实例将消息发送到硬盘文件,以特定的时间间隔轮换日志文件。
    6. SocketHandler 实例将消息发送到 TCP/IP 套接字。从 3.4 开始,也支持 Unix 域套接字。
    7. DatagramHandler 实例将消息发送到 UDP 套接字。从 3.4 开始,也支持 Unix 域套接字。
    8. SMTPHandler 实例将消息发送到指定的电子邮件地址。
    9. SysLogHandler 实例将消息发送到 Unix syslog 守护程序,可能在远程计算机上。
    10. NTEventLogHandler 实例将消息发送到 Windows NT/2000/XP 事件日志。
    11. MemoryHandler 实例将消息发送到内存中的缓冲区,只要满足特定条件,缓冲区就会刷新。
    12. HTTPHandler 实例使用 GETPOST 方法将消息发送到 HTTP 服务器。
    13. WatchedFileHandler 实例会监视他们要写入日志的文件。如果文件发生更改,则会关闭该文件并使用文件名重新打开。此处理程序仅在类 Unix 系统上有用; Windows 不支持依赖的基础机制。
    14. QueueHandler 实例将消息发送到队列,例如在 queuemultiprocessing 模块中实现的队列。
    15. NullHandler 实例对错误消息不执行任何操作。它们由想要使用日志记录的库开发人员使用,但是想要避免如果库用户没有配置日志记录,则显示 "无法找到记录器XXX的消息处理器" 消息的情况。有关更多信息,请参阅 配置库的日志记录

    问题

    多进程写文件

    在使用TimeRotaingFileHandlerdoRollover方法时,每个进程在过了rotate时间点之后写新日志之前,都会执行doRollover方法:

    1. 检查前一刻的文件是否存在,存在的话删除
    2. 将正在写的文件更名为前一刻的时间
    3. 往新的日志写入

    因为多进程执行的不确定性,会出现上个时刻的日志被删除,当前时刻的日志也会有一部分被删除。

    日志在写入文件时,因为磁盘的刷新策略并不会实时的将缓冲区的数据刷新到磁盘中,此时如果多个进程同时写入同一个文件的话,就会出现日志混乱的问题,linux允许多个进程打开一个文件,但windows不行。

    使用进程号和时间进行区分

    将进程id作为日志的一部分,则每个进程的日志操作就不会影响了,但是当重启服务器时则会取得新的进程id,而且日志中出现进程id,会感觉有点乱,可以用crontab执行脚本,来合并日志。

    使用单独的程序来接收日志

    使用SocketHandler,将日志通过网络发送出去。

    服务器端时间

  • 相关阅读:
    无重叠区间
    ARC109E 1D Reversi Builder
    IOI2021集训队作业129CF Longest Rivers
    IOI2021集训队作业281CA Balanced Diet
    IOI2021集训队作业227CB Branch Assignment
    IOI2021集训队作业277BK Tours
    IOI2021集训队作业125AA Baggage
    6894. 【2020.11.25提高组模拟】小 S 埋地雷 (loj6611. 摧毁时间线)
    ARC108 题解&总结
    HTML教程
  • 原文地址:https://www.cnblogs.com/iFanLiwei/p/12819925.html
Copyright © 2020-2023  润新知