question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Integration with fastapi

See original GitHub issue

I’ve created my own FastAPI middleware. I figured it could be useful for others and it would be great to maintain it in the contrib library here.

Implementation of opencensus.ext.fastapi

import logging

from fastapi import Request
from opencensus.trace import (
    attributes_helper,
    execution_context,
    print_exporter,
    samplers,
)
from opencensus.trace import span as span_module
from opencensus.trace import tracer as tracer_module
from opencensus.trace import utils
from opencensus.trace.propagation import trace_context_http_header_format
from starlette.types import ASGIApp

HTTP_HOST = attributes_helper.COMMON_ATTRIBUTES["HTTP_HOST"]
HTTP_METHOD = attributes_helper.COMMON_ATTRIBUTES["HTTP_METHOD"]
HTTP_PATH = attributes_helper.COMMON_ATTRIBUTES["HTTP_PATH"]
HTTP_ROUTE = attributes_helper.COMMON_ATTRIBUTES["HTTP_ROUTE"]
HTTP_URL = attributes_helper.COMMON_ATTRIBUTES["HTTP_URL"]
HTTP_STATUS_CODE = attributes_helper.COMMON_ATTRIBUTES["HTTP_STATUS_CODE"]

module_logger = logging.getLogger(__name__)


class FastAPIMiddleware:
    def __init__(
        self,
        app: ASGIApp,
        excludelist_paths=None,
        excludelist_hostnames=None,
        sampler=None,
        exporter=None,
        propagator=None,
    ) -> None:
        self.app = app
        self.excludelist_paths = excludelist_paths
        self.excludelist_hostnames = excludelist_hostnames
        self.sampler = sampler or samplers.AlwaysOnSampler()
        self.exporter = exporter or print_exporter.PrintExporter()
        self.propagator = (
            propagator or trace_context_http_header_format.TraceContextPropagator()
        )

    async def __call__(self, request: Request, call_next):

        # Do not trace if the url is in the exclude list
        if utils.disable_tracing_url(str(request.url), self.excludelist_paths):
            return await call_next(request)

        try:
            span_context = self.propagator.from_headers(request.headers)

            tracer = tracer_module.Tracer(
                span_context=span_context,
                sampler=self.sampler,
                exporter=self.exporter,
                propagator=self.propagator,
            )
        except Exception:  # pragma: NO COVER
            module_logger.error("Failed to trace request", exc_info=True)
            return await call_next(request)

        try:
            span = tracer.start_span()
            span.span_kind = span_module.SpanKind.SERVER
            span.name = "[{}]{}".format(request.method, request.url)
            tracer.add_attribute_to_current_span(HTTP_HOST, request.url.hostname)
            tracer.add_attribute_to_current_span(HTTP_METHOD, request.method)
            tracer.add_attribute_to_current_span(HTTP_PATH, request.url.path)
            tracer.add_attribute_to_current_span(HTTP_URL, str(request.url))
            execution_context.set_opencensus_attr(
                "excludelist_hostnames", self.excludelist_hostnames
            )
        except Exception:  # pragma: NO COVER
            module_logger.error("Failed to trace request", exc_info=True)

        response = await call_next(request)
        try:
            tracer.add_attribute_to_current_span(HTTP_STATUS_CODE, response.status_code)
        except Exception:  # pragma: NO COVER
            module_logger.error("Failed to trace response", exc_info=True)
        finally:
            tracer.end_span()
            return response

Minimal example using it

from fastapi import FastAPI
from opencensus.ext.fastapi import FastAPIMiddleware
from opencensus.trace import samplers

app = FastAPI()

app.middleware("http")(FastAPIMiddleware(app, sampler=samplers.AlwaysOnSampler()))


@app.get("/")
def ping():
    return {"message": "pong!"}

Let me know if I should proceed with a PR

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:8
  • Comments:9

github_iconTop GitHub Comments

4reactions
mkrdipcommented, Jan 26, 2022

Source: Azure Monitor - Tracking FastAPI applications. This Azure example is good the simple implementation they have.


@app.middleware("http")
async def middlewareOpencensus(request: Request, call_next):
    tracer = Tracer(exporter=AzureExporter(connection_string=f'InstrumentationKey={APPINSIGHTS_INSTRUMENTATIONKEY}'),sampler=ProbabilitySampler(1.0))
    with tracer.span("main") as span:
        span.span_kind = SpanKind.SERVER

        response = await call_next(request)

        tracer.add_attribute_to_current_span(
            attribute_key=HTTP_STATUS_CODE,
            attribute_value=response.status_code)
        tracer.add_attribute_to_current_span(
            attribute_key=HTTP_URL,
            attribute_value=str(request.url))

    return response

@gautam-ergo, the azure example above has the tracer initiated within the function which initiates a new tracer every time and causes OOM. So, please initiate before the function for your program. A simple example is below.

tracer = Tracer(exporter=AzureExporter(connection_string=f'InstrumentationKey={APPINSIGHTS_INSTRUMENTATIONKEY}'),sampler=ProbabilitySampler(1.0))

@app.middleware("http")
async def middlewareOpencensus(request: Request, call_next):
    with tracer.span("main") as span:
        span.span_kind = SpanKind.SERVER

        response = await call_next(request)

        tracer.add_attribute_to_current_span(
            attribute_key=HTTP_STATUS_CODE,
            attribute_value=response.status_code)
        tracer.add_attribute_to_current_span(
            attribute_key=HTTP_URL,
            attribute_value=str(request.url))

    return response

Hope this helps!

2reactions
NixBikscommented, May 2, 2022

Shortly speaking they seem similar. The azure example is a minimal example of what I provided (from a quick perspective). If you mean “out of memory” then I don’t see how that could be the case.

Read more comments on GitHub >

github_iconTop Results From Across the Web

FastAPI
FastAPI framework, high performance, easy to learn, fast to code, ready for production. ... GraphQL integration with Strawberry and other libraries.
Read more >
Integration with FastAPI — Pony ORM documentation
PonyORM can be used with FastAPI, it integrates with Pydantic and can be used in an async environment, as long as you follow...
Read more >
FastAPI - Sentry Documentation
The FastAPI integration adds support for the FastAPI Framework . Install. Install sentry-sdk from PyPI with the fastapi extra: Bash.
Read more >
High-performing Apps With Python: A FastAPI Tutorial - Toptal
Python's FastAPI framework enables engineers to rapidly build new applications by calling functions such as authentication through the Fast API.
Read more >
FastAPI MongoDB Integration: 5 Easy Steps - Learn - Hevo Data
FastAPI is a lighter, fast Python framework for creating APIs. As the name suggests, “FastAPI” is a high-speed and easy-to-develop API that ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found