Skip to content

Commit 4c813c4

Browse files
authored
ASGI: Conditionally create SERVER spans (#843)
1 parent 3de2986 commit 4c813c4

File tree

3 files changed

+80
-15
lines changed

3 files changed

+80
-15
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717
- `opentelemetry-instrumentation-flask` Flask: Conditionally create SERVER spans
1818
([#828](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/828))
1919

20+
- `opentelemetry-instrumentation-asgi` ASGI: Conditionally create SERVER spans
21+
([#843](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/843))
22+
23+
2024
## [1.8.0-0.27b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.8.0-0.27b0) - 2021-12-17
2125

2226
### Added

instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/__init__.py

+30-15
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
# pylint: disable=too-many-locals
1415

1516
"""
1617
The opentelemetry-instrumentation-asgi package provides an ASGI middleware that can be used
@@ -110,7 +111,12 @@ def client_response_hook(span: Span, message: dict):
110111
from opentelemetry.propagate import extract
111112
from opentelemetry.propagators.textmap import Getter, Setter
112113
from opentelemetry.semconv.trace import SpanAttributes
113-
from opentelemetry.trace import Span, set_span_in_context
114+
from opentelemetry.trace import (
115+
INVALID_SPAN,
116+
Span,
117+
SpanKind,
118+
set_span_in_context,
119+
)
114120
from opentelemetry.trace.status import Status, StatusCode
115121
from opentelemetry.util.http import remove_url_credentials
116122

@@ -321,39 +327,48 @@ async def __call__(self, scope, receive, send):
321327
if self.excluded_urls and self.excluded_urls.url_disabled(url):
322328
return await self.app(scope, receive, send)
323329

324-
token = context.attach(extract(scope, getter=asgi_getter))
325-
server_span_name, additional_attributes = self.default_span_details(
326-
scope
327-
)
330+
token = ctx = span_kind = None
331+
332+
if trace.get_current_span() is INVALID_SPAN:
333+
ctx = extract(scope, getter=asgi_getter)
334+
token = context.attach(ctx)
335+
span_kind = SpanKind.SERVER
336+
else:
337+
ctx = context.get_current()
338+
span_kind = SpanKind.INTERNAL
339+
340+
span_name, additional_attributes = self.default_span_details(scope)
328341

329342
try:
330343
with self.tracer.start_as_current_span(
331-
server_span_name,
332-
kind=trace.SpanKind.SERVER,
333-
) as server_span:
334-
if server_span.is_recording():
344+
span_name,
345+
context=ctx,
346+
kind=span_kind,
347+
) as current_span:
348+
if current_span.is_recording():
335349
attributes = collect_request_attributes(scope)
336350
attributes.update(additional_attributes)
337351
for key, value in attributes.items():
338-
server_span.set_attribute(key, value)
352+
current_span.set_attribute(key, value)
339353

340354
if callable(self.server_request_hook):
341-
self.server_request_hook(server_span, scope)
355+
self.server_request_hook(current_span, scope)
342356

343357
otel_receive = self._get_otel_receive(
344-
server_span_name, scope, receive
358+
span_name, scope, receive
345359
)
346360

347361
otel_send = self._get_otel_send(
348-
server_span,
349-
server_span_name,
362+
current_span,
363+
span_name,
350364
scope,
351365
send,
352366
)
353367

354368
await self.app(scope, otel_receive, otel_send)
355369
finally:
356-
context.detach(token)
370+
if token:
371+
context.detach(token)
357372

358373
def _get_otel_receive(self, server_span_name, scope, receive):
359374
@wraps(receive)

instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py

+46
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from fastapi.testclient import TestClient
2020

2121
import opentelemetry.instrumentation.fastapi as otel_fastapi
22+
from opentelemetry import trace
2223
from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
2324
from opentelemetry.sdk.resources import Resource
2425
from opentelemetry.semconv.trace import SpanAttributes
@@ -329,3 +330,48 @@ def test_instrumentation(self):
329330

330331
should_be_original = fastapi.FastAPI
331332
self.assertIs(original, should_be_original)
333+
334+
335+
class TestWrappedApplication(TestBase):
336+
def setUp(self):
337+
super().setUp()
338+
339+
self.app = fastapi.FastAPI()
340+
341+
@self.app.get("/foobar")
342+
async def _():
343+
return {"message": "hello world"}
344+
345+
otel_fastapi.FastAPIInstrumentor().instrument_app(self.app)
346+
self.client = TestClient(self.app)
347+
self.tracer = self.tracer_provider.get_tracer(__name__)
348+
349+
def tearDown(self) -> None:
350+
super().tearDown()
351+
with self.disable_logging():
352+
otel_fastapi.FastAPIInstrumentor().uninstrument_app(self.app)
353+
354+
def test_mark_span_internal_in_presence_of_span_from_other_framework(self):
355+
with self.tracer.start_as_current_span(
356+
"test", kind=trace.SpanKind.SERVER
357+
) as parent_span:
358+
resp = self.client.get("/foobar")
359+
self.assertEqual(200, resp.status_code)
360+
361+
span_list = self.memory_exporter.get_finished_spans()
362+
for span in span_list:
363+
print(str(span.__class__) + ": " + str(span.__dict__))
364+
365+
# there should be 4 spans - single SERVER "test" and three INTERNAL "FastAPI"
366+
self.assertEqual(trace.SpanKind.INTERNAL, span_list[0].kind)
367+
self.assertEqual(trace.SpanKind.INTERNAL, span_list[1].kind)
368+
# main INTERNAL span - child of test
369+
self.assertEqual(trace.SpanKind.INTERNAL, span_list[2].kind)
370+
self.assertEqual(
371+
parent_span.context.span_id, span_list[2].parent.span_id
372+
)
373+
# SERVER "test"
374+
self.assertEqual(trace.SpanKind.SERVER, span_list[3].kind)
375+
self.assertEqual(
376+
parent_span.context.span_id, span_list[3].context.span_id
377+
)

0 commit comments

Comments
 (0)