Skip to content

ASGI middleware sets server span status code only if inner spans are recording #2087

@bastbu

Description

@bastbu

Describe your environment

  • Python 3.11.7
  • Version 0.42b0 of opentelemetry-instrumentation, opentelemetry-instrumentation-asgi and opentelemetry-instrumentation-fastapi

Steps to reproduce

When dropping "http send" spans tracked by the ASGI middleware by using the following sampler:

class DropSampler(StaticSampler):
    def __init__(self, decision: Decision):
        super().__init__(decision)

    def should_sample(
        self,
        parent_context,
        trace_id,
        name,
        kind = None,
        attributes = None,
        links = None,
        trace_state = None,
    ) -> Any:
        if "http send" in name.lower():
            return SamplingResult(decision=Decision.DROP)

        return super().should_sample(
            parent_context,
            trace_id,
            name,
            kind,
            attributes,
            links,
            trace_state,
        )

it exports the following trace as the server span:

{
    "name": "GET /metrics",
    "context": {
        "trace_id": "0x1d9414309f3fd3008fef261573d7b7b4",
        "span_id": "0x3d77a38eb9e6405a",
        "trace_state": "[]"
    },
    "kind": "SpanKind.SERVER",
    "parent_id": null,
    "start_time": "2023-12-13T13:08:41.458169Z",
    "end_time": "2023-12-13T13:08:41.460419Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "http.scheme": "http",
        "http.host": "127.0.0.1:5000",
        "net.host.port": 5000,
        "http.flavor": "1.1",
        "http.target": "/metrics",
        "http.url": "http://127.0.0.1:5000/metrics",
        "http.method": "GET",
        "http.server_name": "localhost:5000",
        "http.user_agent": "curl/7.88.1",
        "net.peer.ip": "127.0.0.1",
        "net.peer.port": 50818,
        "http.route": "/metrics"
    },
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "service.name": "some_service"
        },
        "schema_url": ""
    }
}

However, if we do not drop any HTTP send spans, the server span contains the HTTP status code:

{
    "name": "GET /metrics",
    "context": {
        "trace_id": "0x27fc3b66d067ed4ba043668991074e9d",
        "span_id": "0x8a0f4218411adec8",
        "trace_state": "[]"
    },
    "kind": "SpanKind.SERVER",
    "parent_id": null,
    "start_time": "2023-12-13T13:12:53.385801Z",
    "end_time": "2023-12-13T13:12:53.387893Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "http.scheme": "http",
        "http.host": "127.0.0.1:5000",
        "net.host.port": 5000,
        "http.flavor": "1.1",
        "http.target": "/metrics",
        "http.url": "http://127.0.0.1:5000/metrics",
        "http.method": "GET",
        "http.server_name": "localhost:5000",
        "http.user_agent": "curl/7.88.1",
        "net.peer.ip": "127.0.0.1",
        "net.peer.port": 59612,
        "http.route": "/metrics",
        "http.status_code": 200
    },
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "service.name": "some_service"
        },
        "schema_url": ""
    }
}

Note the absence/presence of the http.status_code attribute.

What is the expected behavior?

The http.status_code attribute should be present on the server span independently of whether a child span is sampled or not.

What is the actual behavior?

The http.status_code attribute is only present on the server span if the "http send" child span is sampled.

Additional context

This is likely a consequence of the following:

https://github.com/open-telemetry/opentelemetry-python-contrib/blob/4bf3577fb76480dacca12eecd8ed93cb9fccc274/instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py#L668-L674C23

The set_status_code(server_span, status_code) is only invoked if send_span.is_recording(), whereas it should be invoked if the server_span itself is recording.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions