Skip to main content

Zipkin integration for ASGI frameworks.

Project description

AioZipkin middleware for Starlette/FastApi

Test Coverage Package version

TODO: test coverage

Features

  • Using sentry-asgi as a boilerplate
  • Client - based on aiozipkin - async compatible zipkin library
  • Server (any zipkin 2.0 compatible server will work) - Jaeger examples
  • Middleware tracing http traffic
  • Injecting tracing headers to responses
  • Extracting tracing headers from requests
  • Context variable with the span for every incoming request - possible to instrument tracing of lower level operations

Quick start

Run tracing server

Jaeger all-in-one

Follow instructions at https://www.jaegertracing.io/docs/1.13/getting-started/

$ docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
  -p 5775:5775/udp \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 14268:14268 \
  -p 9411:9411 \
  jaegertracing/all-in-one:latest

Trace queries at http://localhost:16686/

Add middleware

import uvicorn
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route

from starlette_zipkin import ZipkinMiddleware

routes = [
    Route("/", JSONResponse({"status": "OK"})),
]

app = Starlette(debug=True, routes=routes)

app.add_middleware(ZipkinMiddleware)

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000, log_level="info", reload=True)

By default the client emits to http://localhost:9411.

All traffic is captured and available at http://localhost:16686/

jaeger

Advanced Tutorial

To instrument tracing at lower levels, two helper functions are available:

  • get_root_span - returns the span instance corresponding to current request
  • get_tracer - returns the tracer instance corresponding to current request
import json
import asyncio
import uvicorn
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route

from starlette_zipkin import (
    ZipkinMiddleware,
    ZipkinConfig,
    get_root_span,
    get_tracer,
    B3Headers,
    UberHeaders
)


async def homepage(request):
    root_span = get_root_span()
    tracer = get_tracer()

    with tracer.new_child(root_span.context) as child_span:
        # ! if headers not explicitly provided,\
        # root span from middleware injects headers
        # and becomes the parent for subsequet services
        headers = child_span.context.make_headers()
        child_span.name("NewParent")
        child_span.annotate(
            "Child, sleeps for 1, injects headers and becomes parent"
        )
        await asyncio.sleep(1)
        return JSONResponse({"hello": "world"}, headers=headers)


routes = [
    Route("/", JSONResponse({"status": "OK"})),
    Route("/homepage", homepage),
]

app = Starlette(debug=True, routes=routes)

config = ZipkinConfig(
    host="localhost",
    port=9411,
    service_name="service_name",
    sample_rate=1.0,
    inject_response_headers=True,
    force_new_trace=False,
    json_encoder=json.dumps,
    header_formatter=B3Headers
)
app.add_middleware(ZipkinMiddleware, config=config)

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000, log_level="info", reload=True)

This way we are able to followup at the call from a different service. Here we use the same server, but pass the tracing headers to subsequent calls to demonstrate future spans:

Step by step example:

  1. client sends

    GET /homepage HTTP/1.1
    Host: localhost:8000
    User-Agent: PostmanRuntime/7.15.2
    Accept: */*
    Cache-Control: no-cache
    Postman-Token: 519bda7e-bb9c-40c4-a9a5-c8df5524ced2,189c4252-322a-415d-a637-ecdca9a79cb0
    Host: localhost:8000
    Accept-Encoding: gzip, deflate
    Connection: keep-alive
    cache-control: no-cache
    

    Server responds

    X-B3-TraceId: ddfc5b2181e08d3360e4072522c5235a
    X-B3-SpanId: 34dcd9a29c01efe2
    X-B3-Flags: 0
    X-B3-Sampled: 1
    x-b3-parentspanid: b9872416ce86e870
    
    {"hello":"world"}
    
  2. client follows up on first trace by passing the context from headers

    GET /homepage HTTP/1.1
    Host: localhost:8000
    X-B3-TraceId: ddfc5b2181e08d3360e4072522c5235a
    X-B3-SpanId: 34dcd9a29c01efe2
    X-B3-Flags: 0
    X-B3-Sampled: 1
    x-b3-parentspanid: b9872416ce86e870
    User-Agent: PostmanRuntime/7.15.2
    Accept: */*
    Cache-Control: no-cache
    Postman-Token: 2eb6d43a-ed2c-4291-b0c4-c41335e40f6b,bd6376b5-4ab9-45bd-91ab-10f4831547e7
    Host: localhost:8000
    Accept-Encoding: gzip, deflate
    Connection: keep-alive
    cache-control: no-cache
    

    Server responds (again with a new set of optional tracing ids)

    X-B3-TraceId: ddfc5b2181e08d3360e4072522c5235a
    X-B3-SpanId: 3c550de9d7cb62aa
    X-B3-Flags: 0
    X-B3-Sampled: 1
    x-b3-parentspanid: ecb56ce4eba6aed5
    
    {
    "hello": "world"
    }
    

