Python Tracing SDK
Tracing with Instana is automatic but if you want even more visibility into custom code, a specific application area, or some in-house component, you can use the Python Tracing SDK of Instana.
- Instana Python Tracing SDK
- Asynchronous tracing
- Tracing spans in forked processes
- Carrying context into new threads
- Tracing jobs scheduled to run later
- Adding custom tags to Instana spans
- Related information
Instana Python Tracing SDK
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 asyncio
,
you can use 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 then add a time period after span.finish(); for example, as follows:
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)))