_config.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. # This file is dual licensed under the terms of the Apache License, Version
  2. # 2.0, and the MIT License. See the LICENSE file in the root of this
  3. # repository for complete details.
  4. """
  5. Global state department. Don't reload this module or everything breaks.
  6. """
  7. from __future__ import absolute_import, division, print_function
  8. import warnings
  9. from collections import OrderedDict
  10. from ._generic import BoundLogger
  11. from ._loggers import (
  12. PrintLoggerFactory,
  13. )
  14. from .dev import ConsoleRenderer, _has_colorama
  15. from .processors import (
  16. StackInfoRenderer,
  17. TimeStamper,
  18. format_exc_info,
  19. )
  20. _BUILTIN_DEFAULT_PROCESSORS = [
  21. StackInfoRenderer(),
  22. format_exc_info,
  23. TimeStamper(fmt="%Y-%m-%d %H:%M.%S", utc=False),
  24. ConsoleRenderer(colors=_has_colorama),
  25. ]
  26. _BUILTIN_DEFAULT_CONTEXT_CLASS = OrderedDict
  27. _BUILTIN_DEFAULT_WRAPPER_CLASS = BoundLogger
  28. _BUILTIN_DEFAULT_LOGGER_FACTORY = PrintLoggerFactory()
  29. _BUILTIN_CACHE_LOGGER_ON_FIRST_USE = False
  30. class _Configuration(object):
  31. """
  32. Global defaults.
  33. """
  34. is_configured = False
  35. default_processors = _BUILTIN_DEFAULT_PROCESSORS[:]
  36. default_context_class = _BUILTIN_DEFAULT_CONTEXT_CLASS
  37. default_wrapper_class = _BUILTIN_DEFAULT_WRAPPER_CLASS
  38. logger_factory = _BUILTIN_DEFAULT_LOGGER_FACTORY
  39. cache_logger_on_first_use = _BUILTIN_CACHE_LOGGER_ON_FIRST_USE
  40. _CONFIG = _Configuration()
  41. """
  42. Global defaults used when arguments to :func:`wrap_logger` are omitted.
  43. """
  44. def get_logger(*args, **initial_values):
  45. """
  46. Convenience function that returns a logger according to configuration.
  47. >>> from structlog import get_logger
  48. >>> log = get_logger(y=23)
  49. >>> log.msg("hello", x=42)
  50. y=23 x=42 event='hello'
  51. :param args: *Optional* positional arguments that are passed unmodified to
  52. the logger factory. Therefore it depends on the factory what they
  53. mean.
  54. :param initial_values: Values that are used to pre-populate your contexts.
  55. :rtype: A proxy that creates a correctly configured bound logger when
  56. necessary.
  57. See :ref:`configuration` for details.
  58. If you prefer CamelCase, there's an alias for your reading pleasure:
  59. :func:`structlog.getLogger`.
  60. .. versionadded:: 0.4.0
  61. `args`
  62. """
  63. return wrap_logger(None, logger_factory_args=args, **initial_values)
  64. getLogger = get_logger
  65. """
  66. CamelCase alias for :func:`structlog.get_logger`.
  67. This function is supposed to be in every source file -- we don't want it to
  68. stick out like a sore thumb in frameworks like Twisted or Zope.
  69. """
  70. def wrap_logger(logger, processors=None, wrapper_class=None,
  71. context_class=None, cache_logger_on_first_use=None,
  72. logger_factory_args=None, **initial_values):
  73. """
  74. Create a new bound logger for an arbitrary *logger*.
  75. Default values for *processors*, *wrapper_class*, and *context_class* can
  76. be set using :func:`configure`.
  77. If you set an attribute here, :func:`configure` calls have *no* effect for
  78. the *respective* attribute.
  79. In other words: selective overwriting of the defaults while keeping some
  80. *is* possible.
  81. :param initial_values: Values that are used to pre-populate your contexts.
  82. :param tuple logger_factory_args: Values that are passed unmodified as
  83. ``*logger_factory_args`` to the logger factory if not `None`.
  84. :rtype: A proxy that creates a correctly configured bound logger when
  85. necessary.
  86. See :func:`configure` for the meaning of the rest of the arguments.
  87. .. versionadded:: 0.4.0
  88. `logger_factory_args`
  89. """
  90. return BoundLoggerLazyProxy(
  91. logger,
  92. wrapper_class=wrapper_class,
  93. processors=processors,
  94. context_class=context_class,
  95. cache_logger_on_first_use=cache_logger_on_first_use,
  96. initial_values=initial_values,
  97. logger_factory_args=logger_factory_args,
  98. )
  99. def configure(processors=None, wrapper_class=None, context_class=None,
  100. logger_factory=None, cache_logger_on_first_use=None):
  101. """
  102. Configures the **global** defaults.
  103. They are used if :func:`wrap_logger` has been called without arguments.
  104. Also sets the global class attribute :attr:`is_configured` to `True` on
  105. first call. Can be called several times, keeping an argument at `None`
  106. leaves is unchanged from the current setting.
  107. Use :func:`reset_defaults` to undo your changes.
  108. :param list processors: List of processors.
  109. :param type wrapper_class: Class to use for wrapping loggers instead of
  110. :class:`structlog.BoundLogger`. See :doc:`standard-library`,
  111. :doc:`twisted`, and :doc:`custom-wrappers`.
  112. :param type context_class: Class to be used for internal context keeping.
  113. :param callable logger_factory: Factory to be called to create a new
  114. logger that shall be wrapped.
  115. :param bool cache_logger_on_first_use: `wrap_logger` doesn't return an
  116. actual wrapped logger but a proxy that assembles one when it's first
  117. used. If this option is set to `True`, this assembled logger is
  118. cached. See :doc:`performance`.
  119. .. versionadded:: 0.3.0
  120. `cache_logger_on_first_use`
  121. """
  122. _CONFIG.is_configured = True
  123. if processors is not None:
  124. _CONFIG.default_processors = processors
  125. if wrapper_class:
  126. _CONFIG.default_wrapper_class = wrapper_class
  127. if context_class:
  128. _CONFIG.default_context_class = context_class
  129. if logger_factory:
  130. _CONFIG.logger_factory = logger_factory
  131. if cache_logger_on_first_use is not None:
  132. _CONFIG.cache_logger_on_first_use = cache_logger_on_first_use
  133. def configure_once(*args, **kw):
  134. """
  135. Configures iff structlog isn't configured yet.
  136. It does *not* matter whether is was configured using :func:`configure`
  137. or :func:`configure_once` before.
  138. Raises a RuntimeWarning if repeated configuration is attempted.
  139. """
  140. if not _CONFIG.is_configured:
  141. configure(*args, **kw)
  142. else:
  143. warnings.warn('Repeated configuration attempted.', RuntimeWarning)
  144. def reset_defaults():
  145. """
  146. Resets global default values to builtins.
  147. That means [:class:`~structlog.processors.StackInfoRenderer`,
  148. :func:`~structlog.processors.format_exc_info`,
  149. :class:`~structlog.processors.TimeStamper`,
  150. :class:`~structlog.dev.ConsoleRenderer`] for *processors*,
  151. :class:`~structlog.BoundLogger` for *wrapper_class*, ``OrderedDict`` for
  152. *context_class*, :class:`~structlog.PrintLoggerFactory` for
  153. *logger_factory*, and `False` for *cache_logger_on_first_use*.
  154. Also sets the global class attribute :attr:`is_configured` to `False`.
  155. """
  156. _CONFIG.is_configured = False
  157. _CONFIG.default_processors = _BUILTIN_DEFAULT_PROCESSORS[:]
  158. _CONFIG.default_wrapper_class = _BUILTIN_DEFAULT_WRAPPER_CLASS
  159. _CONFIG.default_context_class = _BUILTIN_DEFAULT_CONTEXT_CLASS
  160. _CONFIG.logger_factory = _BUILTIN_DEFAULT_LOGGER_FACTORY
  161. _CONFIG.cache_logger_on_first_use = _BUILTIN_CACHE_LOGGER_ON_FIRST_USE
  162. class BoundLoggerLazyProxy(object):
  163. """
  164. Instantiates a BoundLogger on first usage.
  165. Takes both configuration and instantiation parameters into account.
  166. The only points where a BoundLogger changes state are bind(), unbind(), and
  167. new() and that return the actual BoundLogger.
  168. If and only if configuration says so, that actual BoundLogger is cached on
  169. first usage.
  170. .. versionchanged:: 0.4.0
  171. Added support for `logger_factory_args`.
  172. """
  173. def __init__(self, logger, wrapper_class=None, processors=None,
  174. context_class=None, cache_logger_on_first_use=None,
  175. initial_values=None, logger_factory_args=None):
  176. self._logger = logger
  177. self._wrapper_class = wrapper_class
  178. self._processors = processors
  179. self._context_class = context_class
  180. self._cache_logger_on_first_use = cache_logger_on_first_use
  181. self._initial_values = initial_values or {}
  182. self._logger_factory_args = logger_factory_args or ()
  183. def __repr__(self):
  184. return (
  185. '<BoundLoggerLazyProxy(logger={0._logger!r}, wrapper_class='
  186. '{0._wrapper_class!r}, processors={0._processors!r}, '
  187. 'context_class={0._context_class!r}, '
  188. 'initial_values={0._initial_values!r}, '
  189. 'logger_factory_args={0._logger_factory_args!r})>'.format(self)
  190. )
  191. def bind(self, **new_values):
  192. """
  193. Assemble a new BoundLogger from arguments and configuration.
  194. """
  195. if self._context_class:
  196. ctx = self._context_class(self._initial_values)
  197. else:
  198. ctx = _CONFIG.default_context_class(self._initial_values)
  199. cls = self._wrapper_class or _CONFIG.default_wrapper_class
  200. _logger = self._logger
  201. if not _logger:
  202. _logger = _CONFIG.logger_factory(*self._logger_factory_args)
  203. if self._processors is None:
  204. procs = _CONFIG.default_processors
  205. else:
  206. procs = self._processors
  207. logger = cls(
  208. _logger,
  209. processors=procs,
  210. context=ctx,
  211. )
  212. def finalized_bind(**new_values):
  213. """
  214. Use cached assembled logger to bind potentially new values.
  215. """
  216. if new_values:
  217. return logger.bind(**new_values)
  218. else:
  219. return logger
  220. if (
  221. self._cache_logger_on_first_use is True or
  222. (self._cache_logger_on_first_use is None and
  223. _CONFIG.cache_logger_on_first_use is True)
  224. ):
  225. self.bind = finalized_bind
  226. return finalized_bind(**new_values)
  227. def unbind(self, *keys):
  228. """
  229. Same as bind, except unbind *keys* first.
  230. In our case that could be only initial values.
  231. """
  232. return self.bind().unbind(*keys)
  233. def new(self, **new_values):
  234. """
  235. Clear context, then bind.
  236. """
  237. if self._context_class:
  238. self._context_class().clear()
  239. else:
  240. _CONFIG.default_context_class().clear()
  241. bl = self.bind(**new_values)
  242. return bl
  243. def __getattr__(self, name):
  244. """
  245. If a logging method if called on a lazy proxy, we have to create an
  246. ephemeral BoundLogger first.
  247. """
  248. bl = self.bind()
  249. return getattr(bl, name)