py-zipkin
py-zipkin is a Python library that provides utilities, including a context manager and decorator, to facilitate the usage of Zipkin for distributed tracing in Python applications. It enables instrumenting code to send trace data to a Zipkin collector. The library is actively maintained with irregular releases, the latest being v1.2.8 released in early 2023.
Common errors
-
requests.exceptions.ConnectionError: ('Connection aborted.', ConnectionRefusedError(111, 'Connection refused'))cause The Zipkin collector is not running or is not accessible at the specified endpoint (e.g., `http://localhost:9411/api/v2/spans`).fixEnsure the Zipkin collector is running and its HTTP API endpoint is correctly configured and reachable from your application. Check firewall rules or container network settings if applicable. Example: `docker run -p 9411:9411 openzipkin/zipkin` -
TypeError: send() takes 2 positional arguments but 3 were given
cause Your custom transport handler is implemented as a function with one argument (`encoded_span`) but `py-zipkin` is calling it expecting a method of a class (which implicitly includes `self` as the first argument). This typically happens if you haven't correctly subclassed `BaseTransportHandler` for your transport.fixRefactor your transport handler to subclass `py_zipkin.transport.BaseTransportHandler` and implement `send(self, encoded_span)` and `get_max_payload_bytes(self)`. -
Error in thread_local context or unexpected trace breakage in asynchronous/multithreaded environments.
cause By default, `py_zipkin` uses thread-local storage for trace attributes, which is not suitable for cooperative multitasking (e.g., `asyncio`) or complex multithreading without explicit context management.fixFor cooperative multitasking, explicitly pass instances of `py_zipkin.storage.Stack` as `context_stack` and `py_zipkin.storage.SpanStorage` as `span_storage` to `zipkin_span` and `create_http_headers_for_new_span`.
Warnings
- deprecated The `py_zipkin.thread_local` module and its functionalities are deprecated. Direct usage should be replaced.
- deprecated Older transport handler implementations that were simple functions taking a single `encoded_span` argument are deprecated.
- gotcha Using `add_sa_binary_annotation` multiple times for the same span in Zipkin V2 span format (which `py-zipkin` predominantly uses) will raise a `ValueError`.
- gotcha The 'Firehose mode' feature is explicitly marked as experimental and may be removed without prior warning.
Install
-
pip install py-zipkin
Imports
- zipkin_span
from py_zipkin.zipkin import zipkin_span
- create_http_headers_for_new_span
from py_zipkin.zipkin import create_http_headers_for_new_span
- ZipkinAttrs
from py_zipkin.zipkin import ZipkinAttrs
- Kind
from py_zipkin.zipkin import Kind
- BaseTransportHandler
from py_zipkin.transport import BaseTransportHandler
- ThreadLocalStack
from py_zipkin.thread_local import ThreadLocalStack
from py_zipkin.storage import ThreadLocalStack
- Stack
from py_zipkin.storage import Stack
- SpanStorage
from py_zipkin.storage import SpanStorage
Quickstart
import requests
import os
from py_zipkin.zipkin import zipkin_span
from py_zipkin.transport import BaseTransportHandler
# Configure your Zipkin collector endpoint (e.g., http://localhost:9411/api/v2/spans)
ZIPKIN_COLLECTOR_ENDPOINT = os.environ.get('ZIPKIN_COLLECTOR_ENDPOINT', 'http://localhost:9411/api/v2/spans')
# A simple HTTP transport handler for demonstration
class HTTPTransportHandler(BaseTransportHandler):
def send(self, encoded_span):
# The collector expects a thrift-encoded list of spans.
try:
requests.post(
ZIPKIN_COLLECTOR_ENDPOINT,
data=encoded_span,
headers={'Content-Type': 'application/x-thrift'},
timeout=5
)
except requests.exceptions.RequestException as e:
print(f"Failed to send span to Zipkin: {e}")
def get_max_payload_bytes(self):
return None # No explicit limit for this example
def do_stuff(a, b):
print(f"Doing stuff with {a} and {b}")
return a + b
# Instantiate the transport handler
my_transport_handler = HTTPTransportHandler()
# Usage as a context manager
def my_function(x, y):
with zipkin_span(
service_name='my_python_service',
span_name='my_function_span',
transport_handler=my_transport_handler,
port=8000, # Optional: Port of the service
sample_rate=100.0 # Sample all traces for demonstration (0.0 - 100.0)
) as zipkin_context:
result = do_stuff(x, y)
zipkin_context.update_binary_annotations({'input_x': x, 'input_y': y, 'result': result})
print(f"Trace for my_function completed with result: {result}")
return result
# Usage as a decorator
@zipkin_span(
service_name='my_python_service',
span_name='decorated_function',
transport_handler=my_transport_handler,
sample_rate=50.0
)
def another_function(data):
print(f"Processing data: {data}")
return data.upper()
if __name__ == "__main__":
print("--- Running context manager example ---")
my_function(10, 20)
print("\n--- Running decorator example ---")
another_function("hello world")
print("Check your Zipkin UI (e.g., http://localhost:9411/zipkin) for traces.")