open-telemetry / open-telemetry/opentelemetry-python-contrib

FastAPI spans names don't include path on 3xx or 4xx responses

Open
#921 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
1.1k
Forks
1.1k
Avg merge
4d 15h
Merged PRs (30d)
16

Description

Describe your environment

  • Python 3.9.6
  • Opentelemetry libraries:
opentelemetry-api==1.9.1
opentelemetry-exporter-jaeger-thrift==1.9.1
opentelemetry-exporter-otlp==1.9.1
opentelemetry-exporter-otlp-proto-grpc==1.9.1
opentelemetry-exporter-otlp-proto-http==1.9.1
opentelemetry-instrumentation==0.28b1
opentelemetry-instrumentation-asgi==0.28b1
opentelemetry-instrumentation-fastapi==0.28b1
opentelemetry-propagator-b3==1.9.1
opentelemetry-proto==1.9.1
opentelemetry-sdk==1.9.1
opentelemetry-semantic-conventions==0.28b1
opentelemetry-util-http==0.28b1

Steps to reproduce

Install the following libraries

pip install fastapi uvicorn "opentelemetry-api>=1.6.2" "opentelemetry-sdk>=1.6.2" "opentelemetry-propagator-b3" "opentelemetry-exporter-jaeger-thrift" "opentelemetry-exporter-otlp" "opentelemetry-instrumentation"

Copy/paste the following code to app.py

import fastapi

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
    BatchSpanProcessor,
    ConsoleSpanExporter
)


app = fastapi.FastAPI()

@app.get("/foobar/")
async def foobar():
    return {"message": "hello world"}



provider = TracerProvider(
    resource=Resource.create(
        attributes={
            "service.name": 'my test app',
            "telemetry.auto.version": 0.1,
        }
    )
)
batch_span_processor = BatchSpanProcessor(ConsoleSpanExporter())
provider.add_span_processor(batch_span_processor)

FastAPIInstrumentor.instrument_app(app, tracer_provider=provider)

Run the application

uvicorn --host 0.0.0.0 --port 1234 --reload app:app 

Make the following cURL requests to the application

# Correct HTTP method and path; expect an HTTP 200
curl -v -X GET http://localhost:1234/foobar/

# Correct HTTP method but missing the trailing slash; expect an HTTP 307
curl -v -X GET http://localhost:1234/foobar

# Correct path, but incorrect HTTP method; expect an HTTP 405
curl -v -X POST http://localhost:1234/foobar/

What is the expected behavior?

For each request above, I expect to see a named span with the path being called. So spans of /foobar/, /foobar, and /foobar/ respectively.

What is the actual behavior?
I see spans named /foobar/, GET, and POST (all verified from the console logs. The aforementioned span names appear as SpanKind.SERVER spans)

Additional context
The last two requests appear to be named by method because of this code. Namely, if the route being called isn't matched by Starlette, we opt to name the span using the HTTP method rather than scope['path'].

I'd argue we want to report the span name as scope['path'] when there is not a match. Grouping all 307, 404, and 405 use-cases into a span named GET muddies the data.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the behavior with the app.py example and the three curl requests, then inspect instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/init.py around line 220. Done means unmatched 3xx and 4xx requests produce spans named from the request path rather than only GET or POST.

Written by the indexing model from the issue text.

Assessment

Tech stack
fastapi, python
Domain
observability
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.