__init__.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. # Copyright The OpenTelemetry Authors
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from __future__ import annotations
  15. import logging
  16. import typing
  17. from contextvars import Token
  18. from os import environ
  19. from uuid import uuid4
  20. # pylint: disable=wrong-import-position
  21. from opentelemetry.context.context import Context, _RuntimeContext # noqa
  22. from opentelemetry.environment_variables import OTEL_PYTHON_CONTEXT
  23. from opentelemetry.util._importlib_metadata import entry_points
  24. logger = logging.getLogger(__name__)
  25. def _load_runtime_context() -> _RuntimeContext:
  26. """Initialize the RuntimeContext
  27. Returns:
  28. An instance of RuntimeContext.
  29. """
  30. # FIXME use a better implementation of a configuration manager
  31. # to avoid having to get configuration values straight from
  32. # environment variables
  33. default_context = "contextvars_context"
  34. configured_context = environ.get(OTEL_PYTHON_CONTEXT, default_context) # type: str
  35. try:
  36. return next( # type: ignore
  37. iter( # type: ignore
  38. entry_points( # type: ignore
  39. group="opentelemetry_context",
  40. name=configured_context,
  41. )
  42. )
  43. ).load()()
  44. except Exception: # pylint: disable=broad-exception-caught
  45. logger.exception(
  46. "Failed to load context: %s, fallback to %s",
  47. configured_context,
  48. default_context,
  49. )
  50. return next( # type: ignore
  51. iter( # type: ignore
  52. entry_points( # type: ignore
  53. group="opentelemetry_context",
  54. name=default_context,
  55. )
  56. )
  57. ).load()()
  58. _RUNTIME_CONTEXT = _load_runtime_context()
  59. def create_key(keyname: str) -> str:
  60. """To allow cross-cutting concern to control access to their local state,
  61. the RuntimeContext API provides a function which takes a keyname as input,
  62. and returns a unique key.
  63. Args:
  64. keyname: The key name is for debugging purposes and is not required to be unique.
  65. Returns:
  66. A unique string representing the newly created key.
  67. """
  68. return keyname + "-" + str(uuid4())
  69. def get_value(key: str, context: typing.Optional[Context] = None) -> "object":
  70. """To access the local state of a concern, the RuntimeContext API
  71. provides a function which takes a context and a key as input,
  72. and returns a value.
  73. Args:
  74. key: The key of the value to retrieve.
  75. context: The context from which to retrieve the value, if None, the current context is used.
  76. Returns:
  77. The value associated with the key.
  78. """
  79. return context.get(key) if context is not None else get_current().get(key)
  80. def set_value(
  81. key: str, value: "object", context: typing.Optional[Context] = None
  82. ) -> Context:
  83. """To record the local state of a cross-cutting concern, the
  84. RuntimeContext API provides a function which takes a context, a
  85. key, and a value as input, and returns an updated context
  86. which contains the new value.
  87. Args:
  88. key: The key of the entry to set.
  89. value: The value of the entry to set.
  90. context: The context to copy, if None, the current context is used.
  91. Returns:
  92. A new `Context` containing the value set.
  93. """
  94. if context is None:
  95. context = get_current()
  96. new_values = context.copy()
  97. new_values[key] = value
  98. return Context(new_values)
  99. def get_current() -> Context:
  100. """To access the context associated with program execution,
  101. the Context API provides a function which takes no arguments
  102. and returns a Context.
  103. Returns:
  104. The current `Context` object.
  105. """
  106. return _RUNTIME_CONTEXT.get_current()
  107. def attach(context: Context) -> Token[Context]:
  108. """Associates a Context with the caller's current execution unit. Returns
  109. a token that can be used to restore the previous Context.
  110. Args:
  111. context: The Context to set as current.
  112. Returns:
  113. A token that can be used with `detach` to reset the context.
  114. """
  115. return _RUNTIME_CONTEXT.attach(context)
  116. def detach(token: Token[Context]) -> None:
  117. """Resets the Context associated with the caller's current execution unit
  118. to the value it had before attaching a specified Context.
  119. Args:
  120. token: The Token that was returned by a previous call to attach a Context.
  121. """
  122. try:
  123. _RUNTIME_CONTEXT.detach(token)
  124. except Exception: # pylint: disable=broad-exception-caught
  125. logger.exception("Failed to detach context")
  126. # FIXME This is a temporary location for the suppress instrumentation key.
  127. # Once the decision around how to suppress instrumentation is made in the
  128. # spec, this key should be moved accordingly.
  129. _SUPPRESS_INSTRUMENTATION_KEY = create_key("suppress_instrumentation")
  130. _SUPPRESS_HTTP_INSTRUMENTATION_KEY = create_key(
  131. "suppress_http_instrumentation"
  132. )
  133. __all__ = [
  134. "Context",
  135. "attach",
  136. "create_key",
  137. "detach",
  138. "get_current",
  139. "get_value",
  140. "set_value",
  141. ]