记录下常用的一些东西,每次用总是查文档有点小麻烦。 py2.7
日志应该是生产应用的重要生命线,谁都不应该掉以轻心
有益原则 级别分离日志系统通常有下面几种级别,看情况是使用
FATAL - 导致程序退出的严重系统级错误,不可恢复,当错误发生时,系统管理员需要立即介入,谨慎使用。 ERROR - 运行时异常以及预期之外的错误,也需要立即处理,但紧急程度低于FATAL,当错误发生时,影响了程序的正确执行。需要注意的是这两种级别属于服务自己的错误,需要管理员介入,用户输入出错不属于此分类。 WARN - 预期之外的运行时状况,表示系统可能出现问题。对于那些目前还不是错误,然而不及时处理也会变成错误的情况,也可以记为WARN,如磁盘过低。 INFO - 有意义的事件信息,记录程序正常的运行状态,比如收到请求,成功执行。通过查看INFO,可以快速定位WARN,ERROR, FATAL。INFO不宜过多,通常情况下不超过TRACE的10%。 DEBUG - 与程序运行时的流程相关的详细信息以及当前变量状态。 TRACE - 更详细的跟踪信息。DEBUG和TRACE这两种规范由项目组自己定义,通过该种日志,可以查看某一个操作每一步的执行过程,可以准确定位是何种操作,何种参数,何种顺序导致了某种错误的发生 单独目录日志最好放到单独的日志目录,例如 /var/logs/ 下,按照应用分成不同的目录,或者是文件。日志不要放在应用目录下,那样不利于自动化部署和应用升级,备份等。
日志分类诊断日志,统计日志,审计日志等等,不同用途等日志存储到不同的文件中,方面后面的查询,分析。
日志格式不管是web日志,还是应用日志,最好有一个比较统一的格式(例如时间格式),方面日志的查询,入库,和分析。还有一些应用统一使用json的日志格式,也挺好的。
不好的做法 日志中含有用户敏感信息 线上程序中使用 print 生产环境使用 debug 级别日志 :cry: 日志切分日志可以按照每天,每周或者是文件的大小,切分之后压缩。一方面容易按时间回溯,另一方面可以减少磁盘空间,对于很久之前的日志,可以传输到远程服务器,或者是删除。
python 日志 好习惯 root级别的设置: 日志格式, 有利于标准化 class 中设置logger self.logger = logging.getLogger(type(self).__name__) 模块,文件中设置 logger logger = logging.getLogger(__name__) 使用JSON YAML等格式来配置logging,感觉比使用代码或者 ini格式看起来更方面 错误日志是比较特殊的日志,因为它需要更多的信息,例如错误产生的上下文,还有错误堆栈等信息。可以通过 python logging context pypi 关键词google一些信息,或者自己设计一个 logging handler 来实现。 实际问题 简单的小应用中,单个日志文件,同时还要打印控制台import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', datefmt='%m-%d %H:%M', filename='/temp/myapp.log', filemode='w') console = logging.StreamHandler() console.setLevel(logging.INFO) formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') console.setFormatter(formatter) # add the handler to the root logger logging.getLogger('').addHandler(console)
记录 Exception 的trace 信息(很有用哦)try: open('/path/to/does/not/exist', 'rb') except (SystemExit, KeyboardInterrupt): raise except Exception, e: logger.error('Failed to open file', exc_info=True)
ini 格式例子这里用了第三方的一个handler,ConcurrentRotatingFileHandler, 实现多进程安全
[loggers] keys=root [handlers] keys=stream, rotatingFile, errorFile [formatters] keys=form01 [logger_root] level=DEBUG handlers=stream, rotatingFile, errorFile [handler_stream] class=StreamHandler level=NOTSET formatter=form01 args=(sys.stdout,) [handler_errorFile] class=FileHandler level=ERROR formatter=form01 args=('./logs/portal.log', 'a') [handler_rotatingFile] level=INFO formatter=form01 class=handlers.ConcurrentRotatingFileHandler args=('./logs/portal.log','a',50240000, 10) [formatter_form01] format=%(asctime)s %(name)s %(levelname)s %(message)s datefmt= class=logging.Formatter引用
import logging import logging.config import cloghandler logging.config.fileConfig(join(BASE_DIR, "conf/log.conf")) logger = logging.getLogger(__name__)
默认会使用 root 这个logger,如果名称匹配就使用对应的logger。 一个logger也可以指定多个 handdler, 用来处理不同的日志级别等。
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"] } }获取配置
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的工具python-json-logger , logmatic json-logger增加的一些封装
import logging.handlers from pythonjsonlogger import jsonlogger import datetime class JsonFormatter(jsonlogger.JsonFormatter, object): def __init__(self, fmt="%(asctime) %(name) %(processName) %(filename) %(funcName) %(levelname) %(lineno) %(module) %(threadName) %(message)", datefmt="%Y-%m-%dT%H:%M:%SZ%z", style='%', extra={}, *args, **kwargs): self._extra = extra jsonlogger.JsonFormatter.__init__(self, fmt=fmt, datefmt=datefmt, *args, **kwargs) def process_log_record(self, log_record): # Enforce the presence of a timestamp if "asctime" in log_record: log_record["timestamp"] = log_record["asctime"] else: log_record["timestamp"] = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ%z") if self._extra is not None: for key, value in self._extra.items(): log_record[key] = value return super(JsonFormatter, self).process_log_record(log_record) 参考 Logging Cookbook 文档一定是要看的 日志最佳实践 good-logging-practice-in-python Sentry 日志收集,分析系统,基于Python