Python 日志学习

旧街凉风 提交于 2019-12-26 19:14:46

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

print 不是一个好主意

虽然日志很重要,但并不是所有的开发者都知道如何正确的记录日志。之前在开发过程中往代码里插入print语句,然后在开发完成之后删掉他们……但是针对以简单的脚本时,这种方式很有效;但是对于复杂的系统,这么做并不是一个明智的选择。首先你不可能指望日志里输出重要的信息,你可能在日志里看到一大堆垃圾信息,却找不到任何有用的内融。print语句不能很好的做到控制,除非你修改源代码。如果忘记删除没有用的print,所有的消息都会给输出到stdout,终究不是记录日志的好习惯。

使用 Python longging标准库

使用Python内置的标准模块,是记录日志的正确姿势。logging是一个标准模块,设计优良,易于使用,同时易于扩展。
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

logger.info('Start reading database')
# read database here

records = {'john': 55, 'tom': 66}
logger.debug('Records: %s', records)
logger.info('Updating records ...')
# update records here

logger.info('Finish updating records')

输出结果如下

INFO:main:Start reading database INFO:main:Updating records ... INFO:main:Finish updating records [Finished in 0.3s]

与print的区别:

  • 你可以控制消息级别,把没那么重要的内容过滤掉
  • 你可以晚一点在决定要输出到哪里,以及输出的方式

日志级别分为: debug、info、warning、error、critical

日志级别说明

级别 数值 何时使用
DEBUG 详细信息,典型的调试问题时会感兴趣
INFO 证明事情按预期工作
WARNING 报名发生了一些意外,或者不就得将来会发生的问题,但是软件还能正常工作
ERROR 由于严重的问题,异常抛出,IO操作失败,软件已经不能执行一些功能了
CRITICAL 严重错误,表明软件已经不能正常运行了,内存不足,磁盘满了

建议使用__name__作为logger名称

__name__变量在Python里面是当前模块的名字。例如,你在模块 ** foo.bar.my_module ** 里调用 logging.getLogger(__name__) ,等价于 logging.getLogger('foo.bar.my_module') 。当你需要设置 logger 的时候,设置为 foo ,那么 foo 包里的所有模块都会共享同一台设置,可以志短的看到这是那个木块记录的日志信息。

捕获异常 记录跟踪信息

出错的时候记录日志是好的,但是没有  traceback 也没什么用。在你捕获异常时,在日志中附加 tracebask 信息,例如:
try:
    open('/path/to/does/not/exist', 'rb')
except (SystemExit, KeyboardInterrupt):
    raise
except Exception, e:
    logger.error('Failed to open file', exc_info=True)

调用logger来记录日志是,指定 exc_info=True参数,traceback信息将会被保存到日志。你也可以调用 logger.exception(msg, *args),这跟 logger.error(msg, *args, exc_info=True) 是一样的


ERROR:main:Failed to open file Traceback (most recent call last): File "example.py", line 6, in <module> open('/path/to/does/not/exist', 'rb') IOError: [Errno 2] No such file or directory: '/path/to/does/not/exist'

##### 除非 disable_existing_logger == False,不然不要在模块级别获取 logger

在网络上可以找到很多例子(这篇文章中我也特意举了这么一个例子),它们在模块级别获取 logger。看起来无害,但实际上是有隐患的——Python 的 logging 模块,在从文件中读取设置之前,对所有 logger 都一视同仁。

使用 JSON 或 YAML 作为日志配置

你可以在 Python 代码中配置你的日志系统,但似乎不是那么灵活。更好的方式是用配置文件。在 Python 2.7 之后,你可以从一个字典中加载日志配置了。这意味着你可以从 JSON 或是 YAML 文件中读取配置。虽然旧的 .ini 格式配置也还是被支持,但那很难写,阅读也不方便。这里举了一个使用 JSON 或 YAML 配置的例子:

logging.json


{
    "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"]
    }
}```
logging.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.RotatingFileHandler

    level: INFO            

    formatter: simple

    filename: info.log

    maxBytes: 10485760 # 10MB

    backupCount: 20

    encoding: utf8

error_file_handler:

    class: logging.handlers.RotatingFileHandler

    level: ERROR            

    formatter: simple

    filename: errors.log

    maxBytes: 10485760 # 10MB

    backupCount: 20

    encoding: utf8

loggers:

my_module:

    level: ERROR

    handlers: [console]

    propagate: no

root:

level: INFO

handlers: [console, info_file_handler, error_file_handler]

...

####下面的代码片段展示了如何从 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)
#####JSON 有个优势就是 json 是个标准库,你不需要安装就可以使用。但是个人来说我更喜欢 YAML,读写都方便。使用如下代码片段可以加载 YAML 配置:
- - - 

import os import logging.config

import yaml

def setup_logging( default_path='logging.yaml', 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 = yaml.load(f.read())
    logging.config.dictConfig(config)
else:
    logging.basicConfig(level=default_level)
#####现在,要设置日志记录的话,在程序启动的时候调用 setup_logging 就行了。它默认读取 logging.json 或是 logging.yaml。你可以设置 LOG_CFG 环境变量来指定从某个路径中加载日志配置。例如:

>LOG_CFG=my_logging.json python my_server.py
或者,如果你偏好 YAML:

>LOG_CFG=my_logging.yaml python my_server.py

### 轮换日志处理器

    如果你用 FileHandler 来写日志的话,日志文件大小会随时间不断增长。总有一天磁盘会被它占满的。为了避免这种情况,你应该在生产环境中使用 RotatingFileHandler 代替 FileHandler。

### 在有多台服务器的情况下,设置中心日志服务器

当你有多台服务器和多个日志文件的时候,你可以设置一台中心日志服务器来收集所有重要的信息(大部分情况下是警告、错误等)。这样你监视起来会比较简单,出错的时候也更容易注意到。

### 最后    
    Pythonde logging 弄快设计的很棒,而且是标准库,很容易扩展,你可以编写自己的处理器和过滤器。还有一些第三方的处理器,pyzmq提供的ZeroMQ处理器,可以让你通过一个zmq套接字来发送日志信息。
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!