Skip to content

Commit

Permalink
Conditionally create server spans for falcon (#867)
Browse files Browse the repository at this point in the history
* Making span as internal for falcon in presence of a span in current context

* Updating changelog

* Fixing lint and generate build failures

* Resolving comments: Converting snippet to re-usable function

* Fixing build failures

* Resolving comments: Creating wrapper for start span to make internal/server span

* Rerun docker tests

* Resolving comments: Refactoring
  • Loading branch information
ashu658 authored Jan 21, 2022
1 parent dd72d94 commit 8bb1e74
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 8 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#817](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/817))
- `opentelemetry-instrumentation-kafka-python` added kafka-python module instrumentation.
([#814](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/814))

- `opentelemetry-instrumentation-falcon` Falcon: Conditionally create SERVER spans
([#867](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/867))
### Fixed

- `opentelemetry-instrumentation-django` Django: Conditionally create SERVER spans
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ def response_hook(span, req, resp):
get_global_response_propagator,
)
from opentelemetry.instrumentation.utils import (
_start_internal_or_server_span,
extract_attributes_from_object,
http_status_to_status_code,
)
from opentelemetry.propagate import extract
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.trace.status import Status
from opentelemetry.util._time import _time_ns
Expand Down Expand Up @@ -195,12 +195,14 @@ def __call__(self, env, start_response):

start_time = _time_ns()

token = context.attach(extract(env, getter=otel_wsgi.wsgi_getter))
span = self._tracer.start_span(
otel_wsgi.get_default_span_name(env),
kind=trace.SpanKind.SERVER,
span, token = _start_internal_or_server_span(
tracer=self._tracer,
span_name=otel_wsgi.get_default_span_name(env),
start_time=start_time,
context_carrier=env,
context_getter=otel_wsgi.wsgi_getter,
)

if span.is_recording():
attributes = otel_wsgi.collect_request_attributes(env)
for key, value in attributes.items():
Expand All @@ -216,7 +218,8 @@ def _start_response(status, response_headers, *args, **kwargs):
status, response_headers, *args, **kwargs
)
activation.__exit__(None, None, None)
context.detach(token)
if token is not None:
context.detach(token)
return response

try:
Expand All @@ -227,7 +230,8 @@ def _start_response(status, response_headers, *args, **kwargs):
exc,
getattr(exc, "__traceback__", None),
)
context.detach(token)
if token is not None:
context.detach(token)
raise


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from falcon import testing

from opentelemetry import trace
from opentelemetry.instrumentation.falcon import FalconInstrumentor
from opentelemetry.instrumentation.propagators import (
TraceResponsePropagator,
Expand Down Expand Up @@ -264,3 +265,18 @@ def test_hooks(self):
self.assertEqual(
span.attributes["request_hook_attr"], "value from hook"
)


class TestFalconInstrumentationWrappedWithOtherFramework(TestFalconBase):
def test_mark_span_internal_in_presence_of_span_from_other_framework(self):
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span(
"test", kind=trace.SpanKind.SERVER
) as parent_span:
self.client().simulate_request(method="GET", path="/hello")
span = self.memory_exporter.get_finished_spans()[0]
assert span.status.is_ok
self.assertEqual(trace.SpanKind.INTERNAL, span.kind)
self.assertEqual(
span.parent.span_id, parent_span.get_span_context().span_id
)
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@

from wrapt import ObjectProxy

from opentelemetry import context, trace

# pylint: disable=unused-import
# pylint: disable=E0611
from opentelemetry.context import _SUPPRESS_INSTRUMENTATION_KEY # noqa: F401
from opentelemetry.propagate import extract
from opentelemetry.trace import StatusCode


Expand Down Expand Up @@ -67,3 +70,39 @@ def unwrap(obj, attr: str):
func = getattr(obj, attr, None)
if func and isinstance(func, ObjectProxy) and hasattr(func, "__wrapped__"):
setattr(obj, attr, func.__wrapped__)


def _start_internal_or_server_span(
tracer, span_name, start_time, context_carrier, context_getter
):
"""Returns internal or server span along with the token which can be used by caller to reset context
Args:
tracer : tracer in use by given instrumentation library
name (string): name of the span
start_time : start time of the span
context_carrier : object which contains values that are
used to construct a Context. This object
must be paired with an appropriate getter
which understands how to extract a value from it.
context_getter : an object which contains a get function that can retrieve zero
or more values from the carrier and a keys function that can get all the keys
from carrier.
"""

token = ctx = span_kind = None
if trace.get_current_span() is trace.INVALID_SPAN:
ctx = extract(context_carrier, getter=context_getter)
token = context.attach(ctx)
span_kind = trace.SpanKind.SERVER
else:
ctx = context.get_current()
span_kind = trace.SpanKind.INTERNAL
span = tracer.start_span(
name=span_name,
context=ctx,
kind=span_kind,
start_time=start_time,
)
return span, token

0 comments on commit 8bb1e74

Please sign in to comment.