1 from __future__ import annotations 2 3 import logging 4 import sys 5 import typing as t 6 7 from werkzeug.local import LocalProxy 8 9 from .globals import request 10 11 if t.TYPE_CHECKING: # pragma: no cover 12 from .sansio.app import App 13 14 15 @LocalProxy 16 def wsgi_errors_stream() -> t.TextIO: 17 """Find the most appropriate error stream for the application. If a request 18 is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``. 19 20 If you configure your own :class:`logging.StreamHandler`, you may want to 21 use this for the stream. If you are using file or dict configuration and 22 can't import this directly, you can refer to it as 23 ``ext://flask.logging.wsgi_errors_stream``. 24 """ 25 if request: 26 return request.environ["wsgi.errors"] # type: ignore[no-any-return] 27 28 return sys.stderr 29 30 31 def has_level_handler(logger: logging.Logger) -> bool: 32 """Check if there is a handler in the logging chain that will handle the 33 given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`. 34 """ 35 level = logger.getEffectiveLevel() 36 current = logger 37 38 while current: 39 if any(handler.level <= level for handler in current.handlers): 40 return True 41 42 if not current.propagate: 43 break 44 45 current = current.parent # type: ignore 46 47 return False 48 49 50 #: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format 51 #: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``. 52 default_handler = logging.StreamHandler(wsgi_errors_stream) # type: ignore 53 default_handler.setFormatter( 54 logging.Formatter("[%(asctime)s] %(levelname)s in %(module)s: %(message)s") 55 ) 56 57 58 def create_logger(app: App) -> logging.Logger: 59 """Get the Flask app's logger and configure it if needed. 60 61 The logger name will be the same as 62 :attr:`app.import_name <flask.Flask.name>`. 63 64 When :attr:`~flask.Flask.debug` is enabled, set the logger level to 65 :data:`logging.DEBUG` if it is not set. 66 67 If there is no handler for the logger's effective level, add a 68 :class:`~logging.StreamHandler` for 69 :func:`~flask.logging.wsgi_errors_stream` with a basic format. 70 """ 71 logger = logging.getLogger(app.name) 72 73 if app.debug and not logger.level: 74 logger.setLevel(logging.DEBUG) 75 76 if not has_level_handler(logger): 77 logger.addHandler(default_handler) 78 79 return logger 80