Tag Archives: fastapi

Opentelemetry + Jaeger Python Version Cross Service Call Example

Opentelemetry Python version cross service invocation example

When calling opentelemetry across services, you need to import opentelemetry-instrumentation-requests, and then use request to cross service requests. (note that aiohttp-client should be used if it is asynchronous)
I use Jaeger on the server, which is deployed directly through the docker of all-in-one. The code is as follows:

docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
  -p 5775:5775/udp \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 14250:14250 \
  -p 14268:14268 \
  -p 14269:14269 \
  -p 9411:9411 \
  jaegertracing/all-in-one:1.32

After starting Jaeger, visit the following page: http://localhost:16686

Python needs to start two services to demonstrate cross service invocation. The framework I use is fastapi, so I need to install opentelemetry-instrumentation-fastapi
call service 1 by service 2
Service 1:

import fastapi
from opentelemetry import trace
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from fastapi import Body
from pybase.lib.tracer.jaeger_ import register_to_jaeger
from pydantic import BaseModel

app = fastapi.FastAPI()


class UserInfo(BaseModel):
    name: str


tracer = trace.get_tracer(__name__)


@app.post("/server")
async def server(userinfo: str = Body(...), name: str = Body(..., )):
    return {"message": f"hello {userinfo},{name}"}



FastAPIInstrumentor.instrument_app(app)
if __name__ == '__main__':
    register_to_jaeger("fastapi-jaeger-server", "localhost")
    import uvicorn

    uvicorn.run(app,port=8001)

Service 2

import fastapi
import requests
from opentelemetry import trace
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from fastapi import Body
from opentelemetry.instrumentation.requests import RequestsInstrumentor
RequestsInstrumentor().instrument()
from pybase.lib.tracer.jaeger_ import register_to_jaeger
from pydantic import BaseModel

app = fastapi.FastAPI()


class UserInfo(BaseModel):
    name: str


tracer = trace.get_tracer(__name__)


@app.post("/foobar")
async def foobar(userinfo: str = Body(...), name: str = Body(..., )):
    with tracer.start_as_current_span("foo"):
        with tracer.start_as_current_span("bar"):
            with tracer.start_as_current_span("baz"):
                print("Hello world from OpenTelemetry Python!")
    return {"message": f"hello {userinfo},{name}"}
@app.post("/foobar2")
async def foobar2(userinfo: str = Body(...), name: str = Body(..., )):
    return {"message": f"hello {userinfo},{name}"}

@app.post("/client")
def client(userinfo: str = Body(...), name: str = Body(..., )):

    res=requests.post("http://127.0.0.1:8001/server",json={
        "userinfo":userinfo,"name":name
    })
    return res.json()


FastAPIInstrumentor.instrument_app(app)
if __name__ == '__main__':
    register_to_jaeger("fastapi-jaeger", "localhost")
    import uvicorn

    uvicorn.run(app)

A function that depends on register_ to_ jaeger:

from opentelemetry import trace
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor


def register_to_jaeger(service_name: str, jaeger_host: str, jaeger_port: int = 6831):
    """
    Register the service to jaeger so that tracer-related information can be sent to the jaeger server
    Args:
        service_name: the registered service name
        jaeger_host: jaeger address
        jaeger_port:

    Returns: TracerProvider

    """
    provider = TracerProvider(
        resource=Resource.create({SERVICE_NAME: service_name})
    )
    trace.set_tracer_provider(
        provider
    )

    # create a JaegerExporter
    jaeger_exporter = JaegerExporter(
        agent_host_name=jaeger_host,
        agent_port=jaeger_port,
    )

    # Create a BatchSpanProcessor and add the exporter to it
    span_processor = BatchSpanProcessor(jaeger_exporter)

    # add to the tracer
    trace.get_tracer_provider().add_span_processor(span_processor)

Then execute:

curl -X 'POST' \
  'http://127.0.0.1:8000/client' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "userinfo": "string",
  "name": "string"
}'

You can get the following results:
if you have any questions, please correct them.