flask的signals模块中基于blinker信号量库,创建了request_started、request_finished、got_request_exception等Signal对象。flask在相关事件发生时,就调用对应Signal对象发布信号。
下面以got_request_exception举例:
class Flask(_PackageBoundObject):
...
def handle_exception(self, e):
"""Default exception handling that kicks in when an exception
occurs that is not caught. In debug mode the exception will
be re-raised immediately, otherwise it is logged and the handler
for a 500 internal server error is used. If no such handler
exists, a default 500 internal server error message is displayed.
.. versionadded:: 0.3
"""
exc_type, exc_value, tb = sys.exc_info()
got_request_exception.send(self, exception=e)
handler = self._find_error_handler(InternalServerError())
if self.propagate_exceptions:
# if we want to repropagate the exception, we can attempt to
# raise it with the whole traceback in case we can do that
# (the function was actually called from the except part)
# otherwise, we just raise the error again
if exc_value is e:
reraise(exc_type, exc_value, tb)
else:
raise e
self.log_exception((exc_type, exc_value, tb))
if handler is None:
return InternalServerError()
return self.finalize_request(handler(e), from_error_handler=True)
...
每当异常发生时,flask都通过got_request_exception.send(self, exception=e)
发布异常信号,所以我们可以订阅对应异常信号,当信号发布时,进行日志记录。例如:
@got_request_exception.connect_via(app)
def log_exception(sender, exception, **extra):
sender.logger.info('{remote_host} -- "{method} {path} {protocol}" - {user_agent}'.format(
remote_host=request.remote_addr,
method=request.method,
protocol=request.headers.environ['SERVER_PROTOCOL'],
path=request.full_path,
user_agent=request.user_agent))
if request.method != "GET":
sender.logger.debug(request.json)
if not isinstance(exception, HTTPException):
sender.logger.exception(exception)