python Log使用

Posted 呓语

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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,将日志通过网络发送出去。

服务器端时间

以上是关于python Log使用的主要内容,如果未能解决你的问题,请参考以下文章

webstorm代码片段的创建

我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情

vs code 自定义代码片段

使用 Python 代码片段编写 LaTeX 文档

常用python日期日志获取内容循环的代码片段

前端开发常用js代码片段