【问题标题】:Python logger stops logging after uncaught exceptionPython 记录器在未捕获的异常后停止记录
【发布时间】:2015-03-01 08:30:13
【问题描述】:

我们的烧瓶应用程序使用的本机 python 记录器似乎在发生异常后停止写入日志。每次停止之前记录的最后一个条目是描述异常的消息。通常下一条消息是由 after_request 中的代码编写的,但对于记录器停止的情况,after_request 消息永远不会被写出。

知道是什么原因造成的吗?

注意:我最初在 Serverfault (https://serverfault.com/questions/655683/python-logger-stops-logging) 上发布了这个问题,认为这是一个基础架构问题。但是现在我们已经将问题范围缩小到异常之后发生的问题,这个问题可能更适合 Stackoverflow。

更新 [2015 年 12 月 22 日]:

记录器实例化:

logging.addLevelName(Config.LOG_AUDIT_LEVEL_NUM, Config.LOG_AUDIT_LEVEL_NAME)

logger = logging.getLogger(Config.LOGGER_NAME)
logger.setLevel(Config.LOG_LEVEL)

handler = SysLogHandler(address='/dev/log', facility=SysLogHandler.LOG_LOCAL3)
handler.setLevel(Config.LOG_LEVEL)

formatter = log_formatter()
handler.setFormatter(formatter)
logger.addHandler(handler)

log_formatter:

class log_formatter(logging.Formatter):

    def __init__(self,
                 fmt=None,
                 datefmt=None,
                 json_cls=None,
                 json_default=_default_json_default):
        """
        :param fmt: Config as a JSON string, allowed fields;
               extra: provide extra fields always present in logs
               source_host: override source host name
        :param datefmt: Date format to use (required by logging.Formatter
            interface but not used)
        :param json_cls: JSON encoder to forward to json.dumps
        :param json_default: Default JSON representation for unknown types,
                             by default coerce everything to a string
        """

        if fmt is not None:
            self._fmt = json.loads(fmt)
        else:
            self._fmt = {}
        self.json_default = json_default
        self.json_cls = json_cls

        if 'extra' not in self._fmt:
            self.defaults = {}
        else:
            self.defaults = self._fmt['extra']

        try:
            self.source_host = socket.gethostname()
        except:
            self.source_host = ""

    def format(self, record):
        """
        Format a log record to JSON, if the message is a dict
        assume an empty message and use the dict as additional
        fields.
        """

        fields = record.__dict__.copy()
        aux_fields = [
            'relativeCreated', 'process', 'args', 'module', 'funcName', 'name',
            'thread', 'created', 'threadName', 'msecs', 'filename', 'levelno',
            'processName', 'pathname', 'lineno', 'levelname'
        ]
        for k in aux_fields:
            del fields[k]

        if isinstance(record.msg, dict):
            fields.update(record.msg)
            fields.pop('msg')
            msg = ""
        else:
            msg = record.getMessage()

        if 'msg' in fields:
            fields.pop('msg')

        if 'exc_info' in fields:
            if fields['exc_info']:
                formatted = tb.format_exception(*fields['exc_info'])
                fields['exception'] = formatted
            fields.pop('exc_info')

        if 'exc_text' in fields and not fields['exc_text']:
            fields.pop('exc_text')

        logr = self.defaults.copy()

        logr = {
            'timestamp': datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
            'host': self.source_host,
        }
        logr.update(self._build_fields(logr, fields))

        if msg:
            logr['message'] = msg

        something = json.dumps(logr, default=self.json_default, cls=self.json_cls)
        return something

    def _build_fields(self, defaults, fields):
        return dict(defaults.get('fields', {}).items() + fields.items())

更新 [01/03/2015]:

回答发布的问题:

出现异常后应用程序是否仍在工作?

是的,应用程序继续运行。

引发了哪种类型的异常,其原因是什么?

内部/自定义异常。由于不同类型的异常,Logger 已停止。

您是否在应用程序中使用线程?

是的,该应用程序由 gunicorn 线程化。

日志库是如何使用的?

我们使用默认的 FileHandler、SysLogHandler 和自定义格式化程序(输出 JSON)

它是否登录文件?它使用日志轮换吗?

是的,它记录到一个文件中,但没有轮换。

【问题讨论】:

  • stackoverflow.com/help/how-to-ask - 代码 sn-ps 和你尝试的会有帮助
  • @dnozay:添加了代码 sn-ps。
  • 你解决过这个问题吗?当日志格式化程序中抛出异常时(尝试序列化对象的 json.dumps 无限递归)并且想知道您是否想出了解决方案,我遇到了类似的问题。

标签: python logging flask gunicorn


【解决方案1】:

关于after_request,来自文档:

从 Flask 0.7 开始,这个函数可能不会在 请求以防发生未处理的异常。

至于您的日志记录问题,可能是您的调试标志设置为 true,这会导致调试器启动并可能停止日志记录。

参考:
(http://flask.pocoo.org/docs/0.10/api/#flask.Flask.after_request)
(http://flask.pocoo.org/docs/0.10/errorhandling/#working-with-debuggers)

【讨论】:

  • 很遗憾,我们没有将 debug 设置为 true。
【解决方案2】:

您没有提供足够的信息。

异常发生后应用程序是否仍在工作? 引发了哪种类型的异常,其原因是什么? 你在你的应用程序中使用线程吗? 日志库是如何使用的?它是否登录文件?是否使用日志轮换?

假设您在应用程序中使用线程,解释是异常导致线程关闭,因此您不会看到来自该特定线程的任何活动。您也应该注意到应用程序的问题。

如果应用程序仍在工作但变得静默,我的猜测是日志库配置不正确。正如您在 Serverfault 上报告的那样,在添加 fluentd 后似乎出现了问题,这可能与您的应用程序使用日志库的方式不兼容。

【讨论】:

  • 在主要问题中回答了您的问题。
猜你喜欢
  • 2011-09-08
  • 2010-11-08
  • 2021-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-26
  • 2023-03-19
  • 2010-09-06
相关资源
最近更新 更多