forked from orbit-oss/flask
simplify logging configuration
single default handler and formatter don't remove handlers configure level once using setLevel document logging reorganize logging tests
This commit is contained in:
parent
85fa8aabf5
commit
66b1b752da
13 changed files with 399 additions and 451 deletions
56
flask/app.py
56
flask/app.py
|
|
@ -16,10 +16,9 @@ from functools import update_wrapper
|
|||
from itertools import chain
|
||||
from threading import Lock
|
||||
|
||||
from werkzeug.datastructures import ImmutableDict, Headers
|
||||
from werkzeug.exceptions import BadRequest, HTTPException, \
|
||||
InternalServerError, MethodNotAllowed, default_exceptions, \
|
||||
BadRequestKeyError
|
||||
from werkzeug.datastructures import Headers, ImmutableDict
|
||||
from werkzeug.exceptions import BadRequest, BadRequestKeyError, HTTPException, \
|
||||
InternalServerError, MethodNotAllowed, default_exceptions
|
||||
from werkzeug.routing import BuildError, Map, RequestRedirect, Rule
|
||||
|
||||
from . import cli, json
|
||||
|
|
@ -30,6 +29,7 @@ from .globals import _request_ctx_stack, g, request, session
|
|||
from .helpers import _PackageBoundObject, \
|
||||
_endpoint_from_view_func, find_package, get_debug_flag, \
|
||||
get_flashed_messages, locked_cached_property, url_for
|
||||
from .logging import create_logger
|
||||
from .sessions import SecureCookieSessionInterface
|
||||
from .signals import appcontext_tearing_down, got_request_exception, \
|
||||
request_finished, request_started, request_tearing_down
|
||||
|
|
@ -37,9 +37,6 @@ from .templating import DispatchingJinjaLoader, Environment, \
|
|||
_default_template_ctx_processor
|
||||
from .wrappers import Request, Response
|
||||
|
||||
# a lock used for logger initialization
|
||||
_logger_lock = Lock()
|
||||
|
||||
# a singleton sentinel value for parameter defaults
|
||||
_sentinel = object()
|
||||
|
||||
|
|
@ -264,12 +261,6 @@ class Flask(_PackageBoundObject):
|
|||
#: ``USE_X_SENDFILE`` configuration key. Defaults to ``False``.
|
||||
use_x_sendfile = ConfigAttribute('USE_X_SENDFILE')
|
||||
|
||||
#: The name of the logger to use. By default the logger name is the
|
||||
#: package name passed to the constructor.
|
||||
#:
|
||||
#: .. versionadded:: 0.4
|
||||
logger_name = ConfigAttribute('LOGGER_NAME')
|
||||
|
||||
#: The JSON encoder class to use. Defaults to :class:`~flask.json.JSONEncoder`.
|
||||
#:
|
||||
#: .. versionadded:: 0.10
|
||||
|
|
@ -294,8 +285,6 @@ class Flask(_PackageBoundObject):
|
|||
'SECRET_KEY': None,
|
||||
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
|
||||
'USE_X_SENDFILE': False,
|
||||
'LOGGER_NAME': None,
|
||||
'LOGGER_HANDLER_POLICY': 'always',
|
||||
'SERVER_NAME': None,
|
||||
'APPLICATION_ROOT': '/',
|
||||
'SESSION_COOKIE_NAME': 'session',
|
||||
|
|
@ -392,10 +381,6 @@ class Flask(_PackageBoundObject):
|
|||
#: to load a config from files.
|
||||
self.config = self.make_config(instance_relative_config)
|
||||
|
||||
# Prepare the deferred setup of the logger.
|
||||
self._logger = None
|
||||
self.logger_name = self.import_name
|
||||
|
||||
#: A dictionary of all view functions registered. The keys will
|
||||
#: be function names which are also used to generate URLs and
|
||||
#: the values are the function objects themselves.
|
||||
|
|
@ -613,27 +598,28 @@ class Flask(_PackageBoundObject):
|
|||
return rv
|
||||
return self.debug
|
||||
|
||||
@property
|
||||
@locked_cached_property
|
||||
def logger(self):
|
||||
"""A :class:`logging.Logger` object for this application. The
|
||||
default configuration is to log to stderr if the application is
|
||||
in debug mode. This logger can be used to (surprise) log messages.
|
||||
Here some examples::
|
||||
"""The ``'flask.app'`` logger, a standard Python
|
||||
:class:`~logging.Logger`.
|
||||
|
||||
app.logger.debug('A value for debugging')
|
||||
app.logger.warning('A warning occurred (%d apples)', 42)
|
||||
app.logger.error('An error occurred')
|
||||
In debug mode, the logger's :attr:`~logging.Logger.level` will be set
|
||||
to :data:`~logging.DEBUG`.
|
||||
|
||||
If there are no handlers configured, a default handler will be added.
|
||||
See :ref:`logging` for more information.
|
||||
|
||||
.. versionchanged:: 1.0
|
||||
Behavior was simplified. The logger is always named
|
||||
``flask.app``. The level is only set during configuration, it
|
||||
doesn't check ``app.debug`` each time. Only one format is used,
|
||||
not different ones depending on ``app.debug``. No handlers are
|
||||
removed, and a handler is only added if no handlers are already
|
||||
configured.
|
||||
|
||||
.. versionadded:: 0.3
|
||||
"""
|
||||
if self._logger and self._logger.name == self.logger_name:
|
||||
return self._logger
|
||||
with _logger_lock:
|
||||
if self._logger and self._logger.name == self.logger_name:
|
||||
return self._logger
|
||||
from flask.logging import create_logger
|
||||
self._logger = rv = create_logger(self)
|
||||
return rv
|
||||
return create_logger(self)
|
||||
|
||||
@locked_cached_property
|
||||
def jinja_env(self):
|
||||
|
|
|
|||
121
flask/logging.py
121
flask/logging.py
|
|
@ -1,94 +1,69 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.logging
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Implements the logging support for Flask.
|
||||
|
||||
:copyright: (c) 2015 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from werkzeug.local import LocalProxy
|
||||
from logging import getLogger, StreamHandler, Formatter, getLoggerClass, \
|
||||
DEBUG, ERROR
|
||||
from .globals import _request_ctx_stack
|
||||
|
||||
|
||||
PROD_LOG_FORMAT = '[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
|
||||
DEBUG_LOG_FORMAT = (
|
||||
'-' * 80 + '\n' +
|
||||
'%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n' +
|
||||
'%(message)s\n' +
|
||||
'-' * 80
|
||||
)
|
||||
from .globals import request
|
||||
|
||||
|
||||
@LocalProxy
|
||||
def _proxy_stream():
|
||||
"""Finds the most appropriate error stream for the application. If a
|
||||
WSGI request is in flight we log to wsgi.errors, otherwise this resolves
|
||||
to sys.stderr.
|
||||
def wsgi_errors_stream():
|
||||
"""Find the most appropriate error stream for the application. If a request
|
||||
is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``.
|
||||
|
||||
If you configure your own :class:`logging.StreamHandler`, you may want to
|
||||
use this for the stream. If you are using file or dict configuration and
|
||||
can't import this directly, you can refer to it as
|
||||
``ext://flask.logging.wsgi_errors_stream``.
|
||||
"""
|
||||
ctx = _request_ctx_stack.top
|
||||
if ctx is not None:
|
||||
return ctx.request.environ['wsgi.errors']
|
||||
return sys.stderr
|
||||
return request.environ['wsgi.errors'] if request else sys.stderr
|
||||
|
||||
|
||||
def _should_log_for(app, mode):
|
||||
policy = app.config['LOGGER_HANDLER_POLICY']
|
||||
if policy == mode or policy == 'always':
|
||||
return True
|
||||
def has_level_handler(logger):
|
||||
"""Check if there is a handler in the logging chain that will handle the
|
||||
given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`.
|
||||
"""
|
||||
level = logger.getEffectiveLevel()
|
||||
current = logger
|
||||
|
||||
while current:
|
||||
if any(handler.level <= level for handler in current.handlers):
|
||||
return True
|
||||
|
||||
if not current.propagate:
|
||||
break
|
||||
|
||||
current = current.parent
|
||||
|
||||
return False
|
||||
|
||||
|
||||
#: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format
|
||||
#: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``.
|
||||
default_handler = logging.StreamHandler(wsgi_errors_stream)
|
||||
default_handler.setFormatter(logging.Formatter(
|
||||
'[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
|
||||
))
|
||||
|
||||
|
||||
def create_logger(app):
|
||||
"""Creates a logger for the given application. This logger works
|
||||
similar to a regular Python logger but changes the effective logging
|
||||
level based on the application's debug flag. Furthermore this
|
||||
function also removes all attached handlers in case there was a
|
||||
logger with the log name before.
|
||||
"""Get the ``'flask.app'`` logger and configure it if needed.
|
||||
|
||||
When :attr:`~flask.Flask.debug` is enabled, set the logger level to
|
||||
:data:`logging.DEBUG` if it is not set.
|
||||
|
||||
If there is no handler for the logger's effective level, add a
|
||||
:class:`~logging.StreamHandler` for
|
||||
:func:`~flask.logging.wsgi_errors_stream` with a basic format.
|
||||
"""
|
||||
Logger = getLoggerClass()
|
||||
logger = logging.getLogger('flask.app')
|
||||
|
||||
class DebugLogger(Logger):
|
||||
def getEffectiveLevel(self):
|
||||
if self.level == 0 and app.debug:
|
||||
return DEBUG
|
||||
return Logger.getEffectiveLevel(self)
|
||||
if app.debug and logger.level == logging.NOTSET:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
class DebugHandler(StreamHandler):
|
||||
def emit(self, record):
|
||||
if app.debug and _should_log_for(app, 'debug'):
|
||||
StreamHandler.emit(self, record)
|
||||
|
||||
class ProductionHandler(StreamHandler):
|
||||
def emit(self, record):
|
||||
if not app.debug and _should_log_for(app, 'production'):
|
||||
StreamHandler.emit(self, record)
|
||||
|
||||
debug_handler = DebugHandler()
|
||||
debug_handler.setLevel(DEBUG)
|
||||
debug_handler.setFormatter(Formatter(DEBUG_LOG_FORMAT))
|
||||
|
||||
prod_handler = ProductionHandler(_proxy_stream)
|
||||
prod_handler.setLevel(ERROR)
|
||||
prod_handler.setFormatter(Formatter(PROD_LOG_FORMAT))
|
||||
|
||||
logger = getLogger(app.logger_name)
|
||||
# just in case that was not a new logger, get rid of all the handlers
|
||||
# already attached to it.
|
||||
del logger.handlers[:]
|
||||
logger.__class__ = DebugLogger
|
||||
logger.addHandler(debug_handler)
|
||||
logger.addHandler(prod_handler)
|
||||
|
||||
# Disable propagation by default
|
||||
logger.propagate = False
|
||||
if not has_level_handler(logger):
|
||||
logger.addHandler(default_handler)
|
||||
|
||||
return logger
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue