Python OpenTracing (deprecated)

OpenTracing is deprecated, and Instana Python 2.5.3 is the latest version that supports OpenTracing.

For information about how to install this version, see Installation of the Instana Python package with OpenTracing support.

OpenTracing provides "Vendor-neutral APIs and instrumentation for distributed tracing". If you're not familiar with OpenTracing, see the OpenTracing portal for more details.

Existing applications that utilize the OpenTracing API or those who wish to add support can easily integrate with this Python package.

When you use this package, the OpenTracing Tracer (opentracing.tracer) is automatically set to the InstanaTracer.

For best results when utilizing OpenTracing, also take note of our best practices outlined in this OpenTracing page.

Installing the Instana Python package with OpenTracing support

You can manually install the Instana Python package for different Python versions.

For runtimes with Python 3.8 and later

To manually install the Instana Python package into a virtualenv, pipenv, or container, run the following command:

pip install instana==2.5.4

Alternatively, you can install the Instana Python package directly from the GitHub repository by running the following command:

pip install git+https://github.com/instana/python-sensor@v2.5.4

For runtimes with Python 3.7

To manually install the Instana Python package into a virtualenv, pipenv, or container, run the following command:

pip install git+https://github.com/instana/python-sensor@v2.4.0

Example

import opentracing

with opentracing.tracer.start_active_span('asteroid 💫') as pscope:
    pscope.span.set_tag(ext.COMPONENT, "Python simple example app")
    pscope.span.set_tag(ext.SPAN_KIND, ext.SPAN_KIND_RPC_SERVER)
    pscope.span.set_tag(ext.PEER_HOSTNAME, "localhost")
    pscope.span.set_tag(ext.HTTP_URL, "/python/simple/one")
    pscope.span.set_tag(ext.HTTP_METHOD, "GET")
    pscope.span.set_tag(ext.HTTP_STATUS_CODE, 200)
    pscope.span.log_kv({"foo": "bar"})
    # ... work ...

    with opentracing.tracer.start_active_span('spacedust 🌚', child_of=pscope.span) as cscope:
        cscope.span.set_tag(ext.SPAN_KIND, ext.SPAN_KIND_RPC_CLIENT)
        cscope.span.set_tag(ext.PEER_HOSTNAME, "localhost")
        cscope.span.set_tag(ext.HTTP_URL, "/python/simple/two")
        cscope.span.set_tag(ext.HTTP_METHOD, "POST")
        cscope.span.set_tag(ext.HTTP_STATUS_CODE, 204)
        cscope.span.set_baggage_item("someBaggage", "someValue")
        # ... work ...

Instana Python Tracing SDK with OpenTracing support

The Instana Python module provides an API to trace any arbitrary part of your application.

You can instrument a section of code for tracing with an API as follows:

from instana.singletons import tracer

try:
  span = tracer.start_span("mywork")
  # The code to be instrumented
  id = user.find_by_name('john.smith')
  span.set_tag('user_id', id)
except Exception as e:
  span.log_exception(e)
finally:
  span.finish()

Alternatively, you can use the context manager with the with-as statement, which automatically captures and logs any exceptions that are raised, as follows:

from instana.singletons import tracer

with tracer.start_active_span("mywork") as scope:
  # The code to be instrumented
  id = user.find_by_name('john.smith')
  scope.span.set_tag('user_id', id)

Asynchronous tracing

Some operations that you want to trace might be asynchronous, which means that they return immediately but still continue to work separately from the main sequence of instructions. To trace these operations, for example, with the asyncio library, you can use the async_tracer related tracing methods as follows:

import asyncio
from instana.singletons import tracer, async_tracer


async def do_work(parent_span):
    with async_tracer.start_active_span('launch_async_work', child_of=parent_span):
        print('Work stared')
        await asyncio.sleep(1)
        print('Work finished!')

with tracer.start_active_span('launch_uvloop') as sync_scope:
    asyncio.run(do_work(sync_scope.span))

Tracing spans in forked processes

If you want to trace spans in a forked process, add a time period after span.finish(). For example:

 def forked_process(num):
    print(f'in forked process {num}')
    try:
        span = tracer.start_span(f"forked process {num}")
        print(f'sleep 10 for forked process {num}')
        time.sleep(10)
    except Exception as e:
        span.log_exception(e)
    finally:
        span.finish()

    logger.warning(f'sleep 2 for forked process {num}')
    time.sleep(2)   ##  < -- with a sleep after span.finish(), the span can be collected

Carrying context into new threads

Tracing is local to a thread. If you create a new thread, the context must be carried to that new thread and then picked up. You can instrument the code to carry context in to new threads as follows:

from threading import Thread

def child_thread_function(parent_span):
    with tracer.start_active_span('child_thread_span', child_of=parent_span) as child_scope:
        print('Thread offloaded work goes here')

with tracer.start_active_span('parent_thread_span') as parent_scope:
    thread = Thread(target = child_thread_function, args = (parent_scope.span, ))
    thread.start()
    thread.join()

Tracing jobs scheduled to run later

You can instrument jobs that are queued to run later as follows:

# Python 3.8
import asyncio
import datetime
import uvloop
import aiohttp

from instana.singletons import tracer, async_tracer

uvloop.install()

async def launch_async_calls(parent_span):
    with async_tracer.start_active_span('launch_async_calls', child_of=parent_span):
        async with aiohttp.ClientSession() as session:
            async with session.get('https://wikipedia.org') as resp:
               print(resp.status)
               print(await resp.text())

async def run_at(dt, coro):
    await asyncio.sleep((dt - datetime.datetime.now()).total_seconds())
    return await coro

with tracer.start_active_span('launch_uvloop') as sync_scope:
    sync_scope.span.set_tag('span.kind', 'entry')
    asyncio.run(
      run_at(datetime.datetime.now() + datetime.timedelta(seconds=5),
             launch_async_calls(sync_scope.span)))

Adding custom tags to Instana spans

To add custom tags to an Instana span, instrument the following code by using the set_tag method:

with tracer.start_active_span('well_chosen_span_name_here') as scope:
    scope.span.set_tag('custom_tag', 'custom_value')

Additional information