logging.py 2.3 KB

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