Python日志

Python记录系列.

三方库

loguru

pip install loguru
from loguru import logger

logger.add(sys.stderr, format="{time} {level} {message}", filter="my_module", level="INFO")
logger.add("file_{time}.log")
logger.add("file.log", format="{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}")

logging

  • logger:logging.getLogger(name) , 以相同的名称多次调用getLogger()将永远返回相同Logger对象的引用
  • config
    • logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG,)
    • logging.config.dictConfig,
    • logging.config.fileConfig
  • Handlers: logging.handlers.RotatingFileHandler(LOG_FILENAME,maxBytes=1024,backupCount=5,)

基础用法

import logging
# 设置文件日志
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
                    # datefmt='%m-%d %H:%M',
                    filename='myapp.log',
                    filemode='w')
logging.getLogger().setLevel(logging.DEBUG)
# define a Handler which writes INFO messages or higher to the sys.stderr
console = logging.StreamHandler()
console.setLevel(logging.INFO)
# add the handler to the root logger
logging.getLogger().addHandler(console)
logger = logging.getLogger('myapp')

使用json配置文件

import os
import json
import logging.config


def setup_logging(
        default_path='logging.json',
        default_level=logging.INFO,
        env_key='LOG_CFG'
):
    """Setup logging configuration

    """
    path = default_path
    value = os.getenv(env_key, None)
    if value:
        path = value
    if os.path.exists(path):
        with open(path, 'rt') as f:
            config = json.load(f)
        logging.config.dictConfig(config)
    else:
        logging.basicConfig(level=default_level)


if __name__ == '__main__':
    setup_logging()
    logger = logging.getLogger()
    logger.info("info log test")
    logger.error("error log test")

配置文件例子:

{
    "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.RotatingFileHandler",
            "level": "INFO",
            "formatter": "simple",
            "filename": "info.log",
            "maxBytes": 10485760,
            "backupCount": 20,
            "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": ["console"],
            "propagate": "no"
        }
    },

    "root": {
        "level": "INFO",
        "handlers": ["console", "info_file_handler", "error_file_handler"]
    }
}

日志Tips

获取当前ROOT日志级别

logging.getLogger().isEnabledFor(logging.DEBUG)

# 10 DEBUG 20 INFO
logging.getLogger().getEffectiveLevel()

禁用Requests日志

NullHandler用来禁用第三方库的日志.

两种方式:

import logging

requests_log = logging.getLogger("requests")
requests_log.addHandler(logging.NullHandler())
requests_log.propagate = False
import requests
import logging
for key in logging.Logger.manager.loggerDict:
    print(key)# requests.packages.urllib3.connectionpool
logging.getLogger('requests').setLevel(logging.CRITICAL)

# 
requests.packages.urllib3.disable_warnings()

urllib3

import urllib3
# 禁用自签证书
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

logging.getLogger('urllib3.connectionpool').setLevel(logging.CRITICAL)

异常处理

  • raise: 没有参数的raise会再次抛出最后的异常
  • try-except-else: 只在没有异常时执行else(在final之前)
logger.exception("something uncaught happened")
# 等价于
logger.error("something uncaught happened", exc_info=True)

规则

  • 使用最适合当前情况的异常
  • 只在你能对异常做处理时捕获
  • 不要在异常块直接pass
  • 需要时创建自定义类型错误

常见内置异常

用户自定义异常的基类: Exception

  • SystemExit
  • KeyboardInterrupt
  • NotImplementedError
  • EnvironmentError
  • IOError
  • ImportError

特殊使用

忽略异常:

# Ignore Exceptions intended
from contextlib import suppress
with suppress(FileNotFoundError):
    shutil.rmtree(path)