README.rst 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. OpenTracing API for Python
  2. ==========================
  3. |GitterChat| |BuildStatus| |PyPI| |ReadTheDocs|
  4. This library is a Python platform API for OpenTracing.
  5. Required Reading
  6. ----------------
  7. In order to understand the Python platform API, one must first be familiar with
  8. the `OpenTracing project <http://opentracing.io>`_ and
  9. `terminology <http://opentracing.io/documentation/pages/spec.html>`_ more
  10. specifically.
  11. Status
  12. ------
  13. In the current version, ``opentracing-python`` provides only the API and a
  14. basic no-op implementation that can be used by instrumentation libraries to
  15. collect and propagate distributed tracing context.
  16. Future versions will include a reference implementation utilizing an
  17. abstract Recorder interface, as well as a
  18. `Zipkin <http://openzipkin.github.io>`_-compatible Tracer.
  19. Usage
  20. -----
  21. The work of instrumentation libraries generally consists of three steps:
  22. 1. When a service receives a new request (over HTTP or some other protocol),
  23. it uses OpenTracing's inject/extract API to continue an active trace, creating a
  24. Span object in the process. If the request does not contain an active trace,
  25. the service starts a new trace and a new *root* Span.
  26. 2. The service needs to store the current Span in some request-local storage,
  27. (called ``Span`` *activation*) where it can be retrieved from when a child Span must
  28. be created, e.g. in case of the service making an RPC to another service.
  29. 3. When making outbound calls to another service, the current Span must be
  30. retrieved from request-local storage, a child span must be created (e.g., by
  31. using the ``start_child_span()`` helper), and that child span must be embedded
  32. into the outbound request (e.g., using HTTP headers) via OpenTracing's
  33. inject/extract API.
  34. Below are the code examples for the previously mentioned steps. Implementation
  35. of request-local storage needed for step 2 is specific to the service and/or frameworks /
  36. instrumentation libraries it is using, exposed as a ``ScopeManager`` child contained
  37. as ``Tracer.scope_manager``. See details below.
  38. Inbound request
  39. ^^^^^^^^^^^^^^^
  40. Somewhere in your server's request handler code:
  41. .. code-block:: python
  42. def handle_request(request):
  43. span = before_request(request, opentracing.global_tracer())
  44. # store span in some request-local storage using Tracer.scope_manager,
  45. # using the returned `Scope` as Context Manager to ensure
  46. # `Span` will be cleared and (in this case) `Span.finish()` be called.
  47. with tracer.scope_manager.activate(span, True) as scope:
  48. # actual business logic
  49. handle_request_for_real(request)
  50. def before_request(request, tracer):
  51. span_context = tracer.extract(
  52. format=Format.HTTP_HEADERS,
  53. carrier=request.headers,
  54. )
  55. span = tracer.start_span(
  56. operation_name=request.operation,
  57. child_of(span_context))
  58. span.set_tag('http.url', request.full_url)
  59. remote_ip = request.remote_ip
  60. if remote_ip:
  61. span.set_tag(tags.PEER_HOST_IPV4, remote_ip)
  62. caller_name = request.caller_name
  63. if caller_name:
  64. span.set_tag(tags.PEER_SERVICE, caller_name)
  65. remote_port = request.remote_port
  66. if remote_port:
  67. span.set_tag(tags.PEER_PORT, remote_port)
  68. return span
  69. Outbound request
  70. ----------------
  71. Somewhere in your service that's about to make an outgoing call:
  72. .. code-block:: python
  73. from opentracing import tags
  74. from opentracing.propagation import Format
  75. from opentracing_instrumentation import request_context
  76. # create and serialize a child span and use it as context manager
  77. with before_http_request(
  78. request=out_request,
  79. current_span_extractor=request_context.get_current_span):
  80. # actual call
  81. return urllib2.urlopen(request)
  82. def before_http_request(request, current_span_extractor):
  83. op = request.operation
  84. parent_span = current_span_extractor()
  85. outbound_span = opentracing.global_tracer().start_span(
  86. operation_name=op,
  87. child_of=parent_span
  88. )
  89. outbound_span.set_tag('http.url', request.full_url)
  90. service_name = request.service_name
  91. host, port = request.host_port
  92. if service_name:
  93. outbound_span.set_tag(tags.PEER_SERVICE, service_name)
  94. if host:
  95. outbound_span.set_tag(tags.PEER_HOST_IPV4, host)
  96. if port:
  97. outbound_span.set_tag(tags.PEER_PORT, port)
  98. http_header_carrier = {}
  99. opentracing.global_tracer().inject(
  100. span_context=outbound_span,
  101. format=Format.HTTP_HEADERS,
  102. carrier=http_header_carrier)
  103. for key, value in http_header_carrier.iteritems():
  104. request.add_header(key, value)
  105. return outbound_span
  106. Scope and within-process propagation
  107. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  108. For getting/setting the current active ``Span`` in the used request-local storage,
  109. OpenTracing requires that every ``Tracer`` contains a ``ScopeManager`` that grants
  110. access to the active ``Span`` through a ``Scope``. Any ``Span`` may be transferred to
  111. another task or thread, but not ``Scope``.
  112. .. code-block:: python
  113. # Access to the active span is straightforward.
  114. scope = tracer.scope_manager.active()
  115. if scope is not None:
  116. scope.span.set_tag('...', '...')
  117. The common case starts a ``Scope`` that's automatically registered for intra-process
  118. propagation via ``ScopeManager``.
  119. Note that ``start_active_span('...')`` automatically finishes the span on ``Scope.close()``
  120. (``start_active_span('...', finish_on_close=False)`` does not finish it, in contrast).
  121. .. code-block:: python
  122. # Manual activation of the Span.
  123. span = tracer.start_span(operation_name='someWork')
  124. with tracer.scope_manager.activate(span, True) as scope:
  125. # Do things.
  126. # Automatic activation of the Span.
  127. # finish_on_close is a required parameter.
  128. with tracer.start_active_span('someWork', finish_on_close=True) as scope:
  129. # Do things.
  130. # Handling done through a try construct:
  131. span = tracer.start_span(operation_name='someWork')
  132. scope = tracer.scope_manager.activate(span, True)
  133. try:
  134. # Do things.
  135. except Exception as e:
  136. scope.set_tag('error', '...')
  137. finally:
  138. scope.finish()
  139. **If there is a Scope, it will act as the parent to any newly started Span** unless
  140. the programmer passes ``ignore_active_span=True`` at ``start_span()``/``start_active_span()``
  141. time or specified parent context explicitly:
  142. .. code-block:: python
  143. scope = tracer.start_active_span('someWork', ignore_active_span=True)
  144. Each service/framework ought to provide a specific ``ScopeManager`` implementation
  145. that relies on their own request-local storage (thread-local storage, or coroutine-based storage
  146. for asynchronous frameworks, for example).
  147. Scope managers
  148. ^^^^^^^^^^^^^^
  149. This project includes a set of ``ScopeManager`` implementations under the ``opentracing.scope_managers`` submodule, which can be imported on demand:
  150. .. code-block:: python
  151. from opentracing.scope_managers import ThreadLocalScopeManager
  152. There exist implementations for ``thread-local`` (the default instance of the submodule ``opentracing.scope_managers``), ``gevent``, ``Tornado`` and ``asyncio``:
  153. .. code-block:: python
  154. from opentracing.scope_managers.gevent import GeventScopeManager # requires gevent
  155. from opentracing.scope_managers.tornado import TornadoScopeManager # requires tornado<6
  156. from opentracing.scope_managers.asyncio import AsyncioScopeManager # requires Python 3.4 or newer.
  157. Development
  158. -----------
  159. Tests
  160. ^^^^^
  161. .. code-block:: sh
  162. virtualenv env
  163. . ./env/bin/activate
  164. make bootstrap
  165. make test
  166. You can use `tox <https://tox.readthedocs.io>`_ to run tests as well.
  167. .. code-block:: sh
  168. tox
  169. Testbed suite
  170. ^^^^^^^^^^^^^
  171. A testbed suite designed to test API changes and experimental features is included under the *testbed* directory. For more information, see the `Testbed README <testbed/README.md>`_.
  172. Instrumentation Tests
  173. ---------------------
  174. This project has a working design of interfaces for the OpenTracing API. There is a MockTracer to
  175. facilitate unit-testing of OpenTracing Python instrumentation.
  176. .. code-block:: python
  177. from opentracing.mocktracer import MockTracer
  178. tracer = MockTracer()
  179. with tracer.start_span('someWork') as span:
  180. pass
  181. spans = tracer.finished_spans()
  182. someWorkSpan = spans[0]
  183. Documentation
  184. ^^^^^^^^^^^^^
  185. .. code-block:: sh
  186. virtualenv env
  187. . ./env/bin/activate
  188. make bootstrap
  189. make docs
  190. The documentation is written to *docs/_build/html*.
  191. LICENSE
  192. ^^^^^^^
  193. `Apache 2.0 License <./LICENSE>`__.
  194. Releases
  195. ^^^^^^^^
  196. Before new release, add a summary of changes since last version to CHANGELOG.rst
  197. .. code-block:: sh
  198. pip install zest.releaser[recommended]
  199. prerelease
  200. release
  201. git push origin master --follow-tags
  202. python setup.py sdist upload -r pypi upload_docs -r pypi
  203. postrelease
  204. git push
  205. .. |GitterChat| image:: http://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg
  206. :target: https://gitter.im/opentracing/public
  207. .. |BuildStatus| image:: https://travis-ci.org/opentracing/opentracing-python.svg?branch-master
  208. :target: https://travis-ci.org/opentracing/opentracing-python
  209. .. |PyPI| image:: https://badge.fury.io/py/opentracing.svg
  210. :target: https://badge.fury.io/py/opentracing
  211. .. |ReadTheDocs| image:: http://readthedocs.org/projects/opentracing-python/badge/?version=latest
  212. :target: https://opentracing-python.readthedocs.io/en/latest/?badge=latest
  213. :alt: Documentation Status