Both calls are collected by Jaeger and available in WebUI

Configuration

To change the middleware configuration, provide a config object (here with default values being as shown)

import json
from starlette_zipkin import ZipkinMiddleware, ZipkinConfig, B3Headers

config = ZipkinConfig(
    host="localhost",
    port=9411,
    service_name="service_name",
    sample_rate=1.0,
    inject_response_headers=True,
    force_new_trace=False,
    json_encoder=json.dumps,
    header_formatter=B3Headers
)

app = Starlette()

app.add_middleware(ZipkinMiddleware, config=config)

where:

  • host = "localhost"
    • default local host, needs to be set to point at the agent that collects traces (e.g. jaeger-agent)
  • port = 9411
    • default port, needs to be set to point at the agent that collects traces (e.g. jaeger-agent)
    • 9411 is default for zipkin client/agent (and jaeger-agent)
    • make sure to make accessible
  • service_name = "service_name"
    • name of the service
  • sample_rate = 1.0
    • zipkin sampling rate, default samples every call
  • inject_response_headers = True
    • automatically inject response headers
  • force_new_trace = False
    • if True, does not create child traces if incoming request contains tracing headers
  • json_encoder=json.dumps
    • json encoder can be provided, defaults to json dumps. It is used to format dictionaries for Jaeger UI.
  • header_formatter=B3Headers
    • defaults to b3 headers format. Can be switched to UberHeaders, which imply the uber-trace-id format.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

starlette-zipkin-0.1.0.tar.gz (11.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

starlette_zipkin-0.1.0-py3-none-any.whl (9.4 kB view details)

Uploaded Python 3

File details

Details for the file starlette-zipkin-0.1.0.tar.gz.

File metadata

  • Download URL: starlette-zipkin-0.1.0.tar.gz
  • Upload date:
  • Size: 11.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/50.3.1 requests-toolbelt/0.9.1 tqdm/4.53.0 CPython/3.9.0

File hashes

Hashes for starlette-zipkin-0.1.0.tar.gz
Algorithm Hash digest
SHA256 616f29ed36fdfdd126eafa4d462253bdf99d4a590ebe244f8e2eb4cc3b116edf
MD5 db928125e0dec3bcea3517bd6305dc2a
BLAKE2b-256 f85a3940121284d343b9f8262a05d4c6ea780ccb644f3b7b6c962dc545c83653

See more details on using hashes here.

File details

Details for the file starlette_zipkin-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: starlette_zipkin-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 9.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/50.3.1 requests-toolbelt/0.9.1 tqdm/4.53.0 CPython/3.9.0

File hashes

Hashes for starlette_zipkin-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 62b18937b304cd6c78d94ba284b2432f40a906a1f92215388a8ad98adbd5c207
MD5 083d5bbe3f4b43f7bd1cad417da2ec19
BLAKE2b-256 06eaccf211fe5a5e970ee3edc74a4d7ac4d350ee668b60dda4d15c1c38820b49

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page