diff --git a/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py b/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py index ce11b18d635..fbc4c13db33 100644 --- a/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py +++ b/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py @@ -3,12 +3,10 @@ import logging -from flask import request as flask_request - import opentelemetry.ext.wsgi as otel_wsgi from opentelemetry import propagators, trace -from opentelemetry.ext.flask.version import __version__ from opentelemetry.util import time_ns +import flask logger = logging.getLogger(__name__) @@ -17,81 +15,95 @@ _ENVIRON_ACTIVATION_KEY = object() -def instrument_app(flask): - """Makes the passed-in Flask object traced by OpenTelemetry. - - You must not call this function multiple times on the same Flask object. - """ - - wsgi = flask.wsgi_app - - def 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 update_name later - # but that API is "highly discouraged" so we better avoid it. - environ[_ENVIRON_STARTTIME_KEY] = time_ns() - - def _start_response(status, response_headers, *args, **kwargs): - span = flask_request.environ.get(_ENVIRON_SPAN_KEY) - if span: - otel_wsgi.add_response_attributes( - span, status, response_headers +def patch(): + + class PatchedFlask(flask.Flask): + + def __init__(self, *args, **kwargs): + + super().__init__(*args, **kwargs) + + # Single use variable here to avoid recursion issues. + wsgi = self.wsgi_app + + def 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 + # update_name later but that API is "highly discouraged" so + # we better avoid it. + environ[_ENVIRON_STARTTIME_KEY] = time_ns() + + def _start_response( + status, response_headers, *args, **kwargs + ): + span = flask.request.environ.get(_ENVIRON_SPAN_KEY) + if span: + otel_wsgi.add_response_attributes( + span, status, response_headers + ) + else: + logger.warning( + "Flask environ's OpenTelemetry span " + "missing at _start_response(%s)", + status, + ) + + return start_response( + status, response_headers, *args, **kwargs + ) + return wsgi(environ, _start_response) + + self.wsgi_app = wrapped_app + + @self.before_request + def _before_flask_request(): + environ = flask.request.environ + span_name = ( + flask.request.endpoint + or otel_wsgi.get_default_span_name(environ) + ) + parent_span = propagators.extract( + otel_wsgi.get_header_from_environ, environ ) - else: - logger.warning( - "Flask environ's OpenTelemetry span missing at _start_response(%s)", - status, + + tracer = trace.tracer() + + attributes = otel_wsgi.collect_request_attributes(environ) + if flask.request.url_rule: + # For 404 that result from no route found, etc, we don't + # have a url_rule. + attributes["http.route"] = flask.request.url_rule.rule + span = tracer.start_span( + span_name, + parent_span, + kind=trace.SpanKind.SERVER, + attributes=attributes, + start_time=environ.get(_ENVIRON_STARTTIME_KEY), ) - return start_response(status, response_headers, *args, **kwargs) - - return wsgi(environ, _start_response) - - flask.wsgi_app = wrapped_app - - flask.before_request(_before_flask_request) - flask.teardown_request(_teardown_flask_request) - - -def _before_flask_request(): - environ = flask_request.environ - span_name = flask_request.endpoint or otel_wsgi.get_default_span_name( - environ - ) - parent_span = propagators.extract( - otel_wsgi.get_header_from_environ, environ - ) - - tracer = trace.tracer_source().get_tracer(__name__, __version__) - - attributes = otel_wsgi.collect_request_attributes(environ) - if flask_request.url_rule: - # For 404 that result from no route found, etc, we don't have a url_rule. - attributes["http.route"] = flask_request.url_rule.rule - span = tracer.start_span( - span_name, - parent_span, - kind=trace.SpanKind.SERVER, - attributes=attributes, - start_time=environ.get(_ENVIRON_STARTTIME_KEY), - ) - activation = tracer.use_span(span, end_on_exit=True) - activation.__enter__() - environ[_ENVIRON_ACTIVATION_KEY] = activation - environ[_ENVIRON_SPAN_KEY] = span - - -def _teardown_flask_request(exc): - activation = flask_request.environ.get(_ENVIRON_ACTIVATION_KEY) - if not activation: - logger.warning( - "Flask environ's OpenTelemetry activation missing at _teardown_flask_request(%s)", - exc, - ) - return - - if exc is None: - activation.__exit__(None, None, None) - else: - activation.__exit__( - type(exc), exc, getattr(exc, "__traceback__", None) - ) + activation = tracer.use_span(span, end_on_exit=True) + activation.__enter__() + environ[_ENVIRON_ACTIVATION_KEY] = activation + environ[_ENVIRON_SPAN_KEY] = span + + @self.teardown_request + def _teardown_flask_request(exc): + activation = flask.request.environ.get(_ENVIRON_ACTIVATION_KEY) + if not activation: + logger.warning( + "Flask environ's OpenTelemetry activation missing at " + "_teardown_flask_request(%s)", + exc, + ) + return + + if exc is None: + activation.__exit__(None, None, None) + else: + activation.__exit__( + type(exc), exc, getattr(exc, "__traceback__", None) + ) + + def tato(self): + pass + + flask.Flask = PatchedFlask diff --git a/ext/opentelemetry-ext-flask/tests/test_flask_integration.py b/ext/opentelemetry-ext-flask/tests/test_flask_integration.py index b943a25f22d..85d2c8dda54 100644 --- a/ext/opentelemetry-ext-flask/tests/test_flask_integration.py +++ b/ext/opentelemetry-ext-flask/tests/test_flask_integration.py @@ -15,11 +15,11 @@ import unittest from unittest import mock -from flask import Flask +import flask from werkzeug.test import Client from werkzeug.wrappers import BaseResponse -import opentelemetry.ext.flask as otel_flask +from opentelemetry.ext.flask import patch from opentelemetry import trace as trace_api from opentelemetry.ext.testutil.wsgitestutil import WsgiTestBase @@ -36,7 +36,9 @@ def setspanattr(key, value): self.span.set_attribute = setspanattr - self.app = Flask(__name__) + patch() + + self.app = flask.Flask(__name__) def hello_endpoint(helloid): if helloid == 500: @@ -45,7 +47,7 @@ def hello_endpoint(helloid): self.app.route("/hello/")(hello_endpoint) - otel_flask.instrument_app(self.app) + # otel_flask.instrument_app(self.app) self.client = Client(self.app, BaseResponse) def test_simple(self): diff --git a/opentelemetry-api/setup.py b/opentelemetry-api/setup.py index ee8adf26aeb..a1de316761c 100644 --- a/opentelemetry-api/setup.py +++ b/opentelemetry-api/setup.py @@ -40,6 +40,11 @@ "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", ], + entry_points={ + "console_scripts": [ + "auto_agent = opentelemetry.commands.auto_agent:run" + ] + }, description="OpenTelemetry Python API", include_package_data=True, long_description=open("README.rst").read(), diff --git a/opentelemetry-api/src/opentelemetry/commands/__init__.py b/opentelemetry-api/src/opentelemetry/commands/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/opentelemetry-api/src/opentelemetry/commands/auto_agent.py b/opentelemetry-api/src/opentelemetry/commands/auto_agent.py new file mode 100755 index 00000000000..f5df116ed93 --- /dev/null +++ b/opentelemetry-api/src/opentelemetry/commands/auto_agent.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +from sys import exit, argv +from os import execl +import os +from os.path import dirname, join +from distutils.spawn import find_executable + + +def run(): + os.environ['PYTHONPATH'] = join(dirname(__file__), 'initialize') + print(os.environ['PYTHONPATH']) + + python3 = find_executable('python3') + execl(python3, python3, argv[1]) + exit(0) + + +if __name__ == "__main__": + run() diff --git a/opentelemetry-api/src/opentelemetry/commands/initialize/__init__.py b/opentelemetry-api/src/opentelemetry/commands/initialize/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/opentelemetry-api/src/opentelemetry/commands/initialize/sitecustomize.py b/opentelemetry-api/src/opentelemetry/commands/initialize/sitecustomize.py new file mode 100644 index 00000000000..c20ba46be4b --- /dev/null +++ b/opentelemetry-api/src/opentelemetry/commands/initialize/sitecustomize.py @@ -0,0 +1,8 @@ +from opentelemetry.ext.flask import patch + +try: + patch() + +except Exception as error: + + print(error)