otel-extensions-python: OpenTelemetry Extensions for Python
OpenTelemetry Extensions for Python is a collection of helper classes, functions, and decorators to facilitate the use of the
OpenTelemetry Python API & SDK packages
Version Support
Python >= 3.6
Installation
pip install
You can install through pip using:
pip install otel-extensions
(you may need to run pip
with root permission: sudo pip install otel-extensions
)
Setuptools
Install via Setuptools.
python setup.py install --user
(or sudo python setup.py install
to install the package for all users)
Features
Tracer Provider Initialization
from otel_extensions import init_telemetry_provider, TelemetryOptions
options = TelemetryOptions(
OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317/",
OTEL_EXPORTER_OTLP_CERTIFICATE="/path/to/ca/bundle",
OTEL_EXPORTER_OTLP_PROTOCOL="grpc",
OTEL_EXPORTER_CUSTOM_SPAN_EXPORTER_TYPE="pkg.ClassName",
OTEL_SERVICE_NAME="My Service",
OTEL_PROCESSOR_TYPE="batch",
TRACEPARENT="001233454656...."
)
init_telemetry_provider(options)
Instrumentation Decorator
You can use the @instrumented
decorator to automatically wrap a span around a function or method.
(As of version 0.2.0, the decorator can support coroutine functions defined as async def
as well as normal functions)
from otel_extensions import init_telemetry_provider, instrumented
import asyncio
async def main():
foo()
await async_foo()
@instrumented
def foo():
"""Creates a span named 'foo'"""
bar()
@instrumented(span_name="custom span name")
def bar():
"""Creates a span named 'custom span name'"""
print("Hello World")
@instrumented(span_attributes={"attr1": "val1", "attr2": "val2"})
def fn_with_attrs():
"""Creates a span named 'fn_with_attrs' and sets key/value pairs
from `span_attributes` as span attributes"""
print("Hello World")
@instrumented
async def async_foo():
"""Creates a span named 'async_foo'"""
await async_bar()
@instrumented(span_name="custom span name")
async def async_bar():
"""Creates a span named 'custom span name'"""
print("Hello World")
@instrumented(span_name="custom span name")
async def async_bar():
"""Creates a span named 'custom span name'"""
print("Hello World")
@instrumented(span_attributes={"attr1": "val1", "attr2": "val2"})
async def async_fn_with_attrs():
"""Creates a span named 'async_fn_with_attrs' and sets key/value pairs
from `span_attributes` as span attributes"""
print("Hello World")
if __name__ == '__main__':
init_telemetry_provider()
asyncio.run(main())
Conditional span creation
If the OTEL_PROCESS_MODULES environment variable is set, the @instrumented
decorator will only create a span if the module of the decorated function is in the list of modules specified in the environment variable.
import os
from otel_extensions import instrumented
os.environ["OTEL_PROCESS_MODULES"] = "module1,module2"
@instrumented
def foo():
"""
Would create a span named 'foo', but only if the module of this function
were named 'module1' or 'module2'
"""
bar()
Trace Context helper class
The TraceContextCarrier
class is useful when propagating context across process or thread boundaries
from otel_extensions import TraceContextCarrier
from threading import Thread
def main_program():
...
ctx = TraceContextCarrier()
thread = Thread(thread_func, args=(ctx))
thread.start()
...
def thread_func(ctx: TraceContextCarrier):
ctx.attach()
...
Also, the TraceContextCarrier
class can attach to context stored in the TRACEPARENT
environment variable.
Note that this is done automatically when calling the init_telemetry_provider()
function.
from otel_extensions import TraceContextCarrier
TraceContextCarrier.attach_from_env()
TraceContextCarrier
can also inject the current context into the TRACEPARENT
environment variable.
This is useful for context propagation when using Popen
to create a subprocess
from otel_extensions import TraceContextCarrier
from subprocess import Popen
TraceContextCarrier.inject_to_env()
process = Popen(...)
Log messages as events
The TraceEventLogHandler
class is a logging.Handler
class that creates events for any log message that occurs in a span.
from otel_extensions import TraceEventLogHandler, init_telemetry_provider, get_tracer
import logging
init_telemetry_provider()
logging.basicConfig()
logging.getLogger(__name__).addHandler(TraceEventLogHandler())
with get_tracer(__name__).start_as_current_span("foo") as span:
logging.getLogger(__name__).warning("Some log message")