def setUp(self): super().setUp() FalconInstrumentor().instrument( request_hook=getattr(self, "request_hook", None), response_hook=getattr(self, "response_hook", None), ) self.app = make_app() # pylint: disable=protected-access self.env_patch = patch.dict( "os.environ", { "OTEL_PYTHON_FALCON_EXCLUDED_URLS": "ping", "OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS": "query_string", }, ) self.env_patch.start() self.exclude_patch = patch( "opentelemetry.instrumentation.falcon._excluded_urls", get_excluded_urls("FALCON"), ) middleware = self.app._middleware[0][ # pylint:disable=W0212 0 ].__self__ self.traced_patch = patch.object( middleware, "_traced_request_attrs", get_traced_request_attrs("FALCON"), ) self.exclude_patch.start() self.traced_patch.start()
def setUp(self): TornadoInstrumentor().instrument( server_request_hook=getattr(self, "server_request_hook", None), client_request_hook=getattr(self, "client_request_hook", None), client_response_hook=getattr(self, "client_response_hook", None), ) super().setUp() # pylint: disable=protected-access self.env_patch = patch.dict( "os.environ", { "OTEL_PYTHON_TORNADO_EXCLUDED_URLS": "healthz,ping", "OTEL_PYTHON_TORNADO_TRACED_REQUEST_ATTRS": "uri,full_url,query", }, ) self.env_patch.start() self.exclude_patch = patch( "opentelemetry.instrumentation.tornado._excluded_urls", get_excluded_urls("TORNADO"), ) self.traced_patch = patch( "opentelemetry.instrumentation.tornado._traced_request_attrs", get_traced_request_attrs("TORNADO"), ) self.exclude_patch.start() self.traced_patch.start()
def __init__(self, *args, **kwargs): otel_opts = kwargs.pop("_otel_opts", {}) # inject trace middleware middlewares = kwargs.pop("middleware", []) tracer_provider = otel_opts.pop("tracer_provider", None) if not isinstance(middlewares, (list, tuple)): middlewares = [middlewares] self._otel_tracer = trace.get_tracer( __name__, __version__, tracer_provider ) trace_middleware = _TraceMiddleware( self._otel_tracer, otel_opts.pop( "traced_request_attributes", get_traced_request_attrs("FALCON") ), otel_opts.pop("request_hook", None), otel_opts.pop("response_hook", None), ) middlewares.insert(0, trace_middleware) kwargs["middleware"] = middlewares self._otel_excluded_urls = get_excluded_urls("FALCON") super().__init__(*args, **kwargs)
def setUp(self): super().setUp() self.env_patch = patch.dict( "os.environ", {"OTEL_PYTHON_STARLETTE_EXCLUDED_URLS": "/exclude/123,healthzz"}, ) self.env_patch.start() self.exclude_patch = patch( "opentelemetry.instrumentation.starlette._excluded_urls", get_excluded_urls("STARLETTE"), ) self.exclude_patch.start() self._instrumentor = otel_starlette.StarletteInstrumentor() self._app = self._create_app() self._client = TestClient(self._app)
def setUp(self): super().setUp() self.env_patch = patch.dict( "os.environ", {"OTEL_PYTHON_FASTAPI_EXCLUDED_URLS": "/exclude/123,healthzz"}, ) self.env_patch.start() self.exclude_patch = patch( "opentelemetry.instrumentation.fastapi._excluded_urls", get_excluded_urls("FASTAPI"), ) self.exclude_patch.start() self._instrumentor = otel_fastapi.FastAPIInstrumentor() self._app = self._create_app() self._client = TestClient(self._app)
def setUp(self): super().setUp() self.env_patch = mock.patch.dict( "os.environ", { "OTEL_PYTHON_REQUESTS_EXCLUDED_URLS": "http://localhost/env_excluded_arg/123,env_excluded_noarg" }, ) self.env_patch.start() self.exclude_patch = mock.patch( "opentelemetry.instrumentation.requests._excluded_urls_from_env", get_excluded_urls("REQUESTS"), ) self.exclude_patch.start() RequestsInstrumentor().instrument() httpretty.enable() httpretty.register_uri(httpretty.GET, self.URL, body="Hello!")
def setUp(self): super().setUp() self.app = Flask(__name__) FlaskInstrumentor().instrument_app(self.app) self._common_initialization() self.env_patch = patch.dict( "os.environ", { "OTEL_PYTHON_FLASK_EXCLUDED_URLS": "http://localhost/excluded_arg/123,excluded_noarg" }, ) self.env_patch.start() self.exclude_patch = patch( "opentelemetry.instrumentation.flask._excluded_urls", get_excluded_urls("FLASK"), ) self.exclude_patch.start()
def setUp(self): super().setUp() config = Configurator() PyramidInstrumentor().instrument_config(config) self.config = config self._common_initialization(self.config) self.env_patch = patch.dict( "os.environ", { "OTEL_PYTHON_PYRAMID_EXCLUDED_URLS": "http://localhost/excluded_arg/123,excluded_noarg" }, ) self.env_patch.start() self.exclude_patch = patch( "opentelemetry.instrumentation.pyramid.callbacks._excluded_urls", get_excluded_urls("PYRAMID"), ) self.exclude_patch.start()
def setUp(self): super().setUp() setup_test_environment() _django_instrumentor.instrument() self.env_patch = patch.dict( "os.environ", { "OTEL_PYTHON_DJANGO_EXCLUDED_URLS": "http://testserver/excluded_arg/123,excluded_noarg", "OTEL_PYTHON_DJANGO_TRACED_REQUEST_ATTRS": "path_info,content_type,non_existing_variable", }, ) self.env_patch.start() self.exclude_patch = patch( "opentelemetry.instrumentation.django.middleware._DjangoMiddleware._excluded_urls", get_excluded_urls("DJANGO"), ) self.traced_patch = patch( "opentelemetry.instrumentation.django.middleware._DjangoMiddleware._traced_request_attrs", get_traced_request_attrs("DJANGO"), ) self.exclude_patch.start() self.traced_patch.start()
import logging import typing from typing import Collection import fastapi from starlette.routing import Match from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware from opentelemetry.instrumentation.asgi.package import _instruments from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.trace import Span from opentelemetry.util.http import get_excluded_urls, parse_excluded_urls _excluded_urls_from_env = get_excluded_urls("FASTAPI") _logger = logging.getLogger(__name__) _ServerRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]] _ClientRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]] _ClientResponseHookT = typing.Optional[typing.Callable[[Span, dict], None]] class FastAPIInstrumentor(BaseInstrumentor): """An instrumentor for FastAPI See `BaseInstrumentor` """ _original_fastapi = None
--- """ import typing from typing import Collection from starlette import applications from starlette.routing import Match from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware from opentelemetry.instrumentation.asgi.package import _instruments from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.trace import Span from opentelemetry.util.http import get_excluded_urls _excluded_urls = get_excluded_urls("STARLETTE") _ServerRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]] _ClientRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]] _ClientResponseHookT = typing.Optional[typing.Callable[[Span, dict], None]] class StarletteInstrumentor(BaseInstrumentor): """An instrumentor for starlette See `BaseInstrumentor` """ _original_starlette = None @staticmethod
http_status_to_status_code, ) from opentelemetry.propagate import extract from opentelemetry.trace.status import Status from opentelemetry.util._time import _time_ns from opentelemetry.util.http import get_excluded_urls, get_traced_request_attrs _logger = getLogger(__name__) _ENVIRON_STARTTIME_KEY = "opentelemetry-falcon.starttime_key" _ENVIRON_SPAN_KEY = "opentelemetry-falcon.span_key" _ENVIRON_ACTIVATION_KEY = "opentelemetry-falcon.activation_key" _ENVIRON_TOKEN = "opentelemetry-falcon.token" _ENVIRON_EXC = "opentelemetry-falcon.exc" _excluded_urls = get_excluded_urls("FALCON") _traced_request_attrs = get_traced_request_attrs("FALCON") _response_propagation_setter = FuncSetter(falcon.api.Response.append_header) class FalconInstrumentor(BaseInstrumentor): # pylint: disable=protected-access,attribute-defined-outside-init """An instrumentor for falcon.API See `BaseInstrumentor` """ def _instrument(self, **kwargs): self._original_falcon_api = falcon.API falcon.API = partial(_InstrumentedFalconAPI, **kwargs) def _uninstrument(self, **kwargs):
def test_config_from_generic_env(self): exclude_list = get_excluded_urls("DJANGO") self.assertTrue(exclude_list.url_disabled("/excluded_arg/123")) self.assertTrue(exclude_list.url_disabled("/excluded_noarg")) self.assertFalse(exclude_list.url_disabled("/excluded_arg/125"))
def test_config_from_instrumentation_env_empty(self): exclude_list = get_excluded_urls("DJANGO") self.assertFalse(exclude_list.url_disabled("/excluded_arg/123")) self.assertFalse(exclude_list.url_disabled("/excluded_noarg")) self.assertFalse(exclude_list.url_disabled("/excluded_arg/125"))
class _DjangoMiddleware(MiddlewareMixin): """Django Middleware for OpenTelemetry""" _environ_activation_key = ( "opentelemetry-instrumentor-django.activation_key") _environ_token = "opentelemetry-instrumentor-django.token" _environ_span_key = "opentelemetry-instrumentor-django.span_key" _environ_exception_key = "opentelemetry-instrumentor-django.exception_key" _traced_request_attrs = get_traced_request_attrs("DJANGO") _excluded_urls = get_excluded_urls("DJANGO") @staticmethod def _get_span_name(request): try: if getattr(request, "resolver_match"): match = request.resolver_match else: match = resolve(request.path) if hasattr(match, "route"): return match.route # Instead of using `view_name`, better to use `_func_name` as some applications can use similar # view names in different modules if hasattr(match, "_func_name"): return match._func_name # pylint: disable=protected-access # Fallback for safety as `_func_name` private field return match.view_name except Resolver404: return "HTTP {}".format(request.method) def process_request(self, request): # request.META is a dictionary containing all available HTTP headers # Read more about request.META here: # https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpRequest.META if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): return # pylint:disable=W0212 request._otel_start_time = time() request_meta = request.META token = attach(extract(carrier_getter, request_meta)) tracer = get_tracer(__name__, __version__) span = tracer.start_span( self._get_span_name(request), kind=SpanKind.SERVER, start_time=request_meta.get( "opentelemetry-instrumentor-django.starttime_key"), ) attributes = collect_request_attributes(request_meta) if span.is_recording(): attributes = extract_attributes_from_object( request, self._traced_request_attrs, attributes) for key, value in attributes.items(): span.set_attribute(key, value) activation = tracer.use_span(span, end_on_exit=True) activation.__enter__() request.META[self._environ_activation_key] = activation request.META[self._environ_span_key] = span request.META[self._environ_token] = token # pylint: disable=unused-argument def process_view(self, request, view_func, *args, **kwargs): # Process view is executed before the view function, here we get the # route template from request.resolver_match. It is not set yet in process_request if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): return if (self._environ_activation_key in request.META.keys() and self._environ_span_key in request.META.keys()): span = request.META[self._environ_span_key] if span.is_recording(): match = getattr(request, "resolver_match") if match: route = getattr(match, "route") if route: span.set_attribute("http.route", route) def process_exception(self, request, exception): if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): return if self._environ_activation_key in request.META.keys(): request.META[self._environ_exception_key] = exception def process_response(self, request, response): if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): return response if (self._environ_activation_key in request.META.keys() and self._environ_span_key in request.META.keys()): add_response_attributes( request.META[self._environ_span_key], "{} {}".format(response.status_code, response.reason_phrase), response, ) request.META.pop(self._environ_span_key) exception = request.META.pop(self._environ_exception_key, None) if exception: request.META[self._environ_activation_key].__exit__( type(exception), exception, getattr(exception, "__traceback__", None), ) else: request.META[self._environ_activation_key].__exit__( None, None, None) request.META.pop(self._environ_activation_key) if self._environ_token in request.META.keys(): detach(request.environ.get(self._environ_token)) request.META.pop(self._environ_token) return response
unwrap, ) from opentelemetry.propagate import extract from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.trace.status import Status, StatusCode from opentelemetry.util._time import _time_ns from opentelemetry.util.http import get_excluded_urls, get_traced_request_attrs from .client import fetch_async # pylint: disable=E0401 _logger = getLogger(__name__) _TraceContext = namedtuple("TraceContext", ["activation", "span", "token"]) _HANDLER_CONTEXT_KEY = "_otel_trace_context_key" _OTEL_PATCHED_KEY = "_otel_patched_key" _excluded_urls = get_excluded_urls("TORNADO") _traced_request_attrs = get_traced_request_attrs("TORNADO") response_propagation_setter = FuncSetter(tornado.web.RequestHandler.add_header) class TornadoInstrumentor(BaseInstrumentor): patched_handlers = [] original_handler_new = None def instrumentation_dependencies(self) -> Collection[str]: return _instruments def _instrument(self, **kwargs): """ _instrument patches tornado.web.RequestHandler and tornado.httpclient.AsyncHTTPClient classes
http_status_to_status_code, ) from opentelemetry.metrics import Histogram, get_meter from opentelemetry.propagate import inject from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.trace import SpanKind, Tracer, get_tracer from opentelemetry.trace.span import Span from opentelemetry.trace.status import Status from opentelemetry.util.http import ( get_excluded_urls, parse_excluded_urls, remove_url_credentials, ) from opentelemetry.util.http.httplib import set_ip_on_next_http_connection _excluded_urls_from_env = get_excluded_urls("REQUESTS") # pylint: disable=unused-argument # pylint: disable=R0915 def _instrument( tracer: Tracer, duration_histogram: Histogram, span_callback: Optional[Callable[[Span, Response], str]] = None, name_callback: Optional[Callable[[str, str], str]] = None, excluded_urls: Iterable[str] = None, ): """Enables tracing of all requests calls that go through :code:`requests.session.Session.request` (this includes :code:`requests.get`, etc.)."""
class _DjangoMiddleware(MiddlewareMixin): """Django Middleware for OpenTelemetry""" _environ_activation_key = ( "opentelemetry-instrumentor-django.activation_key" ) _environ_token = "opentelemetry-instrumentor-django.token" _environ_span_key = "opentelemetry-instrumentor-django.span_key" _environ_exception_key = "opentelemetry-instrumentor-django.exception_key" _traced_request_attrs = get_traced_request_attrs("DJANGO") _excluded_urls = get_excluded_urls("DJANGO") _tracer = None _otel_request_hook: Callable[[Span, HttpRequest], None] = None _otel_response_hook: Callable[ [Span, HttpRequest, HttpResponse], None ] = None @staticmethod def _get_span_name(request): try: if getattr(request, "resolver_match"): match = request.resolver_match else: match = resolve(request.path) if hasattr(match, "route"): return match.route # Instead of using `view_name`, better to use `_func_name` as some applications can use similar # view names in different modules if hasattr(match, "_func_name"): return match._func_name # pylint: disable=protected-access # Fallback for safety as `_func_name` private field return match.view_name except Resolver404: return f"HTTP {request.method}" def process_request(self, request): # request.META is a dictionary containing all available HTTP headers # Read more about request.META here: # https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpRequest.META if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): return is_asgi_request = _is_asgi_request(request) if not _is_asgi_supported and is_asgi_request: return # pylint:disable=W0212 request._otel_start_time = time() request_meta = request.META if is_asgi_request: carrier = request.scope carrier_getter = asgi_getter collect_request_attributes = asgi_collect_request_attributes else: carrier = request_meta carrier_getter = wsgi_getter collect_request_attributes = wsgi_collect_request_attributes token = context = None span_kind = SpanKind.INTERNAL if get_current_span() is INVALID_SPAN: context = extract(carrier, getter=carrier_getter) token = attach(context) span_kind = SpanKind.SERVER span = self._tracer.start_span( self._get_span_name(request), context, kind=span_kind, start_time=request_meta.get( "opentelemetry-instrumentor-django.starttime_key" ), ) attributes = collect_request_attributes(carrier) if span.is_recording(): attributes = extract_attributes_from_object( request, self._traced_request_attrs, attributes ) if is_asgi_request: # ASGI requests include extra attributes in request.scope.headers. attributes = extract_attributes_from_object( types.SimpleNamespace( **{ name.decode("latin1"): value.decode("latin1") for name, value in request.scope.get("headers", []) } ), self._traced_request_attrs, attributes, ) for key, value in attributes.items(): span.set_attribute(key, value) activation = use_span(span, end_on_exit=True) activation.__enter__() # pylint: disable=E1101 request.META[self._environ_activation_key] = activation request.META[self._environ_span_key] = span if token: request.META[self._environ_token] = token if _DjangoMiddleware._otel_request_hook: _DjangoMiddleware._otel_request_hook( # pylint: disable=not-callable span, request ) # pylint: disable=unused-argument def process_view(self, request, view_func, *args, **kwargs): # Process view is executed before the view function, here we get the # route template from request.resolver_match. It is not set yet in process_request if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): return if ( self._environ_activation_key in request.META.keys() and self._environ_span_key in request.META.keys() ): span = request.META[self._environ_span_key] if span.is_recording(): match = getattr(request, "resolver_match", None) if match: route = getattr(match, "route", None) if route: span.set_attribute(SpanAttributes.HTTP_ROUTE, route) def process_exception(self, request, exception): if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): return if self._environ_activation_key in request.META.keys(): request.META[self._environ_exception_key] = exception def process_response(self, request, response): if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): return response is_asgi_request = _is_asgi_request(request) if not _is_asgi_supported and is_asgi_request: return response activation = request.META.pop(self._environ_activation_key, None) span = request.META.pop(self._environ_span_key, None) if activation and span: if is_asgi_request: set_status_code(span, response.status_code) else: add_response_attributes( span, f"{response.status_code} {response.reason_phrase}", response, ) propagator = get_global_response_propagator() if propagator: propagator.inject(response) # record any exceptions raised while processing the request exception = request.META.pop(self._environ_exception_key, None) if _DjangoMiddleware._otel_response_hook: _DjangoMiddleware._otel_response_hook( # pylint: disable=not-callable span, request, response ) if exception: activation.__exit__( type(exception), exception, getattr(exception, "__traceback__", None), ) else: activation.__exit__(None, None, None) if request.META.get(self._environ_token, None) is not None: detach(request.META.get(self._environ_token)) request.META.pop(self._environ_token) return response
from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.util._time import _time_ns from opentelemetry.util.http import get_excluded_urls TWEEN_NAME = "opentelemetry.instrumentation.pyramid.trace_tween_factory" SETTING_TRACE_ENABLED = "opentelemetry-pyramid.trace_enabled" _ENVIRON_STARTTIME_KEY = "opentelemetry-pyramid.starttime_key" _ENVIRON_SPAN_KEY = "opentelemetry-pyramid.span_key" _ENVIRON_ACTIVATION_KEY = "opentelemetry-pyramid.activation_key" _ENVIRON_ENABLED_KEY = "opentelemetry-pyramid.tracing_enabled_key" _ENVIRON_TOKEN = "opentelemetry-pyramid.token" _logger = getLogger(__name__) _excluded_urls = get_excluded_urls("PYRAMID") def includeme(config): config.add_settings({SETTING_TRACE_ENABLED: True}) config.add_subscriber(_before_traversal, BeforeTraversal) _insert_tween(config) def _insert_tween(config): settings = config.get_settings() tweens = settings.get("pyramid.tweens") # If the list is empty, pyramid does not consider the tweens have been # set explicitly. And if our tween is already there, nothing to do if not tweens or not tweens.strip():
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.propagators import ( get_global_response_propagator, ) from opentelemetry.propagate import extract from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.util._time import _time_ns from opentelemetry.util.http import get_excluded_urls, parse_excluded_urls _logger = getLogger(__name__) _ENVIRON_STARTTIME_KEY = "opentelemetry-flask.starttime_key" _ENVIRON_SPAN_KEY = "opentelemetry-flask.span_key" _ENVIRON_ACTIVATION_KEY = "opentelemetry-flask.activation_key" _ENVIRON_TOKEN = "opentelemetry-flask.token" _excluded_urls_from_env = get_excluded_urls("FLASK") def get_default_span_name(): span_name = "" try: span_name = flask.request.url_rule.rule except AttributeError: span_name = otel_wsgi.get_default_span_name(flask.request.environ) return span_name def _rewrapped_app(wsgi_app, response_hook=None, excluded_urls=None): def _wrapped_app(wrapped_app_environ, start_response): # We want to measure the time for route matching, etc. # In theory, we could start the span here and use
from opentelemetry.instrumentation.flask.version import __version__ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.propagators import ( get_global_response_propagator, ) from opentelemetry.propagate import extract from opentelemetry.util._time import _time_ns from opentelemetry.util.http import get_excluded_urls _logger = getLogger(__name__) _ENVIRON_STARTTIME_KEY = "opentelemetry-flask.starttime_key" _ENVIRON_SPAN_KEY = "opentelemetry-flask.span_key" _ENVIRON_ACTIVATION_KEY = "opentelemetry-flask.activation_key" _ENVIRON_TOKEN = "opentelemetry-flask.token" _excluded_urls = get_excluded_urls("FLASK") def get_default_span_name(): span_name = "" try: span_name = flask.request.url_rule.rule except AttributeError: span_name = otel_wsgi.get_default_span_name(flask.request.environ) return span_name def _rewrapped_app(wsgi_app): def _wrapped_app(wrapped_app_environ, start_response): # We want to measure the time for route matching, etc. # In theory, we could start the span here and use
# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import fastapi from starlette.routing import Match from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.util.http import get_excluded_urls _excluded_urls = get_excluded_urls("FASTAPI") class FastAPIInstrumentor(BaseInstrumentor): """An instrumentor for FastAPI See `BaseInstrumentor` """ _original_fastapi = None @staticmethod def instrument_app(app: fastapi.FastAPI): """Instrument an uninstrumented FastAPI application. """ if not getattr(app, "is_instrumented_by_opentelemetry", False):