tracer.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. # Copyright The OpenTracing Authors
  2. # Copyright Uber Technologies, Inc
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from __future__ import absolute_import
  16. from collections import namedtuple
  17. from .span import Span
  18. from .span import SpanContext
  19. from .scope import Scope
  20. from .scope_manager import ScopeManager
  21. from .propagation import Format, UnsupportedFormatException
  22. class Tracer(object):
  23. """Tracer is the entry point API between instrumentation code and the
  24. tracing implementation.
  25. This implementation both defines the public Tracer API, and provides
  26. a default no-op behavior.
  27. """
  28. _supported_formats = [Format.TEXT_MAP, Format.BINARY, Format.HTTP_HEADERS]
  29. def __init__(self, scope_manager=None):
  30. self._scope_manager = ScopeManager() if scope_manager is None \
  31. else scope_manager
  32. self._noop_span_context = SpanContext()
  33. self._noop_span = Span(tracer=self, context=self._noop_span_context)
  34. self._noop_scope = Scope(self._scope_manager, self._noop_span)
  35. @property
  36. def scope_manager(self):
  37. """Provides access to the current :class:`~opentracing.ScopeManager`.
  38. :rtype: :class:`~opentracing.ScopeManager`
  39. """
  40. return self._scope_manager
  41. @property
  42. def active_span(self):
  43. """Provides access to the the active :class:`Span`. This is a shorthand for
  44. :attr:`Tracer.scope_manager.active.span`, and ``None`` will be
  45. returned if :attr:`Scope.span` is ``None``.
  46. :rtype: :class:`~opentracing.Span`
  47. :return: the active :class:`Span`.
  48. """
  49. scope = self._scope_manager.active
  50. return None if scope is None else scope.span
  51. def start_active_span(self,
  52. operation_name,
  53. child_of=None,
  54. references=None,
  55. tags=None,
  56. start_time=None,
  57. ignore_active_span=False,
  58. finish_on_close=True):
  59. """Returns a newly started and activated :class:`Scope`.
  60. The returned :class:`Scope` supports with-statement contexts. For
  61. example::
  62. with tracer.start_active_span('...') as scope:
  63. scope.span.set_tag('http.method', 'GET')
  64. do_some_work()
  65. # Span.finish() is called as part of scope deactivation through
  66. # the with statement.
  67. It's also possible to not finish the :class:`Span` when the
  68. :class:`Scope` context expires::
  69. with tracer.start_active_span('...',
  70. finish_on_close=False) as scope:
  71. scope.span.set_tag('http.method', 'GET')
  72. do_some_work()
  73. # Span.finish() is not called as part of Scope deactivation as
  74. # `finish_on_close` is `False`.
  75. :param operation_name: name of the operation represented by the new
  76. :class:`Span` from the perspective of the current service.
  77. :type operation_name: str
  78. :param child_of: (optional) a :class:`Span` or :class:`SpanContext`
  79. instance representing the parent in a REFERENCE_CHILD_OF reference.
  80. If specified, the `references` parameter must be omitted.
  81. :type child_of: Span or SpanContext
  82. :param references: (optional) references that identify one or more
  83. parent :class:`SpanContext`\\ s. (See the Reference documentation
  84. for detail).
  85. :type references: :obj:`list` of :class:`Reference`
  86. :param tags: an optional dictionary of :class:`Span` tags. The caller
  87. gives up ownership of that dictionary, because the :class:`Tracer`
  88. may use it as-is to avoid extra data copying.
  89. :type tags: dict
  90. :param start_time: an explicit :class:`Span` start time as a unix
  91. timestamp per :meth:`time.time()`.
  92. :type start_time: float
  93. :param ignore_active_span: (optional) an explicit flag that ignores
  94. the current active :class:`Scope` and creates a root :class:`Span`.
  95. :type ignore_active_span: bool
  96. :param finish_on_close: whether :class:`Span` should automatically be
  97. finished when :meth:`Scope.close()` is called.
  98. :type finish_on_close: bool
  99. :rtype: Scope
  100. :return: a :class:`Scope`, already registered via the
  101. :class:`ScopeManager`.
  102. """
  103. return self._noop_scope
  104. def start_span(self,
  105. operation_name=None,
  106. child_of=None,
  107. references=None,
  108. tags=None,
  109. start_time=None,
  110. ignore_active_span=False):
  111. """Starts and returns a new :class:`Span` representing a unit of work.
  112. Starting a root :class:`Span` (a :class:`Span` with no causal
  113. references)::
  114. tracer.start_span('...')
  115. Starting a child :class:`Span` (see also :meth:`start_child_span()`)::
  116. tracer.start_span(
  117. '...',
  118. child_of=parent_span)
  119. Starting a child :class:`Span` in a more verbose way::
  120. tracer.start_span(
  121. '...',
  122. references=[opentracing.child_of(parent_span)])
  123. :param operation_name: name of the operation represented by the new
  124. :class:`Span` from the perspective of the current service.
  125. :type operation_name: str
  126. :param child_of: (optional) a :class:`Span` or :class:`SpanContext`
  127. representing the parent in a REFERENCE_CHILD_OF reference. If
  128. specified, the `references` parameter must be omitted.
  129. :type child_of: Span or SpanContext
  130. :param references: (optional) references that identify one or more
  131. parent :class:`SpanContext`\\ s. (See the Reference documentation
  132. for detail).
  133. :type references: :obj:`list` of :class:`Reference`
  134. :param tags: an optional dictionary of :class:`Span` tags. The caller
  135. gives up ownership of that dictionary, because the :class:`Tracer`
  136. may use it as-is to avoid extra data copying.
  137. :type tags: dict
  138. :param start_time: an explicit Span start time as a unix timestamp per
  139. :meth:`time.time()`
  140. :type start_time: float
  141. :param ignore_active_span: an explicit flag that ignores the current
  142. active :class:`Scope` and creates a root :class:`Span`.
  143. :type ignore_active_span: bool
  144. :rtype: Span
  145. :return: an already-started :class:`Span` instance.
  146. """
  147. return self._noop_span
  148. def inject(self, span_context, format, carrier):
  149. """Injects `span_context` into `carrier`.
  150. The type of `carrier` is determined by `format`. See the
  151. :class:`Format` class/namespace for the built-in OpenTracing formats.
  152. Implementations *must* raise :exc:`UnsupportedFormatException` if
  153. `format` is unknown or disallowed.
  154. :param span_context: the :class:`SpanContext` instance to inject
  155. :type span_context: SpanContext
  156. :param format: a python object instance that represents a given
  157. carrier format. `format` may be of any type, and `format` equality
  158. is defined by python ``==`` equality.
  159. :type format: Format
  160. :param carrier: the format-specific carrier object to inject into
  161. """
  162. if format in Tracer._supported_formats:
  163. return
  164. raise UnsupportedFormatException(format)
  165. def extract(self, format, carrier):
  166. """Returns a :class:`SpanContext` instance extracted from a `carrier` of the
  167. given `format`, or ``None`` if no such :class:`SpanContext` could be
  168. found.
  169. The type of `carrier` is determined by `format`. See the
  170. :class:`Format` class/namespace for the built-in OpenTracing formats.
  171. Implementations *must* raise :exc:`UnsupportedFormatException` if
  172. `format` is unknown or disallowed.
  173. Implementations may raise :exc:`InvalidCarrierException`,
  174. :exc:`SpanContextCorruptedException`, or implementation-specific errors
  175. if there are problems with `carrier`.
  176. :param format: a python object instance that represents a given
  177. carrier format. `format` may be of any type, and `format` equality
  178. is defined by python ``==`` equality.
  179. :param carrier: the format-specific carrier object to extract from
  180. :rtype: SpanContext
  181. :return: a :class:`SpanContext` extracted from `carrier` or ``None`` if
  182. no such :class:`SpanContext` could be found.
  183. """
  184. if format in Tracer._supported_formats:
  185. return self._noop_span_context
  186. raise UnsupportedFormatException(format)
  187. class ReferenceType(object):
  188. """A namespace for OpenTracing reference types.
  189. See http://opentracing.io/spec for more detail about references,
  190. reference types, and CHILD_OF and FOLLOWS_FROM in particular.
  191. """
  192. CHILD_OF = 'child_of'
  193. FOLLOWS_FROM = 'follows_from'
  194. # We use namedtuple since references are meant to be immutable.
  195. # We subclass it to expose a standard docstring.
  196. class Reference(namedtuple('Reference', ['type', 'referenced_context'])):
  197. """A Reference pairs a reference type with a referenced :class:`SpanContext`.
  198. References are used by :meth:`Tracer.start_span()` to describe the
  199. relationships between :class:`Span`\\ s.
  200. :class:`Tracer` implementations must ignore references where
  201. referenced_context is ``None``. This behavior allows for simpler code when
  202. an inbound RPC request contains no tracing information and as a result
  203. :meth:`Tracer.extract()` returns ``None``::
  204. parent_ref = tracer.extract(opentracing.HTTP_HEADERS, request.headers)
  205. span = tracer.start_span(
  206. 'operation', references=child_of(parent_ref)
  207. )
  208. See :meth:`child_of` and :meth:`follows_from` helpers for creating these
  209. references.
  210. """
  211. pass
  212. def child_of(referenced_context=None):
  213. """child_of is a helper that creates CHILD_OF References.
  214. :param referenced_context: the (causal parent) :class:`SpanContext` to
  215. reference. If ``None`` is passed, this reference must be ignored by
  216. the :class:`Tracer`.
  217. :type referenced_context: SpanContext
  218. :rtype: Reference
  219. :return: A reference suitable for ``Tracer.start_span(...,
  220. references=...)``
  221. """
  222. return Reference(
  223. type=ReferenceType.CHILD_OF,
  224. referenced_context=referenced_context)
  225. def follows_from(referenced_context=None):
  226. """follows_from is a helper that creates FOLLOWS_FROM References.
  227. :param referenced_context: the (causal parent) :class:`SpanContext` to
  228. reference. If ``None`` is passed, this reference must be ignored by the
  229. :class:`Tracer`.
  230. :type referenced_context: SpanContext
  231. :rtype: Reference
  232. :return: A Reference suitable for ``Tracer.start_span(...,
  233. references=...)``
  234. """
  235. return Reference(
  236. type=ReferenceType.FOLLOWS_FROM,
  237. referenced_context=referenced_context)
  238. def start_child_span(parent_span, operation_name, tags=None, start_time=None):
  239. """A shorthand method that starts a `child_of` :class:`Span` for a given
  240. parent :class:`Span`.
  241. Equivalent to calling::
  242. parent_span.tracer().start_span(
  243. operation_name,
  244. references=opentracing.child_of(parent_span.context),
  245. tags=tags,
  246. start_time=start_time)
  247. :param parent_span: the :class:`Span` which will act as the parent in the
  248. returned :class:`Span`\\ s child_of reference.
  249. :type parent_span: Span
  250. :param operation_name: the operation name for the child :class:`Span`
  251. instance
  252. :type operation_name: str
  253. :param tags: optional dict of :class:`Span` tags. The caller gives up
  254. ownership of that dict, because the :class:`Tracer` may use it as-is to
  255. avoid extra data copying.
  256. :type tags: dict
  257. :param start_time: an explicit :class:`Span` start time as a unix timestamp
  258. per :meth:`time.time()`.
  259. :type start_time: float
  260. :rtype: Span
  261. :return: an already-started :class:`Span` instance.
  262. """
  263. return parent_span.tracer.start_span(
  264. operation_name=operation_name,
  265. child_of=parent_span,
  266. tags=tags,
  267. start_time=start_time
  268. )