mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-07-31 22:23:12 +08:00
ASGI: Conditionally create SERVER spans (#843)
This commit is contained in:
@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- `opentelemetry-instrumentation-flask` Flask: Conditionally create SERVER spans
|
- `opentelemetry-instrumentation-flask` Flask: Conditionally create SERVER spans
|
||||||
([#828](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/828))
|
([#828](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/828))
|
||||||
|
|
||||||
|
- `opentelemetry-instrumentation-asgi` ASGI: Conditionally create SERVER spans
|
||||||
|
([#843](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/843))
|
||||||
|
|
||||||
|
|
||||||
## [1.8.0-0.27b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.8.0-0.27b0) - 2021-12-17
|
## [1.8.0-0.27b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.8.0-0.27b0) - 2021-12-17
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
# pylint: disable=too-many-locals
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The opentelemetry-instrumentation-asgi package provides an ASGI middleware that can be used
|
The opentelemetry-instrumentation-asgi package provides an ASGI middleware that can be used
|
||||||
@ -110,7 +111,12 @@ from opentelemetry.instrumentation.utils import http_status_to_status_code
|
|||||||
from opentelemetry.propagate import extract
|
from opentelemetry.propagate import extract
|
||||||
from opentelemetry.propagators.textmap import Getter, Setter
|
from opentelemetry.propagators.textmap import Getter, Setter
|
||||||
from opentelemetry.semconv.trace import SpanAttributes
|
from opentelemetry.semconv.trace import SpanAttributes
|
||||||
from opentelemetry.trace import Span, set_span_in_context
|
from opentelemetry.trace import (
|
||||||
|
INVALID_SPAN,
|
||||||
|
Span,
|
||||||
|
SpanKind,
|
||||||
|
set_span_in_context,
|
||||||
|
)
|
||||||
from opentelemetry.trace.status import Status, StatusCode
|
from opentelemetry.trace.status import Status, StatusCode
|
||||||
from opentelemetry.util.http import remove_url_credentials
|
from opentelemetry.util.http import remove_url_credentials
|
||||||
|
|
||||||
@ -321,39 +327,48 @@ class OpenTelemetryMiddleware:
|
|||||||
if self.excluded_urls and self.excluded_urls.url_disabled(url):
|
if self.excluded_urls and self.excluded_urls.url_disabled(url):
|
||||||
return await self.app(scope, receive, send)
|
return await self.app(scope, receive, send)
|
||||||
|
|
||||||
token = context.attach(extract(scope, getter=asgi_getter))
|
token = ctx = span_kind = None
|
||||||
server_span_name, additional_attributes = self.default_span_details(
|
|
||||||
scope
|
if trace.get_current_span() is INVALID_SPAN:
|
||||||
)
|
ctx = extract(scope, getter=asgi_getter)
|
||||||
|
token = context.attach(ctx)
|
||||||
|
span_kind = SpanKind.SERVER
|
||||||
|
else:
|
||||||
|
ctx = context.get_current()
|
||||||
|
span_kind = SpanKind.INTERNAL
|
||||||
|
|
||||||
|
span_name, additional_attributes = self.default_span_details(scope)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with self.tracer.start_as_current_span(
|
with self.tracer.start_as_current_span(
|
||||||
server_span_name,
|
span_name,
|
||||||
kind=trace.SpanKind.SERVER,
|
context=ctx,
|
||||||
) as server_span:
|
kind=span_kind,
|
||||||
if server_span.is_recording():
|
) as current_span:
|
||||||
|
if current_span.is_recording():
|
||||||
attributes = collect_request_attributes(scope)
|
attributes = collect_request_attributes(scope)
|
||||||
attributes.update(additional_attributes)
|
attributes.update(additional_attributes)
|
||||||
for key, value in attributes.items():
|
for key, value in attributes.items():
|
||||||
server_span.set_attribute(key, value)
|
current_span.set_attribute(key, value)
|
||||||
|
|
||||||
if callable(self.server_request_hook):
|
if callable(self.server_request_hook):
|
||||||
self.server_request_hook(server_span, scope)
|
self.server_request_hook(current_span, scope)
|
||||||
|
|
||||||
otel_receive = self._get_otel_receive(
|
otel_receive = self._get_otel_receive(
|
||||||
server_span_name, scope, receive
|
span_name, scope, receive
|
||||||
)
|
)
|
||||||
|
|
||||||
otel_send = self._get_otel_send(
|
otel_send = self._get_otel_send(
|
||||||
server_span,
|
current_span,
|
||||||
server_span_name,
|
span_name,
|
||||||
scope,
|
scope,
|
||||||
send,
|
send,
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.app(scope, otel_receive, otel_send)
|
await self.app(scope, otel_receive, otel_send)
|
||||||
finally:
|
finally:
|
||||||
context.detach(token)
|
if token:
|
||||||
|
context.detach(token)
|
||||||
|
|
||||||
def _get_otel_receive(self, server_span_name, scope, receive):
|
def _get_otel_receive(self, server_span_name, scope, receive):
|
||||||
@wraps(receive)
|
@wraps(receive)
|
||||||
|
@ -19,6 +19,7 @@ import fastapi
|
|||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
import opentelemetry.instrumentation.fastapi as otel_fastapi
|
import opentelemetry.instrumentation.fastapi as otel_fastapi
|
||||||
|
from opentelemetry import trace
|
||||||
from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
|
from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
|
||||||
from opentelemetry.sdk.resources import Resource
|
from opentelemetry.sdk.resources import Resource
|
||||||
from opentelemetry.semconv.trace import SpanAttributes
|
from opentelemetry.semconv.trace import SpanAttributes
|
||||||
@ -329,3 +330,48 @@ class TestAutoInstrumentationLogic(unittest.TestCase):
|
|||||||
|
|
||||||
should_be_original = fastapi.FastAPI
|
should_be_original = fastapi.FastAPI
|
||||||
self.assertIs(original, should_be_original)
|
self.assertIs(original, should_be_original)
|
||||||
|
|
||||||
|
|
||||||
|
class TestWrappedApplication(TestBase):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
self.app = fastapi.FastAPI()
|
||||||
|
|
||||||
|
@self.app.get("/foobar")
|
||||||
|
async def _():
|
||||||
|
return {"message": "hello world"}
|
||||||
|
|
||||||
|
otel_fastapi.FastAPIInstrumentor().instrument_app(self.app)
|
||||||
|
self.client = TestClient(self.app)
|
||||||
|
self.tracer = self.tracer_provider.get_tracer(__name__)
|
||||||
|
|
||||||
|
def tearDown(self) -> None:
|
||||||
|
super().tearDown()
|
||||||
|
with self.disable_logging():
|
||||||
|
otel_fastapi.FastAPIInstrumentor().uninstrument_app(self.app)
|
||||||
|
|
||||||
|
def test_mark_span_internal_in_presence_of_span_from_other_framework(self):
|
||||||
|
with self.tracer.start_as_current_span(
|
||||||
|
"test", kind=trace.SpanKind.SERVER
|
||||||
|
) as parent_span:
|
||||||
|
resp = self.client.get("/foobar")
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
span_list = self.memory_exporter.get_finished_spans()
|
||||||
|
for span in span_list:
|
||||||
|
print(str(span.__class__) + ": " + str(span.__dict__))
|
||||||
|
|
||||||
|
# there should be 4 spans - single SERVER "test" and three INTERNAL "FastAPI"
|
||||||
|
self.assertEqual(trace.SpanKind.INTERNAL, span_list[0].kind)
|
||||||
|
self.assertEqual(trace.SpanKind.INTERNAL, span_list[1].kind)
|
||||||
|
# main INTERNAL span - child of test
|
||||||
|
self.assertEqual(trace.SpanKind.INTERNAL, span_list[2].kind)
|
||||||
|
self.assertEqual(
|
||||||
|
parent_span.context.span_id, span_list[2].parent.span_id
|
||||||
|
)
|
||||||
|
# SERVER "test"
|
||||||
|
self.assertEqual(trace.SpanKind.SERVER, span_list[3].kind)
|
||||||
|
self.assertEqual(
|
||||||
|
parent_span.context.span_id, span_list[3].context.span_id
|
||||||
|
)
|
||||||
|
Reference in New Issue
Block a user