虽然日志很重要,但并非全部的开发者都知道如何正确的记录日志。以前在开发过程当中往代码里插入print语句,而后在开发完成以后删掉他们……可是针对以简单的脚本时,这种方式颇有效;可是对于复杂的系统,这么作并非一个明智的选择。首先你不可能期望日志里输出重要的信息,你可能在日志里看到一大堆垃圾信息,却找不到任何有用的内融。print语句不能很好的作到控制,除非你修改源代码。若是忘记删除没有用的print,全部的消息都会给输出到stdout,终究不是记录日志的好习惯。
使用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')
输出结果以下python
INFO:main:Start reading database INFO:main:Updating records ... INFO:main:Finish updating records [Finished in 0.3s]json
与print的区别:服务器
日志级别分为: debug、info、warning、error、critical网络
级别 | 数值 | 什么时候使用 |
---|---|---|
DEBUG | 详细信息,典型的调试问题时会感兴趣 | |
INFO | 证实事情按预期工做 | |
WARNING | 报名发生了一些意外,或者不就得未来会发生的问题,可是软件还能正常工做 | |
ERROR | 因为严重的问题,异常抛出,IO操做失败,软件已经不能执行一些功能了 | |
CRITICAL | 严重错误,代表软件已经不能正常运行了,内存不足,磁盘满了 |
__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) 是同样的debug
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 都一视同仁。
你能够在 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: 1code
disable_existing_loggers: Falseorm
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套接字来发送日志信息。