/mozilla-release/third_party/python/sentry-sdk/sentry_sdk/integrations/__init__.py

https://github.com/cliqz-oss/browser-f · Python · 183 lines · 111 code · 35 blank · 37 comment · 23 complexity · 26c736d8b08a9ae184f2bbdcee0476aa MD5 · raw file

  1. """This package"""
  2. from __future__ import absolute_import
  3. from threading import Lock
  4. from sentry_sdk._compat import iteritems
  5. from sentry_sdk.utils import logger
  6. from sentry_sdk._types import MYPY
  7. if MYPY:
  8. from typing import Callable
  9. from typing import Dict
  10. from typing import Iterator
  11. from typing import List
  12. from typing import Set
  13. from typing import Tuple
  14. from typing import Type
  15. _installer_lock = Lock()
  16. _installed_integrations = set() # type: Set[str]
  17. def _generate_default_integrations_iterator(integrations, auto_enabling_integrations):
  18. # type: (Tuple[str, ...], Tuple[str, ...]) -> Callable[[bool], Iterator[Type[Integration]]]
  19. def iter_default_integrations(with_auto_enabling_integrations):
  20. # type: (bool) -> Iterator[Type[Integration]]
  21. """Returns an iterator of the default integration classes:
  22. """
  23. from importlib import import_module
  24. if with_auto_enabling_integrations:
  25. all_import_strings = integrations + auto_enabling_integrations
  26. else:
  27. all_import_strings = integrations
  28. for import_string in all_import_strings:
  29. try:
  30. module, cls = import_string.rsplit(".", 1)
  31. yield getattr(import_module(module), cls)
  32. except (DidNotEnable, SyntaxError) as e:
  33. logger.debug(
  34. "Did not import default integration %s: %s", import_string, e
  35. )
  36. if isinstance(iter_default_integrations.__doc__, str):
  37. for import_string in integrations:
  38. iter_default_integrations.__doc__ += "\n- `{}`".format(import_string)
  39. return iter_default_integrations
  40. _AUTO_ENABLING_INTEGRATIONS = (
  41. "sentry_sdk.integrations.django.DjangoIntegration",
  42. "sentry_sdk.integrations.flask.FlaskIntegration",
  43. "sentry_sdk.integrations.bottle.BottleIntegration",
  44. "sentry_sdk.integrations.falcon.FalconIntegration",
  45. "sentry_sdk.integrations.sanic.SanicIntegration",
  46. "sentry_sdk.integrations.celery.CeleryIntegration",
  47. "sentry_sdk.integrations.rq.RqIntegration",
  48. "sentry_sdk.integrations.aiohttp.AioHttpIntegration",
  49. "sentry_sdk.integrations.tornado.TornadoIntegration",
  50. "sentry_sdk.integrations.sqlalchemy.SqlalchemyIntegration",
  51. )
  52. iter_default_integrations = _generate_default_integrations_iterator(
  53. integrations=(
  54. # stdlib/base runtime integrations
  55. "sentry_sdk.integrations.logging.LoggingIntegration",
  56. "sentry_sdk.integrations.stdlib.StdlibIntegration",
  57. "sentry_sdk.integrations.excepthook.ExcepthookIntegration",
  58. "sentry_sdk.integrations.dedupe.DedupeIntegration",
  59. "sentry_sdk.integrations.atexit.AtexitIntegration",
  60. "sentry_sdk.integrations.modules.ModulesIntegration",
  61. "sentry_sdk.integrations.argv.ArgvIntegration",
  62. "sentry_sdk.integrations.threading.ThreadingIntegration",
  63. ),
  64. auto_enabling_integrations=_AUTO_ENABLING_INTEGRATIONS,
  65. )
  66. del _generate_default_integrations_iterator
  67. def setup_integrations(
  68. integrations, with_defaults=True, with_auto_enabling_integrations=False
  69. ):
  70. # type: (List[Integration], bool, bool) -> Dict[str, Integration]
  71. """Given a list of integration instances this installs them all. When
  72. `with_defaults` is set to `True` then all default integrations are added
  73. unless they were already provided before.
  74. """
  75. integrations = dict(
  76. (integration.identifier, integration) for integration in integrations or ()
  77. )
  78. logger.debug("Setting up integrations (with default = %s)", with_defaults)
  79. # Integrations that are not explicitly set up by the user.
  80. used_as_default_integration = set()
  81. if with_defaults:
  82. for integration_cls in iter_default_integrations(
  83. with_auto_enabling_integrations
  84. ):
  85. if integration_cls.identifier not in integrations:
  86. instance = integration_cls()
  87. integrations[instance.identifier] = instance
  88. used_as_default_integration.add(instance.identifier)
  89. for identifier, integration in iteritems(integrations):
  90. with _installer_lock:
  91. if identifier not in _installed_integrations:
  92. logger.debug(
  93. "Setting up previously not enabled integration %s", identifier
  94. )
  95. try:
  96. type(integration).setup_once()
  97. except NotImplementedError:
  98. if getattr(integration, "install", None) is not None:
  99. logger.warning(
  100. "Integration %s: The install method is "
  101. "deprecated. Use `setup_once`.",
  102. identifier,
  103. )
  104. integration.install()
  105. else:
  106. raise
  107. except DidNotEnable as e:
  108. if identifier not in used_as_default_integration:
  109. raise
  110. logger.debug(
  111. "Did not enable default integration %s: %s", identifier, e
  112. )
  113. _installed_integrations.add(identifier)
  114. for identifier in integrations:
  115. logger.debug("Enabling integration %s", identifier)
  116. return integrations
  117. class DidNotEnable(Exception):
  118. """
  119. The integration could not be enabled due to a trivial user error like
  120. `flask` not being installed for the `FlaskIntegration`.
  121. This exception is silently swallowed for default integrations, but reraised
  122. for explicitly enabled integrations.
  123. """
  124. class Integration(object):
  125. """Baseclass for all integrations.
  126. To accept options for an integration, implement your own constructor that
  127. saves those options on `self`.
  128. """
  129. install = None
  130. """Legacy method, do not implement."""
  131. identifier = None # type: str
  132. """String unique ID of integration type"""
  133. @staticmethod
  134. def setup_once():
  135. # type: () -> None
  136. """
  137. Initialize the integration.
  138. This function is only called once, ever. Configuration is not available
  139. at this point, so the only thing to do here is to hook into exception
  140. handlers, and perhaps do monkeypatches.
  141. Inside those hooks `Integration.current` can be used to access the
  142. instance again.
  143. """
  144. raise NotImplementedError()