From b5b7386b51bd4473bd783d847b947d35943b24b8 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 17 Apr 2020 22:14:50 -0600 Subject: [PATCH 01/58] Make sure filedir is at the beginning of PYTHONPATH --- .../tests/test_instrumentor.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/opentelemetry-auto-instrumentation/tests/test_instrumentor.py b/opentelemetry-auto-instrumentation/tests/test_instrumentor.py index a768a40eb42..d5309fff3cf 100644 --- a/opentelemetry-auto-instrumentation/tests/test_instrumentor.py +++ b/opentelemetry-auto-instrumentation/tests/test_instrumentor.py @@ -14,8 +14,12 @@ # type: ignore from logging import WARNING +from os import environ +from os.path import abspath, dirname, pathsep from unittest import TestCase +from unittest.mock import patch +from opentelemetry.auto_instrumentation import auto_instrumentation from opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor @@ -45,3 +49,41 @@ def test_protect(self): def test_singleton(self): self.assertIs(self.Instrumentor(), self.Instrumentor()) + + +class TestRun(TestCase): + auto_instrumentation_path = dirname(abspath(auto_instrumentation.__file__)) + + @patch.dict("os.environ", {"PYTHONPATH": ""}) + @patch("opentelemetry.auto_instrumentation.auto_instrumentation.argv") + @patch("opentelemetry.auto_instrumentation.auto_instrumentation.execl") + def test_run_empty( + self, mock_execl, mock_argv + ): # pylint: disable=unused-argument + auto_instrumentation.run() + assert environ["PYTHONPATH"] == self.auto_instrumentation_path + + @patch.dict("os.environ", {"PYTHONPATH": "abc"}) + @patch("opentelemetry.auto_instrumentation.auto_instrumentation.argv") + @patch("opentelemetry.auto_instrumentation.auto_instrumentation.execl") + def test_run_non_empty( + self, mock_execl, mock_argv + ): # pylint: disable=unused-argument + auto_instrumentation.run() + assert environ["PYTHONPATH"] == pathsep.join( + [self.auto_instrumentation_path, "abc"] + ) + + @patch.dict( + "os.environ", + {"PYTHONPATH": pathsep.join(["abc", auto_instrumentation_path])}, + ) + @patch("opentelemetry.auto_instrumentation.auto_instrumentation.argv") + @patch("opentelemetry.auto_instrumentation.auto_instrumentation.execl") + def test_run_after_path( + self, mock_execl, mock_argv + ): # pylint: disable=unused-argument + auto_instrumentation.run() + assert environ["PYTHONPATH"] == pathsep.join( + [self.auto_instrumentation_path, "abc"] + ) From bfc1764179c7aa68fc3390f08ac8a6af203c8fe1 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Sat, 18 Apr 2020 16:00:47 -0600 Subject: [PATCH 02/58] Make the tests pass --- .../tests/test_instrumentor.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/opentelemetry-auto-instrumentation/tests/test_instrumentor.py b/opentelemetry-auto-instrumentation/tests/test_instrumentor.py index d5309fff3cf..e5d9ae0bf97 100644 --- a/opentelemetry-auto-instrumentation/tests/test_instrumentor.py +++ b/opentelemetry-auto-instrumentation/tests/test_instrumentor.py @@ -57,8 +57,9 @@ class TestRun(TestCase): @patch.dict("os.environ", {"PYTHONPATH": ""}) @patch("opentelemetry.auto_instrumentation.auto_instrumentation.argv") @patch("opentelemetry.auto_instrumentation.auto_instrumentation.execl") + @patch("opentelemetry.auto_instrumentation.auto_instrumentation.which") def test_run_empty( - self, mock_execl, mock_argv + self, mock_which, mock_execl, mock_argv ): # pylint: disable=unused-argument auto_instrumentation.run() assert environ["PYTHONPATH"] == self.auto_instrumentation_path @@ -66,8 +67,9 @@ def test_run_empty( @patch.dict("os.environ", {"PYTHONPATH": "abc"}) @patch("opentelemetry.auto_instrumentation.auto_instrumentation.argv") @patch("opentelemetry.auto_instrumentation.auto_instrumentation.execl") + @patch("opentelemetry.auto_instrumentation.auto_instrumentation.which") def test_run_non_empty( - self, mock_execl, mock_argv + self, mock_which, mock_execl, mock_argv ): # pylint: disable=unused-argument auto_instrumentation.run() assert environ["PYTHONPATH"] == pathsep.join( @@ -80,8 +82,9 @@ def test_run_non_empty( ) @patch("opentelemetry.auto_instrumentation.auto_instrumentation.argv") @patch("opentelemetry.auto_instrumentation.auto_instrumentation.execl") + @patch("opentelemetry.auto_instrumentation.auto_instrumentation.which") def test_run_after_path( - self, mock_execl, mock_argv + self, mock_which, mock_execl, mock_argv ): # pylint: disable=unused-argument auto_instrumentation.run() assert environ["PYTHONPATH"] == pathsep.join( From 3501ae1b06ef70b9a38e6199086d881f78a9a7c1 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 23 Apr 2020 16:46:31 -0600 Subject: [PATCH 03/58] Fix assertions --- .../tests/test_instrumentor.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/opentelemetry-auto-instrumentation/tests/test_instrumentor.py b/opentelemetry-auto-instrumentation/tests/test_instrumentor.py index e5d9ae0bf97..f33e3c03717 100644 --- a/opentelemetry-auto-instrumentation/tests/test_instrumentor.py +++ b/opentelemetry-auto-instrumentation/tests/test_instrumentor.py @@ -62,7 +62,7 @@ def test_run_empty( self, mock_which, mock_execl, mock_argv ): # pylint: disable=unused-argument auto_instrumentation.run() - assert environ["PYTHONPATH"] == self.auto_instrumentation_path + self.assertEqual(environ["PYTHONPATH"], self.auto_instrumentation_path) @patch.dict("os.environ", {"PYTHONPATH": "abc"}) @patch("opentelemetry.auto_instrumentation.auto_instrumentation.argv") @@ -72,8 +72,11 @@ def test_run_non_empty( self, mock_which, mock_execl, mock_argv ): # pylint: disable=unused-argument auto_instrumentation.run() - assert environ["PYTHONPATH"] == pathsep.join( - [self.auto_instrumentation_path, "abc"] + self.assertEqual( + environ["PYTHONPATH"], + pathsep.join( + [self.auto_instrumentation_path, "abc"] + ) ) @patch.dict( @@ -87,6 +90,8 @@ def test_run_after_path( self, mock_which, mock_execl, mock_argv ): # pylint: disable=unused-argument auto_instrumentation.run() - assert environ["PYTHONPATH"] == pathsep.join( - [self.auto_instrumentation_path, "abc"] + self.assertEqual( + environ["PYTHONPATH"] == pathsep.join( + [self.auto_instrumentation_path, "abc"] + ) ) From 3814f2cd1c85c56fe67bc12b0d164b650a92feb3 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 23 Apr 2020 16:48:48 -0600 Subject: [PATCH 04/58] More fixing --- .../tests/test_instrumentor.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/opentelemetry-auto-instrumentation/tests/test_instrumentor.py b/opentelemetry-auto-instrumentation/tests/test_instrumentor.py index f33e3c03717..da8d95a6d5f 100644 --- a/opentelemetry-auto-instrumentation/tests/test_instrumentor.py +++ b/opentelemetry-auto-instrumentation/tests/test_instrumentor.py @@ -76,7 +76,7 @@ def test_run_non_empty( environ["PYTHONPATH"], pathsep.join( [self.auto_instrumentation_path, "abc"] - ) + ), ) @patch.dict( @@ -91,7 +91,8 @@ def test_run_after_path( ): # pylint: disable=unused-argument auto_instrumentation.run() self.assertEqual( - environ["PYTHONPATH"] == pathsep.join( + environ["PYTHONPATH"], + pathsep.join( [self.auto_instrumentation_path, "abc"] - ) + ), ) From 650f048c10f15408efc854742d058e2542fb9d31 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 23 Apr 2020 17:25:23 -0600 Subject: [PATCH 05/58] Add lint fixes --- .../tests/test_instrumentor.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/opentelemetry-auto-instrumentation/tests/test_instrumentor.py b/opentelemetry-auto-instrumentation/tests/test_instrumentor.py index da8d95a6d5f..922f4b474a0 100644 --- a/opentelemetry-auto-instrumentation/tests/test_instrumentor.py +++ b/opentelemetry-auto-instrumentation/tests/test_instrumentor.py @@ -74,9 +74,7 @@ def test_run_non_empty( auto_instrumentation.run() self.assertEqual( environ["PYTHONPATH"], - pathsep.join( - [self.auto_instrumentation_path, "abc"] - ), + pathsep.join([self.auto_instrumentation_path, "abc"]), ) @patch.dict( @@ -92,7 +90,5 @@ def test_run_after_path( auto_instrumentation.run() self.assertEqual( environ["PYTHONPATH"], - pathsep.join( - [self.auto_instrumentation_path, "abc"] - ), + pathsep.join([self.auto_instrumentation_path, "abc"]), ) From 2445df5335f1399c7cc44b07cb1f6e8d564029e2 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 24 Apr 2020 15:45:17 -0600 Subject: [PATCH 06/58] Add more tests --- .../tests/test_instrumentor.py | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/opentelemetry-auto-instrumentation/tests/test_instrumentor.py b/opentelemetry-auto-instrumentation/tests/test_instrumentor.py index 922f4b474a0..a768a40eb42 100644 --- a/opentelemetry-auto-instrumentation/tests/test_instrumentor.py +++ b/opentelemetry-auto-instrumentation/tests/test_instrumentor.py @@ -14,12 +14,8 @@ # type: ignore from logging import WARNING -from os import environ -from os.path import abspath, dirname, pathsep from unittest import TestCase -from unittest.mock import patch -from opentelemetry.auto_instrumentation import auto_instrumentation from opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor @@ -49,46 +45,3 @@ def test_protect(self): def test_singleton(self): self.assertIs(self.Instrumentor(), self.Instrumentor()) - - -class TestRun(TestCase): - auto_instrumentation_path = dirname(abspath(auto_instrumentation.__file__)) - - @patch.dict("os.environ", {"PYTHONPATH": ""}) - @patch("opentelemetry.auto_instrumentation.auto_instrumentation.argv") - @patch("opentelemetry.auto_instrumentation.auto_instrumentation.execl") - @patch("opentelemetry.auto_instrumentation.auto_instrumentation.which") - def test_run_empty( - self, mock_which, mock_execl, mock_argv - ): # pylint: disable=unused-argument - auto_instrumentation.run() - self.assertEqual(environ["PYTHONPATH"], self.auto_instrumentation_path) - - @patch.dict("os.environ", {"PYTHONPATH": "abc"}) - @patch("opentelemetry.auto_instrumentation.auto_instrumentation.argv") - @patch("opentelemetry.auto_instrumentation.auto_instrumentation.execl") - @patch("opentelemetry.auto_instrumentation.auto_instrumentation.which") - def test_run_non_empty( - self, mock_which, mock_execl, mock_argv - ): # pylint: disable=unused-argument - auto_instrumentation.run() - self.assertEqual( - environ["PYTHONPATH"], - pathsep.join([self.auto_instrumentation_path, "abc"]), - ) - - @patch.dict( - "os.environ", - {"PYTHONPATH": pathsep.join(["abc", auto_instrumentation_path])}, - ) - @patch("opentelemetry.auto_instrumentation.auto_instrumentation.argv") - @patch("opentelemetry.auto_instrumentation.auto_instrumentation.execl") - @patch("opentelemetry.auto_instrumentation.auto_instrumentation.which") - def test_run_after_path( - self, mock_which, mock_execl, mock_argv - ): # pylint: disable=unused-argument - auto_instrumentation.run() - self.assertEqual( - environ["PYTHONPATH"], - pathsep.join([self.auto_instrumentation_path, "abc"]), - ) From a6e6790e50a2b49583a390e25475638f6383925d Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 16 Apr 2020 16:09:44 -0600 Subject: [PATCH 07/58] Add files --- .../CHANGELOG.md | 3 + .../README.rst | 13 ++ .../example/client.py | 45 +++++++ .../instrumentation_example/__init__.py | 0 .../example/instrumentation_example/asgi.py | 16 +++ .../instrumentation_example/settings.py | 121 ++++++++++++++++++ .../example/instrumentation_example/urls.py | 22 ++++ .../example/instrumentation_example/wsgi.py | 16 +++ .../example/manage.py | 21 +++ .../example/pages/__init__.py | 0 .../example/pages/admin.py | 3 + .../example/pages/apps.py | 5 + .../example/pages/migrations/__init__.py | 0 .../example/pages/models.py | 3 + .../example/pages/tests.py | 3 + .../example/pages/urls.py | 7 + .../example/pages/views.py | 18 +++ .../setup.cfg | 51 ++++++++ .../setup.py | 34 +++++ .../instrumentors/django/__init__.py | 87 +++++++++++++ .../instrumentors/django/middleware.py | 103 +++++++++++++++ .../instrumentors/django/version.py | 15 +++ .../tests/__init__.py | 0 .../tests/conftest.py | 32 +++++ .../tests/settings.py | 120 +++++++++++++++++ .../tests/test_middleware.py | 9 ++ .../tests/urls.py | 7 + .../tests/views.py | 7 + 28 files changed, 761 insertions(+) create mode 100644 ext/opentelemetry-instrumentation-django/CHANGELOG.md create mode 100644 ext/opentelemetry-instrumentation-django/README.rst create mode 100644 ext/opentelemetry-instrumentation-django/example/client.py create mode 100644 ext/opentelemetry-instrumentation-django/example/instrumentation_example/__init__.py create mode 100644 ext/opentelemetry-instrumentation-django/example/instrumentation_example/asgi.py create mode 100644 ext/opentelemetry-instrumentation-django/example/instrumentation_example/settings.py create mode 100644 ext/opentelemetry-instrumentation-django/example/instrumentation_example/urls.py create mode 100644 ext/opentelemetry-instrumentation-django/example/instrumentation_example/wsgi.py create mode 100755 ext/opentelemetry-instrumentation-django/example/manage.py create mode 100644 ext/opentelemetry-instrumentation-django/example/pages/__init__.py create mode 100644 ext/opentelemetry-instrumentation-django/example/pages/admin.py create mode 100644 ext/opentelemetry-instrumentation-django/example/pages/apps.py create mode 100644 ext/opentelemetry-instrumentation-django/example/pages/migrations/__init__.py create mode 100644 ext/opentelemetry-instrumentation-django/example/pages/models.py create mode 100644 ext/opentelemetry-instrumentation-django/example/pages/tests.py create mode 100644 ext/opentelemetry-instrumentation-django/example/pages/urls.py create mode 100644 ext/opentelemetry-instrumentation-django/example/pages/views.py create mode 100644 ext/opentelemetry-instrumentation-django/setup.cfg create mode 100644 ext/opentelemetry-instrumentation-django/setup.py create mode 100644 ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/__init__.py create mode 100644 ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/middleware.py create mode 100644 ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/version.py create mode 100644 ext/opentelemetry-instrumentation-django/tests/__init__.py create mode 100644 ext/opentelemetry-instrumentation-django/tests/conftest.py create mode 100644 ext/opentelemetry-instrumentation-django/tests/settings.py create mode 100644 ext/opentelemetry-instrumentation-django/tests/test_middleware.py create mode 100644 ext/opentelemetry-instrumentation-django/tests/urls.py create mode 100644 ext/opentelemetry-instrumentation-django/tests/views.py diff --git a/ext/opentelemetry-instrumentation-django/CHANGELOG.md b/ext/opentelemetry-instrumentation-django/CHANGELOG.md new file mode 100644 index 00000000000..1512c421622 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/CHANGELOG.md @@ -0,0 +1,3 @@ +# Changelog + +## Unreleased diff --git a/ext/opentelemetry-instrumentation-django/README.rst b/ext/opentelemetry-instrumentation-django/README.rst new file mode 100644 index 00000000000..02e6f94a305 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/README.rst @@ -0,0 +1,13 @@ +OpenTelemetry Django Instrumentor +================================ + +This instrumentor creates OpenTelemetry spans for Django applications. It just +needs to be installed in the virtual environment where OpenTelemetry and the +Django application are. The instrumentation of the Django application will be +done automatically. + +References +---------- + +* `OpenTelemetry Project `_ +* `OpenTelemetry WSGI extension `_ diff --git a/ext/opentelemetry-instrumentation-django/example/client.py b/ext/opentelemetry-instrumentation-django/example/client.py new file mode 100644 index 00000000000..e65285c35d2 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/example/client.py @@ -0,0 +1,45 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. + +from sys import argv + +from requests import get + +from opentelemetry import propagators, trace +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import ( + ConsoleSpanExporter, + SimpleExportSpanProcessor, +) + +trace.set_tracer_provider(TracerProvider()) +tracer = trace.get_tracer_provider().get_tracer(__name__) + +trace.get_tracer_provider().add_span_processor( + SimpleExportSpanProcessor(ConsoleSpanExporter()) +) + + +with tracer.start_as_current_span("client"): + + with tracer.start_as_current_span("client-server"): + headers = {} + propagators.inject(dict.__setitem__, headers) + requested = get( + "http://localhost:8000", + params={"param": argv[1]}, + headers=headers, + ) + + assert requested.status_code == 200 diff --git a/ext/opentelemetry-instrumentation-django/example/instrumentation_example/__init__.py b/ext/opentelemetry-instrumentation-django/example/instrumentation_example/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ext/opentelemetry-instrumentation-django/example/instrumentation_example/asgi.py b/ext/opentelemetry-instrumentation-django/example/instrumentation_example/asgi.py new file mode 100644 index 00000000000..1e58b3e5817 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/example/instrumentation_example/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for instrumentation_example project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'instrumentation_example.settings') + +application = get_asgi_application() diff --git a/ext/opentelemetry-instrumentation-django/example/instrumentation_example/settings.py b/ext/opentelemetry-instrumentation-django/example/instrumentation_example/settings.py new file mode 100644 index 00000000000..8c735c05c46 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/example/instrumentation_example/settings.py @@ -0,0 +1,121 @@ +""" +Django settings for instrumentation_example project. + +Generated by 'django-admin startproject' using Django 3.0.4. + +For more information on this file, see +https://docs.djangoproject.com/en/3.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/3.0/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'it%*!=l2(fcawu=!m-06n&#j(iq2j#%$fu6)myi*b9i5ojk+6+' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'pages.apps.PagesConfig', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'instrumentation_example.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'instrumentation_example.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/3.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/3.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/3.0/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/ext/opentelemetry-instrumentation-django/example/instrumentation_example/urls.py b/ext/opentelemetry-instrumentation-django/example/instrumentation_example/urls.py new file mode 100644 index 00000000000..14d04f0fb87 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/example/instrumentation_example/urls.py @@ -0,0 +1,22 @@ +"""instrumentation_example URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/3.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + path('admin/', admin.site.urls), + path("", include("pages.urls")) +] diff --git a/ext/opentelemetry-instrumentation-django/example/instrumentation_example/wsgi.py b/ext/opentelemetry-instrumentation-django/example/instrumentation_example/wsgi.py new file mode 100644 index 00000000000..95413e96ee6 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/example/instrumentation_example/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for instrumentation_example project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'instrumentation_example.settings') + +application = get_wsgi_application() diff --git a/ext/opentelemetry-instrumentation-django/example/manage.py b/ext/opentelemetry-instrumentation-django/example/manage.py new file mode 100755 index 00000000000..6ce07115f6c --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/example/manage.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'instrumentation_example.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/ext/opentelemetry-instrumentation-django/example/pages/__init__.py b/ext/opentelemetry-instrumentation-django/example/pages/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ext/opentelemetry-instrumentation-django/example/pages/admin.py b/ext/opentelemetry-instrumentation-django/example/pages/admin.py new file mode 100644 index 00000000000..8c38f3f3dad --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/example/pages/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/ext/opentelemetry-instrumentation-django/example/pages/apps.py b/ext/opentelemetry-instrumentation-django/example/pages/apps.py new file mode 100644 index 00000000000..acdb960739b --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/example/pages/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class PagesConfig(AppConfig): + name = 'pages' diff --git a/ext/opentelemetry-instrumentation-django/example/pages/migrations/__init__.py b/ext/opentelemetry-instrumentation-django/example/pages/migrations/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ext/opentelemetry-instrumentation-django/example/pages/models.py b/ext/opentelemetry-instrumentation-django/example/pages/models.py new file mode 100644 index 00000000000..71a83623907 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/example/pages/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/ext/opentelemetry-instrumentation-django/example/pages/tests.py b/ext/opentelemetry-instrumentation-django/example/pages/tests.py new file mode 100644 index 00000000000..7ce503c2dd9 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/example/pages/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/ext/opentelemetry-instrumentation-django/example/pages/urls.py b/ext/opentelemetry-instrumentation-django/example/pages/urls.py new file mode 100644 index 00000000000..aa67b9c7546 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/example/pages/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from .views import home_page_view + + +urlpatterns = [ + path("", home_page_view, name="home") +] diff --git a/ext/opentelemetry-instrumentation-django/example/pages/views.py b/ext/opentelemetry-instrumentation-django/example/pages/views.py new file mode 100644 index 00000000000..a7233aab0ca --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/example/pages/views.py @@ -0,0 +1,18 @@ +from django.http import HttpResponse +from opentelemetry import trace +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import ( + ConsoleSpanExporter, + SimpleExportSpanProcessor, +) + +trace.set_tracer_provider(TracerProvider()) +tracer = trace.get_tracer_provider().get_tracer(__name__) + +trace.get_tracer_provider().add_span_processor( + SimpleExportSpanProcessor(ConsoleSpanExporter()) +) + + +def home_page_view(request): + return HttpResponse("Hello, world") diff --git a/ext/opentelemetry-instrumentation-django/setup.cfg b/ext/opentelemetry-instrumentation-django/setup.cfg new file mode 100644 index 00000000000..55e7262f954 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/setup.cfg @@ -0,0 +1,51 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. +# +[metadata] +name = opentelemetry-instrumentors-django +description = OpenTelemetry Instrumentor for Django +long_description = file: README.rst +long_description_content_type = text/x-rst +author = OpenTelemetry Authors +author_email = cncf-opentelemetry-contributors@lists.cncf.io +url = https://github.com/open-telemetry/opentelemetry-python/ext/opentelemetry-ext-django +platforms = any +license = Apache-2.0 +classifiers = + Development Status :: 3 - Alpha + Intended Audience :: Developers + License :: OSI Approved :: Apache Software License + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.4 + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + +[options] +python_requires = >=3.4 +package_dir= + =src +packages=find_namespace: +install_requires = + opentelemetry-api + opentelemetry-auto-instrumentation + opentelemetry-ext-wsgi + +[options.extras_require] +test = + django~=3.0.4 + +[options.packages.find] +where = src diff --git a/ext/opentelemetry-instrumentation-django/setup.py b/ext/opentelemetry-instrumentation-django/setup.py new file mode 100644 index 00000000000..c99093263ef --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/setup.py @@ -0,0 +1,34 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. + +from os.path import dirname, join +from setuptools import setup + +PACKAGE_INFO = {} +with open( + join( + dirname(__file__), + "src", "opentelemetry", "instrumentors", "django", "version.py" + ) +) as f: + exec(f.read(), PACKAGE_INFO) + +setup( + version=PACKAGE_INFO["__version__"], + entry_points={ + "opentelemetry_instrumentor": [ + "django = opentelemetry.instrumentors.django:DjangoInstrumentor" + ] + }, +) diff --git a/ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/__init__.py b/ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/__init__.py new file mode 100644 index 00000000000..63fc5d9a887 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/__init__.py @@ -0,0 +1,87 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. + +from logging import getLogger +from os import environ + +from django import VERSION +from django.conf import settings + +from opentelemetry.configuration import Configuration + +from opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor + +from opentelemetry.instrumentors.django.middleware import ( + OpenTelemetryMiddleware +) + +_logger = getLogger(__name__) + + +class DjangoInstrumentor(BaseInstrumentor): + """A instrumentor for flask.Django + + See `BaseInstrumentor` + """ + + def _instrument(self): + # Django Middleware is code that is executed before and/or after a + # request. Read about Django middleware here: + # https://docs.djangoproject.com/en/3.0/topics/http/middleware/ + + # This method must set this Django settings attribute: + # MIDDLEWARE: for Django version > 1.0 + # MIDDLEWARE_CLASSES: for Django version <= 1.0 + + # Django settings.MIDDLEWARE is a list of strings, each one a Python + # path to a class or a function that acts as middleware. + + # FIXME this is probably a pattern that will show up in the rest of the + # instrumentors. Find a better way of implementing this. + if ( + hasattr(Configuration(), "django_instrument") + and not Configuration().django_instrument + ): + return + + self._middleware_setting = ( + "MIDDLEWARE" if VERSION >= (1, 10, 0) else "MIDDLEWARE_CLASSES" + ) + + if "DJANGO_SETTINGS_MODULE" not in environ.keys(): + raise Exception( + "Missing environment variable DJANGO_SETTINGS_MODULE" + ) + + settings_middleware = getattr( + settings, + self._middleware_setting, + [] + ) + settings_middleware.append( + ".".join( + [ + OpenTelemetryMiddleware.__module__, + OpenTelemetryMiddleware.__qualname__ + ] + ) + ) + setattr( + settings, + self._middleware_setting, + settings_middleware + ) + + def _uninstrument(self): + pass diff --git a/ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/middleware.py b/ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/middleware.py new file mode 100644 index 00000000000..87d8519d172 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/middleware.py @@ -0,0 +1,103 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. + +from logging import getLogger + +from django import VERSION + +from opentelemetry.propagators import extract +from opentelemetry.context import attach, detach +from opentelemetry.trace import get_tracer, SpanKind +from opentelemetry.ext.wsgi import ( + get_header_from_environ, collect_request_attributes +) +from opentelemetry.instrumentors.django.version import __version__ + +if VERSION >= (1, 10, 0): + # Read more about django.utils.deprecation.MiddlewareMixin here: + # https://docs.djangoproject.com/en/3.0/topics/http/middleware/#django.utils.deprecation.MiddlewareMixin + + # This mixin provides these methods: + # def __init__(self, get_response=None): + # self._get_response = get_reponse + # def __call__(self, request): + # self.process_request(request) + # self.get_response(request) + # self.process_response(request, response) + # return response + from django.utils.deprecation import MiddlewareMixin +else: + MiddlewareMixin = object + + +_logger = getLogger(__name__) + + +class OpenTelemetryMiddleware(MiddlewareMixin): + """Django Middleware for OpenTelemetry + """ + + _environ_starttime_key = "opentelemetry-instrumentor-django.starttime_key" + _environ_span_key = "opentelemetry-instrumentor-django.span_key" + _environ_activation_key = ( + "opentelemetry-instrumentor-djangoactivation_key" + ) + _environ_token = "opentelemetry-instrumentor-django.token" + + def process_view(self, request, view_func, view_args, view_kwargs): + # 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 + + # environ = { + # key.lower().replace('_', '-').replace("http-", "", 1): value + # for key, value in request.META.items() + # } + + environ = request.META + + token = attach(extract(get_header_from_environ, environ)) + + tracer = get_tracer(__name__, __version__) + + attributes = collect_request_attributes(environ) + + span = tracer.start_span( + view_func.__name__, + kind=SpanKind.SERVER, + attributes=attributes, + start_time=environ.get(self._environ_starttime_key), + ) + + 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 + + def process_exception(self, request, exception): + request.META.get(self._environ_activation_key).__exit__( + type(exception), + exception, + getattr(exception, "__traceback__", None) + ) + detach(request.environ.get(self._environ_token)) + + def process_response(self, request, response): + request.META[self._environ_activation_key].__exit__( + None, None, None + ) + detach(request.environ.get(self._environ_token)) + return response diff --git a/ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/version.py b/ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/version.py new file mode 100644 index 00000000000..0941210ca3f --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/version.py @@ -0,0 +1,15 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. + +__version__ = "0.6.dev0" diff --git a/ext/opentelemetry-instrumentation-django/tests/__init__.py b/ext/opentelemetry-instrumentation-django/tests/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ext/opentelemetry-instrumentation-django/tests/conftest.py b/ext/opentelemetry-instrumentation-django/tests/conftest.py new file mode 100644 index 00000000000..473b645ff59 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/tests/conftest.py @@ -0,0 +1,32 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. + +from os import environ + +from django import setup + +from opentelemetry.instrumentors.django import DjangoInstrumentor + + +_django_instrumentor = DjangoInstrumentor() + + +def pytest_sessionstart(session): # pylint: disable=unused-argument + environ.setdefault('DJANGO_SETTINGS_MODULE', 'tests.settings') + setup() + _django_instrumentor.instrument() + + +def pytest_sessionfinish(session): # pylint: disable=unused-argument + _django_instrumentor.uninstrument() diff --git a/ext/opentelemetry-instrumentation-django/tests/settings.py b/ext/opentelemetry-instrumentation-django/tests/settings.py new file mode 100644 index 00000000000..efcc33588ba --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/tests/settings.py @@ -0,0 +1,120 @@ +""" +Django settings for tests project. + +Generated by 'django-admin startproject' using Django 3.0.4. + +For more information on this file, see +https://docs.djangoproject.com/en/3.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/3.0/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'm^c^r=-gx(j_tx)hyisz6%f4**thdz#v-u$)ghhpd^fs-!47t=' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ["testserver"] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'tests.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'tests.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/3.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/3.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/3.0/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/ext/opentelemetry-instrumentation-django/tests/test_middleware.py b/ext/opentelemetry-instrumentation-django/tests/test_middleware.py new file mode 100644 index 00000000000..f38caef0f2f --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/tests/test_middleware.py @@ -0,0 +1,9 @@ +from django.test import SimpleTestCase, Client + + +class TestDjangoOpenTracingMiddleware(SimpleTestCase): + + def test_middleware_traced(self): + client = Client() + response = client.get('/traced/') + assert response['numspans'] == '1' diff --git a/ext/opentelemetry-instrumentation-django/tests/urls.py b/ext/opentelemetry-instrumentation-django/tests/urls.py new file mode 100644 index 00000000000..584595e5d2c --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/tests/urls.py @@ -0,0 +1,7 @@ +from django.conf.urls import url + +from . import views + +urlpatterns = [ + url(r'^traced/', views.traced_func), +] diff --git a/ext/opentelemetry-instrumentation-django/tests/views.py b/ext/opentelemetry-instrumentation-django/tests/views.py new file mode 100644 index 00000000000..2ba850340d6 --- /dev/null +++ b/ext/opentelemetry-instrumentation-django/tests/views.py @@ -0,0 +1,7 @@ +from django.http import HttpResponse + + +def traced_func(request): + response = HttpResponse() + response['numspans'] = 1 + return response From 854ab6016d5afa1bb4e978dbc607abf4359efd1c Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 16 Apr 2020 16:24:20 -0600 Subject: [PATCH 08/58] Rename files --- .../CHANGELOG.md | 0 .../README.rst | 0 .../example/client.py | 0 .../example/instrumentation_example/__init__.py | 0 .../example/instrumentation_example/asgi.py | 0 .../example/instrumentation_example/settings.py | 0 .../example/instrumentation_example/urls.py | 0 .../example/instrumentation_example/wsgi.py | 0 .../example/manage.py | 0 .../example/pages/__init__.py | 0 .../example/pages/admin.py | 0 .../example/pages/apps.py | 0 .../example/pages/migrations/__init__.py | 0 .../example/pages/models.py | 0 .../example/pages/tests.py | 0 .../example/pages/urls.py | 0 .../example/pages/views.py | 0 .../setup.cfg | 0 .../setup.py | 0 .../src/opentelemetry/instrumentors/django/__init__.py | 0 .../src/opentelemetry/instrumentors/django/middleware.py | 0 .../src/opentelemetry/instrumentors/django/version.py | 0 .../tests/__init__.py | 0 .../tests/conftest.py | 0 .../tests/settings.py | 0 .../tests/test_middleware.py | 0 .../tests/urls.py | 0 .../tests/views.py | 0 tox.ini | 7 +++++++ 29 files changed, 7 insertions(+) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/CHANGELOG.md (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/README.rst (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/client.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/instrumentation_example/__init__.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/instrumentation_example/asgi.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/instrumentation_example/settings.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/instrumentation_example/urls.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/instrumentation_example/wsgi.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/manage.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/pages/__init__.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/pages/admin.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/pages/apps.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/pages/migrations/__init__.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/pages/models.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/pages/tests.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/pages/urls.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/example/pages/views.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/setup.cfg (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/setup.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/src/opentelemetry/instrumentors/django/__init__.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/src/opentelemetry/instrumentors/django/middleware.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/src/opentelemetry/instrumentors/django/version.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/tests/__init__.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/tests/conftest.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/tests/settings.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/tests/test_middleware.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/tests/urls.py (100%) rename ext/{opentelemetry-instrumentation-django => opentelemetry-ext-django}/tests/views.py (100%) diff --git a/ext/opentelemetry-instrumentation-django/CHANGELOG.md b/ext/opentelemetry-ext-django/CHANGELOG.md similarity index 100% rename from ext/opentelemetry-instrumentation-django/CHANGELOG.md rename to ext/opentelemetry-ext-django/CHANGELOG.md diff --git a/ext/opentelemetry-instrumentation-django/README.rst b/ext/opentelemetry-ext-django/README.rst similarity index 100% rename from ext/opentelemetry-instrumentation-django/README.rst rename to ext/opentelemetry-ext-django/README.rst diff --git a/ext/opentelemetry-instrumentation-django/example/client.py b/ext/opentelemetry-ext-django/example/client.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/client.py rename to ext/opentelemetry-ext-django/example/client.py diff --git a/ext/opentelemetry-instrumentation-django/example/instrumentation_example/__init__.py b/ext/opentelemetry-ext-django/example/instrumentation_example/__init__.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/instrumentation_example/__init__.py rename to ext/opentelemetry-ext-django/example/instrumentation_example/__init__.py diff --git a/ext/opentelemetry-instrumentation-django/example/instrumentation_example/asgi.py b/ext/opentelemetry-ext-django/example/instrumentation_example/asgi.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/instrumentation_example/asgi.py rename to ext/opentelemetry-ext-django/example/instrumentation_example/asgi.py diff --git a/ext/opentelemetry-instrumentation-django/example/instrumentation_example/settings.py b/ext/opentelemetry-ext-django/example/instrumentation_example/settings.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/instrumentation_example/settings.py rename to ext/opentelemetry-ext-django/example/instrumentation_example/settings.py diff --git a/ext/opentelemetry-instrumentation-django/example/instrumentation_example/urls.py b/ext/opentelemetry-ext-django/example/instrumentation_example/urls.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/instrumentation_example/urls.py rename to ext/opentelemetry-ext-django/example/instrumentation_example/urls.py diff --git a/ext/opentelemetry-instrumentation-django/example/instrumentation_example/wsgi.py b/ext/opentelemetry-ext-django/example/instrumentation_example/wsgi.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/instrumentation_example/wsgi.py rename to ext/opentelemetry-ext-django/example/instrumentation_example/wsgi.py diff --git a/ext/opentelemetry-instrumentation-django/example/manage.py b/ext/opentelemetry-ext-django/example/manage.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/manage.py rename to ext/opentelemetry-ext-django/example/manage.py diff --git a/ext/opentelemetry-instrumentation-django/example/pages/__init__.py b/ext/opentelemetry-ext-django/example/pages/__init__.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/pages/__init__.py rename to ext/opentelemetry-ext-django/example/pages/__init__.py diff --git a/ext/opentelemetry-instrumentation-django/example/pages/admin.py b/ext/opentelemetry-ext-django/example/pages/admin.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/pages/admin.py rename to ext/opentelemetry-ext-django/example/pages/admin.py diff --git a/ext/opentelemetry-instrumentation-django/example/pages/apps.py b/ext/opentelemetry-ext-django/example/pages/apps.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/pages/apps.py rename to ext/opentelemetry-ext-django/example/pages/apps.py diff --git a/ext/opentelemetry-instrumentation-django/example/pages/migrations/__init__.py b/ext/opentelemetry-ext-django/example/pages/migrations/__init__.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/pages/migrations/__init__.py rename to ext/opentelemetry-ext-django/example/pages/migrations/__init__.py diff --git a/ext/opentelemetry-instrumentation-django/example/pages/models.py b/ext/opentelemetry-ext-django/example/pages/models.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/pages/models.py rename to ext/opentelemetry-ext-django/example/pages/models.py diff --git a/ext/opentelemetry-instrumentation-django/example/pages/tests.py b/ext/opentelemetry-ext-django/example/pages/tests.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/pages/tests.py rename to ext/opentelemetry-ext-django/example/pages/tests.py diff --git a/ext/opentelemetry-instrumentation-django/example/pages/urls.py b/ext/opentelemetry-ext-django/example/pages/urls.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/pages/urls.py rename to ext/opentelemetry-ext-django/example/pages/urls.py diff --git a/ext/opentelemetry-instrumentation-django/example/pages/views.py b/ext/opentelemetry-ext-django/example/pages/views.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/example/pages/views.py rename to ext/opentelemetry-ext-django/example/pages/views.py diff --git a/ext/opentelemetry-instrumentation-django/setup.cfg b/ext/opentelemetry-ext-django/setup.cfg similarity index 100% rename from ext/opentelemetry-instrumentation-django/setup.cfg rename to ext/opentelemetry-ext-django/setup.cfg diff --git a/ext/opentelemetry-instrumentation-django/setup.py b/ext/opentelemetry-ext-django/setup.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/setup.py rename to ext/opentelemetry-ext-django/setup.py diff --git a/ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/__init__.py b/ext/opentelemetry-ext-django/src/opentelemetry/instrumentors/django/__init__.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/__init__.py rename to ext/opentelemetry-ext-django/src/opentelemetry/instrumentors/django/__init__.py diff --git a/ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/instrumentors/django/middleware.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/middleware.py rename to ext/opentelemetry-ext-django/src/opentelemetry/instrumentors/django/middleware.py diff --git a/ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/version.py b/ext/opentelemetry-ext-django/src/opentelemetry/instrumentors/django/version.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/src/opentelemetry/instrumentors/django/version.py rename to ext/opentelemetry-ext-django/src/opentelemetry/instrumentors/django/version.py diff --git a/ext/opentelemetry-instrumentation-django/tests/__init__.py b/ext/opentelemetry-ext-django/tests/__init__.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/tests/__init__.py rename to ext/opentelemetry-ext-django/tests/__init__.py diff --git a/ext/opentelemetry-instrumentation-django/tests/conftest.py b/ext/opentelemetry-ext-django/tests/conftest.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/tests/conftest.py rename to ext/opentelemetry-ext-django/tests/conftest.py diff --git a/ext/opentelemetry-instrumentation-django/tests/settings.py b/ext/opentelemetry-ext-django/tests/settings.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/tests/settings.py rename to ext/opentelemetry-ext-django/tests/settings.py diff --git a/ext/opentelemetry-instrumentation-django/tests/test_middleware.py b/ext/opentelemetry-ext-django/tests/test_middleware.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/tests/test_middleware.py rename to ext/opentelemetry-ext-django/tests/test_middleware.py diff --git a/ext/opentelemetry-instrumentation-django/tests/urls.py b/ext/opentelemetry-ext-django/tests/urls.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/tests/urls.py rename to ext/opentelemetry-ext-django/tests/urls.py diff --git a/ext/opentelemetry-instrumentation-django/tests/views.py b/ext/opentelemetry-ext-django/tests/views.py similarity index 100% rename from ext/opentelemetry-instrumentation-django/tests/views.py rename to ext/opentelemetry-ext-django/tests/views.py diff --git a/tox.ini b/tox.ini index 1570df787c6..aecfc1b5a85 100644 --- a/tox.ini +++ b/tox.ini @@ -28,6 +28,10 @@ envlist = py3{4,5,6,7,8}-test-example-http pypy3-test-example-http + ; opentelemetry-ext-django + py3{4,5,6,7,8}-test-ext-django + pypy3-test-ext-django + ; opentelemetry-ext-dbapi py3{4,5,6,7,8}-test-ext-dbapi pypy3-test-ext-dbapi @@ -127,6 +131,7 @@ changedir = test-ext-requests: ext/opentelemetry-ext-requests/tests test-ext-jaeger: ext/opentelemetry-ext-jaeger/tests test-ext-dbapi: ext/opentelemetry-ext-dbapi/tests + test-ext-django: ext/opentelemetry-ext-django/tests test-ext-mysql: ext/opentelemetry-ext-mysql/tests test-ext-otcollector: ext/opentelemetry-ext-otcollector/tests test-ext-prometheus: ext/opentelemetry-ext-prometheus/tests @@ -175,6 +180,8 @@ commands_pre = dbapi: pip install {toxinidir}/ext/opentelemetry-ext-dbapi[test] + django: pip install {toxinidir}/ext/opentelemetry-ext-django + mysql: pip install {toxinidir}/ext/opentelemetry-ext-dbapi mysql: pip install {toxinidir}/ext/opentelemetry-ext-mysql[test] From 5de10067e3688600f25d928b04d85ef4aaf91414 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 17 Apr 2020 14:53:23 -0600 Subject: [PATCH 09/58] Add tox tests --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index aecfc1b5a85..4a0014b713e 100644 --- a/tox.ini +++ b/tox.ini @@ -180,7 +180,7 @@ commands_pre = dbapi: pip install {toxinidir}/ext/opentelemetry-ext-dbapi[test] - django: pip install {toxinidir}/ext/opentelemetry-ext-django + django: pip install {toxinidir}/ext/opentelemetry-ext-django[test] mysql: pip install {toxinidir}/ext/opentelemetry-ext-dbapi mysql: pip install {toxinidir}/ext/opentelemetry-ext-mysql[test] From f3aeca7412413469d1a88c95282b3bf04a5fde62 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Sat, 18 Apr 2020 20:24:05 -0600 Subject: [PATCH 10/58] Rename directory --- .../{instrumentors => ext}/django/__init__.py | 29 ++++++++++++++++++- .../django/middleware.py | 0 .../{instrumentors => ext}/django/version.py | 0 3 files changed, 28 insertions(+), 1 deletion(-) rename ext/opentelemetry-ext-django/src/opentelemetry/{instrumentors => ext}/django/__init__.py (76%) rename ext/opentelemetry-ext-django/src/opentelemetry/{instrumentors => ext}/django/middleware.py (100%) rename ext/opentelemetry-ext-django/src/opentelemetry/{instrumentors => ext}/django/version.py (100%) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/instrumentors/django/__init__.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py similarity index 76% rename from ext/opentelemetry-ext-django/src/opentelemetry/instrumentors/django/__init__.py rename to ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py index 63fc5d9a887..db6828fee0c 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/instrumentors/django/__init__.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py @@ -69,6 +69,7 @@ def _instrument(self): self._middleware_setting, [] ) + settings_middleware.append( ".".join( [ @@ -84,4 +85,30 @@ def _instrument(self): ) def _uninstrument(self): - pass + settings_middleware = getattr( + settings, + self._middleware_setting, + None + ) + + # FIXME This is starting to smell like trouble. We have 2 mechanisms + # that may make this condition be True, one implemented in + # BaseInstrumentor and another one implemented in _instrument. Both + # stop _instrument from running and thus, settings_middleware not being + # set. + if settings_middleware is None: + return + + settings_middleware.remove( + ".".join( + [ + OpenTelemetryMiddleware.__module__, + OpenTelemetryMiddleware.__qualname__ + ] + ) + ) + setattr( + settings, + self._middleware_setting, + settings_middleware + ) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/instrumentors/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py similarity index 100% rename from ext/opentelemetry-ext-django/src/opentelemetry/instrumentors/django/middleware.py rename to ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/instrumentors/django/version.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/version.py similarity index 100% rename from ext/opentelemetry-ext-django/src/opentelemetry/instrumentors/django/version.py rename to ext/opentelemetry-ext-django/src/opentelemetry/ext/django/version.py From d2a3e3bc8e6de1689caa69fcc7df10bbec4f2b8e Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Sun, 19 Apr 2020 13:16:54 -0600 Subject: [PATCH 11/58] Fix test case --- ext/opentelemetry-ext-django/setup.cfg | 2 +- ext/opentelemetry-ext-django/setup.py | 4 ++-- .../src/opentelemetry/ext/django/__init__.py | 12 ++++++------ .../src/opentelemetry/ext/django/middleware.py | 2 +- ext/opentelemetry-ext-django/tests/conftest.py | 2 +- .../tests/test_middleware.py | 12 +++++++++--- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/ext/opentelemetry-ext-django/setup.cfg b/ext/opentelemetry-ext-django/setup.cfg index 55e7262f954..77fc1eaf5a5 100644 --- a/ext/opentelemetry-ext-django/setup.cfg +++ b/ext/opentelemetry-ext-django/setup.cfg @@ -13,7 +13,7 @@ # limitations under the License. # [metadata] -name = opentelemetry-instrumentors-django +name = opentelemetry-ext-django description = OpenTelemetry Instrumentor for Django long_description = file: README.rst long_description_content_type = text/x-rst diff --git a/ext/opentelemetry-ext-django/setup.py b/ext/opentelemetry-ext-django/setup.py index c99093263ef..2f4adc70c88 100644 --- a/ext/opentelemetry-ext-django/setup.py +++ b/ext/opentelemetry-ext-django/setup.py @@ -19,7 +19,7 @@ with open( join( dirname(__file__), - "src", "opentelemetry", "instrumentors", "django", "version.py" + "src", "opentelemetry", "ext", "django", "version.py" ) ) as f: exec(f.read(), PACKAGE_INFO) @@ -28,7 +28,7 @@ version=PACKAGE_INFO["__version__"], entry_points={ "opentelemetry_instrumentor": [ - "django = opentelemetry.instrumentors.django:DjangoInstrumentor" + "django = opentelemetry.ext.django:DjangoInstrumentor" ] }, ) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py index db6828fee0c..322dddba4a6 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py @@ -22,7 +22,7 @@ from opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor -from opentelemetry.instrumentors.django.middleware import ( +from opentelemetry.ext.django.middleware import ( OpenTelemetryMiddleware ) @@ -48,17 +48,17 @@ def _instrument(self): # path to a class or a function that acts as middleware. # FIXME this is probably a pattern that will show up in the rest of the - # instrumentors. Find a better way of implementing this. + # ext. Find a better way of implementing this. + self._middleware_setting = ( + "MIDDLEWARE" if VERSION >= (1, 10, 0) else "MIDDLEWARE_CLASSES" + ) + if ( hasattr(Configuration(), "django_instrument") and not Configuration().django_instrument ): return - self._middleware_setting = ( - "MIDDLEWARE" if VERSION >= (1, 10, 0) else "MIDDLEWARE_CLASSES" - ) - if "DJANGO_SETTINGS_MODULE" not in environ.keys(): raise Exception( "Missing environment variable DJANGO_SETTINGS_MODULE" diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py index 87d8519d172..a3667bdf1e2 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py @@ -22,7 +22,7 @@ from opentelemetry.ext.wsgi import ( get_header_from_environ, collect_request_attributes ) -from opentelemetry.instrumentors.django.version import __version__ +from opentelemetry.ext.django.version import __version__ if VERSION >= (1, 10, 0): # Read more about django.utils.deprecation.MiddlewareMixin here: diff --git a/ext/opentelemetry-ext-django/tests/conftest.py b/ext/opentelemetry-ext-django/tests/conftest.py index 473b645ff59..a695ad3a29e 100644 --- a/ext/opentelemetry-ext-django/tests/conftest.py +++ b/ext/opentelemetry-ext-django/tests/conftest.py @@ -16,7 +16,7 @@ from django import setup -from opentelemetry.instrumentors.django import DjangoInstrumentor +from opentelemetry.ext.django import DjangoInstrumentor _django_instrumentor = DjangoInstrumentor() diff --git a/ext/opentelemetry-ext-django/tests/test_middleware.py b/ext/opentelemetry-ext-django/tests/test_middleware.py index f38caef0f2f..1109fbeca77 100644 --- a/ext/opentelemetry-ext-django/tests/test_middleware.py +++ b/ext/opentelemetry-ext-django/tests/test_middleware.py @@ -1,9 +1,15 @@ -from django.test import SimpleTestCase, Client +from django.test import Client +from opentelemetry.test.wsgitestutil import WsgiTestBase -class TestDjangoOpenTracingMiddleware(SimpleTestCase): +class TestDjangoOpenTracingMiddleware(WsgiTestBase): def test_middleware_traced(self): client = Client() response = client.get('/traced/') - assert response['numspans'] == '1' + span_list = self.memory_exporter.get_finished_spans() + span_list + response + from ipdb import set_trace + set_trace() + True From 5d042baf7ba6cafb3880a0d53450e5bd9c54e289 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Sun, 19 Apr 2020 19:26:23 -0600 Subject: [PATCH 12/58] One test failing --- ext/opentelemetry-ext-django/setup.cfg | 8 +++--- .../src/opentelemetry/ext/django/__init__.py | 25 +++++++++++-------- .../src/opentelemetry/ext/django/version.py | 2 +- .../tests/conftest.py | 3 ++- .../tests/test_middleware.py | 15 ++++++----- ext/opentelemetry-ext-django/tests/urls.py | 6 ++--- ext/opentelemetry-ext-django/tests/views.py | 11 +++++--- tox.ini | 8 +++--- 8 files changed, 45 insertions(+), 33 deletions(-) diff --git a/ext/opentelemetry-ext-django/setup.cfg b/ext/opentelemetry-ext-django/setup.cfg index 77fc1eaf5a5..eb8e9c5709e 100644 --- a/ext/opentelemetry-ext-django/setup.cfg +++ b/ext/opentelemetry-ext-django/setup.cfg @@ -39,13 +39,15 @@ package_dir= =src packages=find_namespace: install_requires = - opentelemetry-api - opentelemetry-auto-instrumentation - opentelemetry-ext-wsgi + django~=3.0.4 + opentelemetry-ext-wsgi == 0.7.dev0 + opentelemetry-auto-instrumentation == 0.7.dev0 + opentelemetry-api == 0.7.dev0 [options.extras_require] test = django~=3.0.4 + opentelemetry-test == 0.7.dev0 [options.packages.find] where = src diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py index 322dddba4a6..a891d849242 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py @@ -53,9 +53,12 @@ def _instrument(self): "MIDDLEWARE" if VERSION >= (1, 10, 0) else "MIDDLEWARE_CLASSES" ) + # FIXME Probably the evaluation of strings into boolean values can be + # built inside the Configuration class itself with the magic method + # __bool__ if ( hasattr(Configuration(), "django_instrument") - and not Configuration().django_instrument + and Configuration().django_instrument != "True" ): return @@ -91,22 +94,24 @@ def _uninstrument(self): None ) + open_telemetry_middleware = ".".join( + [ + OpenTelemetryMiddleware.__module__, + OpenTelemetryMiddleware.__qualname__ + ] + ) + # FIXME This is starting to smell like trouble. We have 2 mechanisms # that may make this condition be True, one implemented in # BaseInstrumentor and another one implemented in _instrument. Both # stop _instrument from running and thus, settings_middleware not being # set. - if settings_middleware is None: + if settings_middleware is None or open_telemetry_middleware not in ( + settings_middleware + ): return - settings_middleware.remove( - ".".join( - [ - OpenTelemetryMiddleware.__module__, - OpenTelemetryMiddleware.__qualname__ - ] - ) - ) + settings_middleware.remove(open_telemetry_middleware) setattr( settings, self._middleware_setting, diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/version.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/version.py index 0941210ca3f..86c61362ab5 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/version.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.6.dev0" +__version__ = "0.7.dev0" diff --git a/ext/opentelemetry-ext-django/tests/conftest.py b/ext/opentelemetry-ext-django/tests/conftest.py index a695ad3a29e..7c910ebb396 100644 --- a/ext/opentelemetry-ext-django/tests/conftest.py +++ b/ext/opentelemetry-ext-django/tests/conftest.py @@ -23,7 +23,8 @@ def pytest_sessionstart(session): # pylint: disable=unused-argument - environ.setdefault('DJANGO_SETTINGS_MODULE', 'tests.settings') + environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") + environ.setdefault("OPENTELEMETRY_PYTHON_DJANGO_INSTRUMENT", "True") setup() _django_instrumentor.instrument() diff --git a/ext/opentelemetry-ext-django/tests/test_middleware.py b/ext/opentelemetry-ext-django/tests/test_middleware.py index 1109fbeca77..4eac38909ff 100644 --- a/ext/opentelemetry-ext-django/tests/test_middleware.py +++ b/ext/opentelemetry-ext-django/tests/test_middleware.py @@ -5,11 +5,10 @@ class TestDjangoOpenTracingMiddleware(WsgiTestBase): def test_middleware_traced(self): - client = Client() - response = client.get('/traced/') - span_list = self.memory_exporter.get_finished_spans() - span_list - response - from ipdb import set_trace - set_trace() - True + Client().get("/traced/") + assert len(self.memory_exporter.get_finished_spans()) == 1 + + def test_middleware_error(self): + with self.assertRaises(ValueError): + Client().get("/error/") + assert len(self.memory_exporter.get_finished_spans()) == 1 diff --git a/ext/opentelemetry-ext-django/tests/urls.py b/ext/opentelemetry-ext-django/tests/urls.py index 584595e5d2c..ad9e1cbfdb1 100644 --- a/ext/opentelemetry-ext-django/tests/urls.py +++ b/ext/opentelemetry-ext-django/tests/urls.py @@ -1,7 +1,7 @@ from django.conf.urls import url - -from . import views +from .views import traced, error urlpatterns = [ - url(r'^traced/', views.traced_func), + url(r'^traced/', traced), + url(r'^error/', error), ] diff --git a/ext/opentelemetry-ext-django/tests/views.py b/ext/opentelemetry-ext-django/tests/views.py index 2ba850340d6..f1e9c2e7c09 100644 --- a/ext/opentelemetry-ext-django/tests/views.py +++ b/ext/opentelemetry-ext-django/tests/views.py @@ -1,7 +1,10 @@ from django.http import HttpResponse -def traced_func(request): - response = HttpResponse() - response['numspans'] = 1 - return response +def traced(request): + return HttpResponse() + + +def error(request): + return HttpResponse() + raise ValueError("error") diff --git a/tox.ini b/tox.ini index 4a0014b713e..9b34a90d7b3 100644 --- a/tox.ini +++ b/tox.ini @@ -173,9 +173,11 @@ commands_pre = grpc: pip install {toxinidir}/ext/opentelemetry-ext-grpc[test] - wsgi,flask: pip install {toxinidir}/ext/opentelemetry-ext-wsgi - - flask: pip install {toxinidir}/opentelemetry-auto-instrumentation + ext: pip install {toxinidir}/opentelemetry-api + wsgi,flask,django: pip install {toxinidir}/tests/util + wsgi,flask,django: pip install {toxinidir}/ext/opentelemetry-ext-wsgi + wsgi,flask,django: pip install {toxinidir}/opentelemetry-sdk + flask,django: pip install {toxinidir}/opentelemetry-auto-instrumentation flask: pip install {toxinidir}/ext/opentelemetry-ext-flask[test] dbapi: pip install {toxinidir}/ext/opentelemetry-ext-dbapi[test] From 045eeaadd00805cc3fedbcc8d2d98934a829d87f Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Sun, 19 Apr 2020 19:28:50 -0600 Subject: [PATCH 13/58] Remove response --- ext/opentelemetry-ext-django/tests/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/opentelemetry-ext-django/tests/views.py b/ext/opentelemetry-ext-django/tests/views.py index f1e9c2e7c09..795e3987584 100644 --- a/ext/opentelemetry-ext-django/tests/views.py +++ b/ext/opentelemetry-ext-django/tests/views.py @@ -6,5 +6,4 @@ def traced(request): def error(request): - return HttpResponse() raise ValueError("error") From c599e0a6a489cb325422c8adb748925d250dac2c Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Sun, 19 Apr 2020 21:16:11 -0600 Subject: [PATCH 14/58] Add more test fixes --- .../src/opentelemetry/ext/django/__init__.py | 33 ++++++++----------- .../tests/settings.py | 3 ++ .../tests/test_middleware.py | 5 ++- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py index a891d849242..86b556cbc94 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py @@ -35,6 +35,13 @@ class DjangoInstrumentor(BaseInstrumentor): See `BaseInstrumentor` """ + _opentelemetry_middleware = ".".join( + [ + OpenTelemetryMiddleware.__module__, + OpenTelemetryMiddleware.__qualname__ + ] + ) + def _instrument(self): # Django Middleware is code that is executed before and/or after a # request. Read about Django middleware here: @@ -73,18 +80,13 @@ def _instrument(self): [] ) - settings_middleware.append( - ".".join( - [ - OpenTelemetryMiddleware.__module__, - OpenTelemetryMiddleware.__qualname__ - ] - ) - ) + settings_middleware.append(self._opentelemetry_middleware) + setattr( settings, self._middleware_setting, - settings_middleware + settings_middleware, + # [self._opentelemetry_middleware] ) def _uninstrument(self): @@ -94,24 +96,17 @@ def _uninstrument(self): None ) - open_telemetry_middleware = ".".join( - [ - OpenTelemetryMiddleware.__module__, - OpenTelemetryMiddleware.__qualname__ - ] - ) - # FIXME This is starting to smell like trouble. We have 2 mechanisms # that may make this condition be True, one implemented in # BaseInstrumentor and another one implemented in _instrument. Both # stop _instrument from running and thus, settings_middleware not being # set. - if settings_middleware is None or open_telemetry_middleware not in ( - settings_middleware + if settings_middleware is None or ( + self._opentelemetry_middleware not in settings_middleware ): return - settings_middleware.remove(open_telemetry_middleware) + settings_middleware.remove(self._opentelemetry_middleware) setattr( settings, self._middleware_setting, diff --git a/ext/opentelemetry-ext-django/tests/settings.py b/ext/opentelemetry-ext-django/tests/settings.py index efcc33588ba..47440d7d4e8 100644 --- a/ext/opentelemetry-ext-django/tests/settings.py +++ b/ext/opentelemetry-ext-django/tests/settings.py @@ -24,6 +24,9 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True +# FIXME this is needed in order for test_middleware_error to pass. Apparently +# without it Django will try to render some HTML causing an error (apparently)# unrelated to process_exception. +DEBUG_PROPAGATE_EXCEPTIONS = True ALLOWED_HOSTS = ["testserver"] diff --git a/ext/opentelemetry-ext-django/tests/test_middleware.py b/ext/opentelemetry-ext-django/tests/test_middleware.py index 4eac38909ff..13292ff51e3 100644 --- a/ext/opentelemetry-ext-django/tests/test_middleware.py +++ b/ext/opentelemetry-ext-django/tests/test_middleware.py @@ -1,8 +1,8 @@ -from django.test import Client +from django.test import Client, SimpleTestCase from opentelemetry.test.wsgitestutil import WsgiTestBase -class TestDjangoOpenTracingMiddleware(WsgiTestBase): +class TestDjangoOpenTracingMiddleware(WsgiTestBase, SimpleTestCase): def test_middleware_traced(self): Client().get("/traced/") @@ -11,4 +11,3 @@ def test_middleware_traced(self): def test_middleware_error(self): with self.assertRaises(ValueError): Client().get("/error/") - assert len(self.memory_exporter.get_finished_spans()) == 1 From b1fe718e7441cd69eee53c8801a45755db02fb73 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Sun, 19 Apr 2020 22:11:00 -0600 Subject: [PATCH 15/58] Linting --- ext/opentelemetry-ext-django/README.rst | 12 +-- .../example/instrumentation_example/asgi.py | 4 +- .../instrumentation_example/settings.py | 76 +++++++++---------- .../example/instrumentation_example/urls.py | 12 +-- .../example/instrumentation_example/wsgi.py | 4 +- .../example/manage.py | 8 +- .../example/pages/apps.py | 2 +- .../example/pages/urls.py | 6 +- .../example/pages/views.py | 1 + ext/opentelemetry-ext-django/setup.py | 7 +- .../src/opentelemetry/ext/django/__init__.py | 45 ++++------- .../opentelemetry/ext/django/middleware.py | 21 ++--- .../tests/conftest.py | 1 - .../tests/settings.py | 74 +++++++++--------- .../tests/test_middleware.py | 2 +- ext/opentelemetry-ext-django/tests/urls.py | 7 +- ext/opentelemetry-ext-django/tests/views.py | 2 +- 17 files changed, 138 insertions(+), 146 deletions(-) diff --git a/ext/opentelemetry-ext-django/README.rst b/ext/opentelemetry-ext-django/README.rst index 02e6f94a305..e0f896f2bf3 100644 --- a/ext/opentelemetry-ext-django/README.rst +++ b/ext/opentelemetry-ext-django/README.rst @@ -1,10 +1,10 @@ -OpenTelemetry Django Instrumentor -================================ +OpenTelemetry Django Instrumentation +==================================== -This instrumentor creates OpenTelemetry spans for Django applications. It just -needs to be installed in the virtual environment where OpenTelemetry and the -Django application are. The instrumentation of the Django application will be -done automatically. +This instrumentation creates OpenTelemetry spans for Django applications. It +just needs to be installed in the virtual environment where OpenTelemetry and +the Django application are. The instrumentation of the Django application will +be done automatically. References ---------- diff --git a/ext/opentelemetry-ext-django/example/instrumentation_example/asgi.py b/ext/opentelemetry-ext-django/example/instrumentation_example/asgi.py index 1e58b3e5817..b75cbf51ef9 100644 --- a/ext/opentelemetry-ext-django/example/instrumentation_example/asgi.py +++ b/ext/opentelemetry-ext-django/example/instrumentation_example/asgi.py @@ -11,6 +11,8 @@ from django.core.asgi import get_asgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'instrumentation_example.settings') +os.environ.setdefault( + "DJANGO_SETTINGS_MODULE", "instrumentation_example.settings" +) application = get_asgi_application() diff --git a/ext/opentelemetry-ext-django/example/instrumentation_example/settings.py b/ext/opentelemetry-ext-django/example/instrumentation_example/settings.py index 8c735c05c46..1b4fbd5b441 100644 --- a/ext/opentelemetry-ext-django/example/instrumentation_example/settings.py +++ b/ext/opentelemetry-ext-django/example/instrumentation_example/settings.py @@ -1,7 +1,7 @@ """ Django settings for instrumentation_example project. -Generated by 'django-admin startproject' using Django 3.0.4. +Generated by "django-admin startproject" using Django 3.0.4. For more information on this file, see https://docs.djangoproject.com/en/3.0/topics/settings/ @@ -20,9 +20,9 @@ # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'it%*!=l2(fcawu=!m-06n&#j(iq2j#%$fu6)myi*b9i5ojk+6+' +SECRET_KEY = "it%*!=l2(fcawu=!m-06n&#j(iq2j#%$fu6)myi*b9i5ojk+6+" -# SECURITY WARNING: don't run with debug turned on in production! +# SECURITY WARNING: don"t run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] @@ -31,53 +31,53 @@ # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'pages.apps.PagesConfig', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "pages.apps.PagesConfig", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -ROOT_URLCONF = 'instrumentation_example.urls' +ROOT_URLCONF = "instrumentation_example.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, ] -WSGI_APPLICATION = 'instrumentation_example.wsgi.application' +WSGI_APPLICATION = "instrumentation_example.wsgi.application" # Database # https://docs.djangoproject.com/en/3.0/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } } @@ -87,16 +87,16 @@ AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] @@ -104,9 +104,9 @@ # Internationalization # https://docs.djangoproject.com/en/3.0/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -118,4 +118,4 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.0/howto/static-files/ -STATIC_URL = '/static/' +STATIC_URL = "/static/" diff --git a/ext/opentelemetry-ext-django/example/instrumentation_example/urls.py b/ext/opentelemetry-ext-django/example/instrumentation_example/urls.py index 14d04f0fb87..724aeafca0f 100644 --- a/ext/opentelemetry-ext-django/example/instrumentation_example/urls.py +++ b/ext/opentelemetry-ext-django/example/instrumentation_example/urls.py @@ -5,18 +5,18 @@ Examples: Function views 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') + 2. Add a URL to urlpatterns: path("", views.home, name="home") Class-based views 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') + 2. Add a URL to urlpatterns: path("", Home.as_view(), name="home") Including another URLconf 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) + 2. Add a URL to urlpatterns: path("blog/", include("blog.urls")) """ from django.contrib import admin -from django.urls import path, include +from django.urls import include, path urlpatterns = [ - path('admin/', admin.site.urls), - path("", include("pages.urls")) + path("admin/", admin.site.urls), + path("", include("pages.urls")), ] diff --git a/ext/opentelemetry-ext-django/example/instrumentation_example/wsgi.py b/ext/opentelemetry-ext-django/example/instrumentation_example/wsgi.py index 95413e96ee6..d1031bb8ec3 100644 --- a/ext/opentelemetry-ext-django/example/instrumentation_example/wsgi.py +++ b/ext/opentelemetry-ext-django/example/instrumentation_example/wsgi.py @@ -11,6 +11,8 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'instrumentation_example.settings') +os.environ.setdefault( + "DJANGO_SETTINGS_MODULE", "instrumentation_example.settings" +) application = get_wsgi_application() diff --git a/ext/opentelemetry-ext-django/example/manage.py b/ext/opentelemetry-ext-django/example/manage.py index 6ce07115f6c..592d0f377b4 100755 --- a/ext/opentelemetry-ext-django/example/manage.py +++ b/ext/opentelemetry-ext-django/example/manage.py @@ -1,11 +1,13 @@ #!/usr/bin/env python -"""Django's command-line utility for administrative tasks.""" +"""Django"s command-line utility for administrative tasks.""" import os import sys def main(): - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'instrumentation_example.settings') + os.environ.setdefault( + "DJANGO_SETTINGS_MODULE", "instrumentation_example.settings" + ) try: from django.core.management import execute_from_command_line except ImportError as exc: @@ -17,5 +19,5 @@ def main(): execute_from_command_line(sys.argv) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ext/opentelemetry-ext-django/example/pages/apps.py b/ext/opentelemetry-ext-django/example/pages/apps.py index acdb960739b..344e0f0cf68 100644 --- a/ext/opentelemetry-ext-django/example/pages/apps.py +++ b/ext/opentelemetry-ext-django/example/pages/apps.py @@ -2,4 +2,4 @@ class PagesConfig(AppConfig): - name = 'pages' + name = "pages" diff --git a/ext/opentelemetry-ext-django/example/pages/urls.py b/ext/opentelemetry-ext-django/example/pages/urls.py index aa67b9c7546..485f176f8f5 100644 --- a/ext/opentelemetry-ext-django/example/pages/urls.py +++ b/ext/opentelemetry-ext-django/example/pages/urls.py @@ -1,7 +1,5 @@ from django.urls import path -from .views import home_page_view +from .views import home_page_view -urlpatterns = [ - path("", home_page_view, name="home") -] +urlpatterns = [path("", home_page_view, name="home")] diff --git a/ext/opentelemetry-ext-django/example/pages/views.py b/ext/opentelemetry-ext-django/example/pages/views.py index a7233aab0ca..3f20467cd70 100644 --- a/ext/opentelemetry-ext-django/example/pages/views.py +++ b/ext/opentelemetry-ext-django/example/pages/views.py @@ -1,4 +1,5 @@ from django.http import HttpResponse + from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import ( diff --git a/ext/opentelemetry-ext-django/setup.py b/ext/opentelemetry-ext-django/setup.py index 2f4adc70c88..84ea533fa43 100644 --- a/ext/opentelemetry-ext-django/setup.py +++ b/ext/opentelemetry-ext-django/setup.py @@ -13,13 +13,18 @@ # limitations under the License. from os.path import dirname, join + from setuptools import setup PACKAGE_INFO = {} with open( join( dirname(__file__), - "src", "opentelemetry", "ext", "django", "version.py" + "src", + "opentelemetry", + "ext", + "django", + "version.py", ) ) as f: exec(f.read(), PACKAGE_INFO) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py index 86b556cbc94..20c3eb1b70b 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py @@ -18,13 +18,9 @@ from django import VERSION from django.conf import settings -from opentelemetry.configuration import Configuration - from opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor - -from opentelemetry.ext.django.middleware import ( - OpenTelemetryMiddleware -) +from opentelemetry.configuration import Configuration +from opentelemetry.ext.django.middleware import OpenTelemetryMiddleware _logger = getLogger(__name__) @@ -38,11 +34,12 @@ class DjangoInstrumentor(BaseInstrumentor): _opentelemetry_middleware = ".".join( [ OpenTelemetryMiddleware.__module__, - OpenTelemetryMiddleware.__qualname__ + OpenTelemetryMiddleware.__qualname__, ] ) - def _instrument(self): + def __init__(self): + super().__init__() # Django Middleware is code that is executed before and/or after a # request. Read about Django middleware here: # https://docs.djangoproject.com/en/3.0/topics/http/middleware/ @@ -54,12 +51,14 @@ def _instrument(self): # Django settings.MIDDLEWARE is a list of strings, each one a Python # path to a class or a function that acts as middleware. - # FIXME this is probably a pattern that will show up in the rest of the - # ext. Find a better way of implementing this. self._middleware_setting = ( "MIDDLEWARE" if VERSION >= (1, 10, 0) else "MIDDLEWARE_CLASSES" ) + def _instrument(self): + + # FIXME this is probably a pattern that will show up in the rest of the + # ext. Find a better way of implementing this. # FIXME Probably the evaluation of strings into boolean values can be # built inside the Configuration class itself with the magic method # __bool__ @@ -74,27 +73,13 @@ def _instrument(self): "Missing environment variable DJANGO_SETTINGS_MODULE" ) - settings_middleware = getattr( - settings, - self._middleware_setting, - [] - ) - + settings_middleware = getattr(settings, self._middleware_setting, []) settings_middleware.append(self._opentelemetry_middleware) - setattr( - settings, - self._middleware_setting, - settings_middleware, - # [self._opentelemetry_middleware] - ) + setattr(settings, self._middleware_setting, settings_middleware) def _uninstrument(self): - settings_middleware = getattr( - settings, - self._middleware_setting, - None - ) + settings_middleware = getattr(settings, self._middleware_setting, None) # FIXME This is starting to smell like trouble. We have 2 mechanisms # that may make this condition be True, one implemented in @@ -107,8 +92,4 @@ def _uninstrument(self): return settings_middleware.remove(self._opentelemetry_middleware) - setattr( - settings, - self._middleware_setting, - settings_middleware - ) + setattr(settings, self._middleware_setting, settings_middleware) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py index a3667bdf1e2..e91f072a04d 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py @@ -16,13 +16,14 @@ from django import VERSION -from opentelemetry.propagators import extract from opentelemetry.context import attach, detach -from opentelemetry.trace import get_tracer, SpanKind +from opentelemetry.ext.django.version import __version__ from opentelemetry.ext.wsgi import ( - get_header_from_environ, collect_request_attributes + collect_request_attributes, + get_header_from_environ, ) -from opentelemetry.ext.django.version import __version__ +from opentelemetry.propagators import extract +from opentelemetry.trace import SpanKind, get_tracer if VERSION >= (1, 10, 0): # Read more about django.utils.deprecation.MiddlewareMixin here: @@ -51,11 +52,13 @@ class OpenTelemetryMiddleware(MiddlewareMixin): _environ_starttime_key = "opentelemetry-instrumentor-django.starttime_key" _environ_span_key = "opentelemetry-instrumentor-django.span_key" _environ_activation_key = ( - "opentelemetry-instrumentor-djangoactivation_key" + "opentelemetry-instrumentor-django.activation_key" ) _environ_token = "opentelemetry-instrumentor-django.token" - def process_view(self, request, view_func, view_args, view_kwargs): + def process_view( + self, request, view_func, view_args, view_kwargs + ): # pylint: disable=unused-argument # 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 @@ -91,13 +94,11 @@ def process_exception(self, request, exception): request.META.get(self._environ_activation_key).__exit__( type(exception), exception, - getattr(exception, "__traceback__", None) + getattr(exception, "__traceback__", None), ) detach(request.environ.get(self._environ_token)) def process_response(self, request, response): - request.META[self._environ_activation_key].__exit__( - None, None, None - ) + request.META[self._environ_activation_key].__exit__(None, None, None) detach(request.environ.get(self._environ_token)) return response diff --git a/ext/opentelemetry-ext-django/tests/conftest.py b/ext/opentelemetry-ext-django/tests/conftest.py index 7c910ebb396..3750f92fc7e 100644 --- a/ext/opentelemetry-ext-django/tests/conftest.py +++ b/ext/opentelemetry-ext-django/tests/conftest.py @@ -18,7 +18,6 @@ from opentelemetry.ext.django import DjangoInstrumentor - _django_instrumentor = DjangoInstrumentor() diff --git a/ext/opentelemetry-ext-django/tests/settings.py b/ext/opentelemetry-ext-django/tests/settings.py index 47440d7d4e8..638d2cfc210 100644 --- a/ext/opentelemetry-ext-django/tests/settings.py +++ b/ext/opentelemetry-ext-django/tests/settings.py @@ -1,7 +1,7 @@ """ Django settings for tests project. -Generated by 'django-admin startproject' using Django 3.0.4. +Generated by "django-admin startproject" using Django 3.0.4. For more information on this file, see https://docs.djangoproject.com/en/3.0/topics/settings/ @@ -20,9 +20,9 @@ # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'm^c^r=-gx(j_tx)hyisz6%f4**thdz#v-u$)ghhpd^fs-!47t=' +SECRET_KEY = "m^c^r=-gx(j_tx)hyisz6%f4**thdz#v-u$)ghhpd^fs-!47t=" -# SECURITY WARNING: don't run with debug turned on in production! +# SECURITY WARNING: don"t run with debug turned on in production! DEBUG = True # FIXME this is needed in order for test_middleware_error to pass. Apparently # without it Django will try to render some HTML causing an error (apparently)# unrelated to process_exception. @@ -34,52 +34,52 @@ # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -ROOT_URLCONF = 'tests.urls' +ROOT_URLCONF = "tests.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, ] -WSGI_APPLICATION = 'tests.wsgi.application' +WSGI_APPLICATION = "tests.wsgi.application" # Database # https://docs.djangoproject.com/en/3.0/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } } @@ -89,16 +89,16 @@ AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] @@ -106,9 +106,9 @@ # Internationalization # https://docs.djangoproject.com/en/3.0/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -120,4 +120,4 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.0/howto/static-files/ -STATIC_URL = '/static/' +STATIC_URL = "/static/" diff --git a/ext/opentelemetry-ext-django/tests/test_middleware.py b/ext/opentelemetry-ext-django/tests/test_middleware.py index 13292ff51e3..9422e9ccbb9 100644 --- a/ext/opentelemetry-ext-django/tests/test_middleware.py +++ b/ext/opentelemetry-ext-django/tests/test_middleware.py @@ -1,9 +1,9 @@ from django.test import Client, SimpleTestCase + from opentelemetry.test.wsgitestutil import WsgiTestBase class TestDjangoOpenTracingMiddleware(WsgiTestBase, SimpleTestCase): - def test_middleware_traced(self): Client().get("/traced/") assert len(self.memory_exporter.get_finished_spans()) == 1 diff --git a/ext/opentelemetry-ext-django/tests/urls.py b/ext/opentelemetry-ext-django/tests/urls.py index ad9e1cbfdb1..59f65245111 100644 --- a/ext/opentelemetry-ext-django/tests/urls.py +++ b/ext/opentelemetry-ext-django/tests/urls.py @@ -1,7 +1,8 @@ from django.conf.urls import url -from .views import traced, error + +from .views import error, traced # pylint: disable=import-error urlpatterns = [ - url(r'^traced/', traced), - url(r'^error/', error), + url(r"^traced/", traced), + url(r"^error/", error), ] diff --git a/ext/opentelemetry-ext-django/tests/views.py b/ext/opentelemetry-ext-django/tests/views.py index 795e3987584..9a885f253b4 100644 --- a/ext/opentelemetry-ext-django/tests/views.py +++ b/ext/opentelemetry-ext-django/tests/views.py @@ -1,7 +1,7 @@ from django.http import HttpResponse -def traced(request): +def traced(request): # pylint: disable=unused-argument return HttpResponse() From 627fd9a199e8007cb5e2fd115780cec769841164 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Sun, 19 Apr 2020 22:29:38 -0600 Subject: [PATCH 16/58] Work around old versions --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 9b34a90d7b3..6d3b43ab0f9 100644 --- a/tox.ini +++ b/tox.ini @@ -28,8 +28,9 @@ envlist = py3{4,5,6,7,8}-test-example-http pypy3-test-example-http + ; FIXME find versions of Django that can work with these old Python versions ; opentelemetry-ext-django - py3{4,5,6,7,8}-test-ext-django + py3{6,7,8}-test-ext-django pypy3-test-ext-django ; opentelemetry-ext-dbapi From 2372a33b5085035ea732712027e753b375d5a3ef Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Mon, 20 Apr 2020 16:30:53 -0600 Subject: [PATCH 17/58] Remove mention to virtual environment --- ext/opentelemetry-ext-django/README.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ext/opentelemetry-ext-django/README.rst b/ext/opentelemetry-ext-django/README.rst index e0f896f2bf3..425d6b06d6b 100644 --- a/ext/opentelemetry-ext-django/README.rst +++ b/ext/opentelemetry-ext-django/README.rst @@ -1,13 +1,15 @@ OpenTelemetry Django Instrumentation ==================================== -This instrumentation creates OpenTelemetry spans for Django applications. It -just needs to be installed in the virtual environment where OpenTelemetry and -the Django application are. The instrumentation of the Django application will -be done automatically. +This instrumentation creates OpenTelemetry spans for Django applications. + +Once this package is installed, run your Django application like this: + +`opentelemetry-python-autoinstrumentation python3 manage.py runserver` References ---------- * `OpenTelemetry Project `_ * `OpenTelemetry WSGI extension `_ +* `OpenTelemetry Django extension `_ From d1a9e59a911759d9e54d37248ae36528b8059a22 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Mon, 20 Apr 2020 17:12:38 -0600 Subject: [PATCH 18/58] Fixing setup --- ext/opentelemetry-ext-django/setup.cfg | 11 +++++++---- ext/opentelemetry-ext-django/setup.py | 9 +-------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/ext/opentelemetry-ext-django/setup.cfg b/ext/opentelemetry-ext-django/setup.cfg index eb8e9c5709e..c02527d1691 100644 --- a/ext/opentelemetry-ext-django/setup.cfg +++ b/ext/opentelemetry-ext-django/setup.cfg @@ -23,18 +23,17 @@ url = https://github.com/open-telemetry/opentelemetry-python/ext/opentelemetry-e platforms = any license = Apache-2.0 classifiers = - Development Status :: 3 - Alpha + Development Status :: 3 - Beta Intended Audience :: Developers License :: OSI Approved :: Apache Software License Programming Language :: Python Programming Language :: Python :: 3 - Programming Language :: Python :: 3.4 - Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 [options] -python_requires = >=3.4 +python_requires = >=3.6 package_dir= =src packages=find_namespace: @@ -51,3 +50,7 @@ test = [options.packages.find] where = src + +[options.entry_points] +opentelemetry_instrumentor = + django = opentelemetry.ext.django:DjangoInstrumentor diff --git a/ext/opentelemetry-ext-django/setup.py b/ext/opentelemetry-ext-django/setup.py index 84ea533fa43..45cc68c0f42 100644 --- a/ext/opentelemetry-ext-django/setup.py +++ b/ext/opentelemetry-ext-django/setup.py @@ -29,11 +29,4 @@ ) as f: exec(f.read(), PACKAGE_INFO) -setup( - version=PACKAGE_INFO["__version__"], - entry_points={ - "opentelemetry_instrumentor": [ - "django = opentelemetry.ext.django:DjangoInstrumentor" - ] - }, -) +setup(version=PACKAGE_INFO["__version__"]) From d8500d16b08b509d5f82bf4772656fa9a0938248 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Mon, 20 Apr 2020 17:42:33 -0600 Subject: [PATCH 19/58] Remove class attributes --- .../src/opentelemetry/ext/django/middleware.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py index e91f072a04d..689c9b5443b 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py @@ -49,8 +49,6 @@ class OpenTelemetryMiddleware(MiddlewareMixin): """Django Middleware for OpenTelemetry """ - _environ_starttime_key = "opentelemetry-instrumentor-django.starttime_key" - _environ_span_key = "opentelemetry-instrumentor-django.span_key" _environ_activation_key = ( "opentelemetry-instrumentor-django.activation_key" ) @@ -80,14 +78,16 @@ def process_view( view_func.__name__, kind=SpanKind.SERVER, attributes=attributes, - start_time=environ.get(self._environ_starttime_key), + start_time=environ.get( + "opentelemetry-instrumentor-django.starttime_key" + ) ) 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["opentelemetry-instrumentor-django.span_key"] = span request.META[self._environ_token] = token def process_exception(self, request, exception): From 9914165febe62b7f011f52614460e59923e5385d Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Mon, 20 Apr 2020 17:52:51 -0600 Subject: [PATCH 20/58] Update ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Mauricio Vásquez --- .../src/opentelemetry/ext/django/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py index 20c3eb1b70b..628b3f15749 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py @@ -68,7 +68,7 @@ def _instrument(self): ): return - if "DJANGO_SETTINGS_MODULE" not in environ.keys(): + if "DJANGO_SETTINGS_MODULE" not in environ: raise Exception( "Missing environment variable DJANGO_SETTINGS_MODULE" ) From a54c2d7f790f11d1893985f8e491c4d34cc0caa0 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 21 Apr 2020 13:14:39 -0600 Subject: [PATCH 21/58] Collect response attributes --- .../src/opentelemetry/ext/django/middleware.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py index 689c9b5443b..e4351fb1069 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py @@ -21,6 +21,7 @@ from opentelemetry.ext.wsgi import ( collect_request_attributes, get_header_from_environ, + add_response_attributes, ) from opentelemetry.propagators import extract from opentelemetry.trace import SpanKind, get_tracer @@ -53,6 +54,7 @@ class OpenTelemetryMiddleware(MiddlewareMixin): "opentelemetry-instrumentor-django.activation_key" ) _environ_token = "opentelemetry-instrumentor-django.token" + _environ_span_key = "opentelemetry-instrumentor-django.span_key" def process_view( self, request, view_func, view_args, view_kwargs @@ -87,7 +89,7 @@ def process_view( activation.__enter__() request.META[self._environ_activation_key] = activation - request.META["opentelemetry-instrumentor-django.span_key"] = span + request.META[self._environ_span_key] = span request.META[self._environ_token] = token def process_exception(self, request, exception): @@ -100,5 +102,10 @@ def process_exception(self, request, exception): def process_response(self, request, response): request.META[self._environ_activation_key].__exit__(None, None, None) + add_response_attributes( + request.META[self._environ_span_key], + "{} {}".format(response.status_code, response.reason_phrase), + response + ) detach(request.environ.get(self._environ_token)) return response From 1c71a4fc57912ea28431146f9e2fea1e361d2ebb Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 21 Apr 2020 13:32:50 -0600 Subject: [PATCH 22/58] Add more test fixes --- .../src/opentelemetry/ext/django/middleware.py | 2 +- ext/opentelemetry-ext-django/tests/test_middleware.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py index e4351fb1069..4f13b17306c 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py @@ -101,11 +101,11 @@ def process_exception(self, request, exception): detach(request.environ.get(self._environ_token)) def process_response(self, request, response): - request.META[self._environ_activation_key].__exit__(None, None, None) add_response_attributes( request.META[self._environ_span_key], "{} {}".format(response.status_code, response.reason_phrase), response ) + request.META[self._environ_activation_key].__exit__(None, None, None) detach(request.environ.get(self._environ_token)) return response diff --git a/ext/opentelemetry-ext-django/tests/test_middleware.py b/ext/opentelemetry-ext-django/tests/test_middleware.py index 9422e9ccbb9..ddf237227d9 100644 --- a/ext/opentelemetry-ext-django/tests/test_middleware.py +++ b/ext/opentelemetry-ext-django/tests/test_middleware.py @@ -6,8 +6,9 @@ class TestDjangoOpenTracingMiddleware(WsgiTestBase, SimpleTestCase): def test_middleware_traced(self): Client().get("/traced/") - assert len(self.memory_exporter.get_finished_spans()) == 1 + self.assertEqual(len(self.memory_exporter.get_finished_spans()), 1) def test_middleware_error(self): with self.assertRaises(ValueError): Client().get("/error/") + self.assertEqual(len(self.memory_exporter.get_finished_spans()), 1) From 86354d69b7a7540075d05597121f00b5307574c2 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 21 Apr 2020 13:36:12 -0600 Subject: [PATCH 23/58] Add copyright headers --- .../tests/test_middleware.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ext/opentelemetry-ext-django/tests/test_middleware.py b/ext/opentelemetry-ext-django/tests/test_middleware.py index ddf237227d9..cd18f73c60f 100644 --- a/ext/opentelemetry-ext-django/tests/test_middleware.py +++ b/ext/opentelemetry-ext-django/tests/test_middleware.py @@ -1,3 +1,17 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. + from django.test import Client, SimpleTestCase from opentelemetry.test.wsgitestutil import WsgiTestBase From ca2156635879b9b89857460aedc3ed8c1e2fff12 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 21 Apr 2020 18:56:33 -0600 Subject: [PATCH 24/58] Refactor testing WIP Co-authored-by: Mathieu Hinderyckx --- .../tests/conftest.py | 12 +++--- .../tests/test_middleware.py | 37 ++++++++++++++++++- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/ext/opentelemetry-ext-django/tests/conftest.py b/ext/opentelemetry-ext-django/tests/conftest.py index 3750f92fc7e..56bfb1aadef 100644 --- a/ext/opentelemetry-ext-django/tests/conftest.py +++ b/ext/opentelemetry-ext-django/tests/conftest.py @@ -14,7 +14,7 @@ from os import environ -from django import setup +# from django import setup from opentelemetry.ext.django import DjangoInstrumentor @@ -22,11 +22,13 @@ def pytest_sessionstart(session): # pylint: disable=unused-argument - environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") + # environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") environ.setdefault("OPENTELEMETRY_PYTHON_DJANGO_INSTRUMENT", "True") - setup() - _django_instrumentor.instrument() + # setup() + # _django_instrumentor.instrument() + pass def pytest_sessionfinish(session): # pylint: disable=unused-argument - _django_instrumentor.uninstrument() + # _django_instrumentor.uninstrument() + pass diff --git a/ext/opentelemetry-ext-django/tests/test_middleware.py b/ext/opentelemetry-ext-django/tests/test_middleware.py index cd18f73c60f..4c96ec89263 100644 --- a/ext/opentelemetry-ext-django/tests/test_middleware.py +++ b/ext/opentelemetry-ext-django/tests/test_middleware.py @@ -12,12 +12,45 @@ # See the License for the specific language governing permissions and # limitations under the License. -from django.test import Client, SimpleTestCase +from sys import modules + +from django.test import Client +from django.conf import settings +from django.conf.urls import url +from django.test.utils import ( + setup_test_environment, teardown_test_environment +) from opentelemetry.test.wsgitestutil import WsgiTestBase +from opentelemetry.ext.django import DjangoInstrumentor + +from .views import error, traced # pylint: disable=import-error + +urlpatterns = [ + url(r"^traced/", traced), + url(r"^error/", error), +] +_django_instrumentor = DjangoInstrumentor() + + +# class TestDjangoOpenTracingMiddleware(WsgiTestBase, SimpleTestCase): +class TestDjangoOpenTracingMiddleware(WsgiTestBase): + + @classmethod + def setUpClass(cls): + super().setUpClass() + settings.configure(ROOT_URLCONF=modules[__name__]) + + def setUp(self): + super().setUp() + setup_test_environment() + _django_instrumentor.instrument() + def tearDown(self): + super().tearDown() + teardown_test_environment() + _django_instrumentor.uninstrument() -class TestDjangoOpenTracingMiddleware(WsgiTestBase, SimpleTestCase): def test_middleware_traced(self): Client().get("/traced/") self.assertEqual(len(self.memory_exporter.get_finished_spans()), 1) From a468bbc4b52a1c0865176e30f8b8ced387c0c6db Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 22 Apr 2020 13:47:23 -0600 Subject: [PATCH 25/58] Add checks in process_* methods Co-authored-by: Mathieu Hinderyckx --- .../src/opentelemetry/ext/django/__init__.py | 6 ---- .../opentelemetry/ext/django/middleware.py | 34 ++++++++++++++----- .../src/opentelemetry/context/__init__.py | 1 + 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py index 628b3f15749..b9cbc629ff7 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py @@ -13,7 +13,6 @@ # limitations under the License. from logging import getLogger -from os import environ from django import VERSION from django.conf import settings @@ -68,11 +67,6 @@ def _instrument(self): ): return - if "DJANGO_SETTINGS_MODULE" not in environ: - raise Exception( - "Missing environment variable DJANGO_SETTINGS_MODULE" - ) - settings_middleware = getattr(settings, self._middleware_setting, []) settings_middleware.append(self._opentelemetry_middleware) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py index 4f13b17306c..b59538383ae 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py @@ -93,19 +93,35 @@ def process_view( request.META[self._environ_token] = token def process_exception(self, request, exception): - request.META.get(self._environ_activation_key).__exit__( + # Django can call this method and process_response later. In order + # to avoid __exit__ and detach from being called twice then, the + # respective keys are being removed here. + request.META[self._environ_activation_key].__exit__( type(exception), exception, getattr(exception, "__traceback__", None), ) - detach(request.environ.get(self._environ_token)) + request.META.pop(self._environ_activation_key) + + detach(request.environ[self._environ_token]) + request.META.pop(self._environ_token) def process_response(self, request, response): - add_response_attributes( - request.META[self._environ_span_key], - "{} {}".format(response.status_code, response.reason_phrase), - response - ) - request.META[self._environ_activation_key].__exit__(None, None, None) - detach(request.environ.get(self._environ_token)) + if self._environ_activation_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) + + 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 diff --git a/opentelemetry-api/src/opentelemetry/context/__init__.py b/opentelemetry-api/src/opentelemetry/context/__init__.py index adf1bc0869f..f55636e3232 100644 --- a/opentelemetry-api/src/opentelemetry/context/__init__.py +++ b/opentelemetry-api/src/opentelemetry/context/__init__.py @@ -148,3 +148,4 @@ def detach(token: object) -> None: _RUNTIME_CONTEXT.detach(token) # type: ignore except Exception: # pylint: disable=broad-except logger.error("Failed to detach context") + raise From 100e89ba0d570f8d433346886d06911e3e2b7fd8 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 22 Apr 2020 19:11:29 -0600 Subject: [PATCH 26/58] More fixes Co-authored-by: Mathieu Hinderyckx --- .../tests/test_middleware.py | 60 +++++++++++++++++-- ext/opentelemetry-ext-django/tests/views.py | 2 +- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/ext/opentelemetry-ext-django/tests/test_middleware.py b/ext/opentelemetry-ext-django/tests/test_middleware.py index 4c96ec89263..cd1df79a75a 100644 --- a/ext/opentelemetry-ext-django/tests/test_middleware.py +++ b/ext/opentelemetry-ext-django/tests/test_middleware.py @@ -21,6 +21,8 @@ setup_test_environment, teardown_test_environment ) +from opentelemetry.trace import SpanKind +from opentelemetry.trace.status import StatusCanonicalCode from opentelemetry.test.wsgitestutil import WsgiTestBase from opentelemetry.ext.django import DjangoInstrumentor @@ -51,11 +53,61 @@ def tearDown(self): teardown_test_environment() _django_instrumentor.uninstrument() - def test_middleware_traced(self): + def test_traced_get(self): Client().get("/traced/") - self.assertEqual(len(self.memory_exporter.get_finished_spans()), 1) - def test_middleware_error(self): + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + + span = spans[0] + + self.assertEqual(span.name, "traced") + self.assertEqual(span.kind, SpanKind.SERVER) + self.assertEqual(span.status.canonical_code, StatusCanonicalCode.OK) + self.assertEqual(span.attributes["http.method"], "GET") + self.assertEqual( + span.attributes["http.url"], "http://testserver/traced/" + ) + self.assertEqual(span.attributes["http.scheme"], "http") + self.assertEqual(span.attributes["http.status_code"], 200) + self.assertEqual(span.attributes["http.status_text"], "OK") + + def test_traced_post(self): + Client().post("/traced/") + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + + span = spans[0] + + self.assertEqual(span.name, "traced") + self.assertEqual(span.kind, SpanKind.SERVER) + self.assertEqual(span.status.canonical_code, StatusCanonicalCode.OK) + self.assertEqual(span.attributes["http.method"], "POST") + self.assertEqual( + span.attributes["http.url"], "http://testserver/traced/" + ) + self.assertEqual(span.attributes["http.scheme"], "http") + self.assertEqual(span.attributes["http.status_code"], 200) + self.assertEqual(span.attributes["http.status_text"], "OK") + + def test_error(self): with self.assertRaises(ValueError): Client().get("/error/") - self.assertEqual(len(self.memory_exporter.get_finished_spans()), 1) + + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + + span = spans[0] + + self.assertEqual(span.name, "error") + self.assertEqual(span.kind, SpanKind.SERVER) + self.assertEqual(span.status.canonical_code, StatusCanonicalCode.OK) + self.assertEqual(span.attributes["http.method"], "POST") + self.assertEqual( + span.attributes["http.url"], "http://testserver/traced/" + ) + self.assertEqual(span.attributes["http.scheme"], "http") + self.assertEqual(span.attributes["http.status_code"], 500) + # FIXME should the status_text be "OK" + self.assertEqual(span.attributes["http.status_text"], "OK") diff --git a/ext/opentelemetry-ext-django/tests/views.py b/ext/opentelemetry-ext-django/tests/views.py index 9a885f253b4..71beb97ce86 100644 --- a/ext/opentelemetry-ext-django/tests/views.py +++ b/ext/opentelemetry-ext-django/tests/views.py @@ -1,7 +1,7 @@ from django.http import HttpResponse -def traced(request): # pylint: disable=unused-argument +def traced(request): # pylint: disable=unused-argumentt return HttpResponse() From fea559711f81fea2e8079c2a8a54094e20bfa39a Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 22 Apr 2020 19:44:41 -0600 Subject: [PATCH 27/58] Lint fixes Co-authored-by: Mathieu Hinderyckx --- .../src/opentelemetry/ext/django/__init__.py | 4 +- .../opentelemetry/ext/django/middleware.py | 6 +- .../tests/conftest.py | 15 --- .../tests/settings.py | 123 ------------------ .../tests/test_middleware.py | 22 ++-- ext/opentelemetry-ext-django/tests/urls.py | 8 -- ext/opentelemetry-ext-django/tests/views.py | 4 +- 7 files changed, 16 insertions(+), 166 deletions(-) delete mode 100644 ext/opentelemetry-ext-django/tests/settings.py delete mode 100644 ext/opentelemetry-ext-django/tests/urls.py diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py index b9cbc629ff7..cdb0cb88e14 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py @@ -54,7 +54,7 @@ def __init__(self): "MIDDLEWARE" if VERSION >= (1, 10, 0) else "MIDDLEWARE_CLASSES" ) - def _instrument(self): + def _instrument(self, **kwargs): # FIXME this is probably a pattern that will show up in the rest of the # ext. Find a better way of implementing this. @@ -72,7 +72,7 @@ def _instrument(self): setattr(settings, self._middleware_setting, settings_middleware) - def _uninstrument(self): + def _uninstrument(self, **kwargs): settings_middleware = getattr(settings, self._middleware_setting, None) # FIXME This is starting to smell like trouble. We have 2 mechanisms diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py index b59538383ae..50f84f2ed8a 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py @@ -19,9 +19,9 @@ from opentelemetry.context import attach, detach from opentelemetry.ext.django.version import __version__ from opentelemetry.ext.wsgi import ( + add_response_attributes, collect_request_attributes, get_header_from_environ, - add_response_attributes, ) from opentelemetry.propagators import extract from opentelemetry.trace import SpanKind, get_tracer @@ -82,7 +82,7 @@ def process_view( attributes=attributes, start_time=environ.get( "opentelemetry-instrumentor-django.starttime_key" - ) + ), ) activation = tracer.use_span(span, end_on_exit=True) @@ -111,7 +111,7 @@ def process_response(self, request, response): add_response_attributes( request.META[self._environ_span_key], "{} {}".format(response.status_code, response.reason_phrase), - response + response, ) request.META.pop(self._environ_span_key) diff --git a/ext/opentelemetry-ext-django/tests/conftest.py b/ext/opentelemetry-ext-django/tests/conftest.py index 56bfb1aadef..b2b39bc049a 100644 --- a/ext/opentelemetry-ext-django/tests/conftest.py +++ b/ext/opentelemetry-ext-django/tests/conftest.py @@ -14,21 +14,6 @@ from os import environ -# from django import setup - -from opentelemetry.ext.django import DjangoInstrumentor - -_django_instrumentor = DjangoInstrumentor() - def pytest_sessionstart(session): # pylint: disable=unused-argument - # environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") environ.setdefault("OPENTELEMETRY_PYTHON_DJANGO_INSTRUMENT", "True") - # setup() - # _django_instrumentor.instrument() - pass - - -def pytest_sessionfinish(session): # pylint: disable=unused-argument - # _django_instrumentor.uninstrument() - pass diff --git a/ext/opentelemetry-ext-django/tests/settings.py b/ext/opentelemetry-ext-django/tests/settings.py deleted file mode 100644 index 638d2cfc210..00000000000 --- a/ext/opentelemetry-ext-django/tests/settings.py +++ /dev/null @@ -1,123 +0,0 @@ -""" -Django settings for tests project. - -Generated by "django-admin startproject" using Django 3.0.4. - -For more information on this file, see -https://docs.djangoproject.com/en/3.0/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/3.0/ref/settings/ -""" - -import os - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "m^c^r=-gx(j_tx)hyisz6%f4**thdz#v-u$)ghhpd^fs-!47t=" - -# SECURITY WARNING: don"t run with debug turned on in production! -DEBUG = True -# FIXME this is needed in order for test_middleware_error to pass. Apparently -# without it Django will try to render some HTML causing an error (apparently)# unrelated to process_exception. -DEBUG_PROPAGATE_EXCEPTIONS = True - -ALLOWED_HOSTS = ["testserver"] - - -# Application definition - -INSTALLED_APPS = [ - "django.contrib.admin", - "django.contrib.auth", - "django.contrib.contenttypes", - "django.contrib.sessions", - "django.contrib.messages", - "django.contrib.staticfiles", -] - -MIDDLEWARE = [ - "django.middleware.security.SecurityMiddleware", - "django.contrib.sessions.middleware.SessionMiddleware", - "django.middleware.common.CommonMiddleware", - "django.middleware.csrf.CsrfViewMiddleware", - "django.contrib.auth.middleware.AuthenticationMiddleware", - "django.contrib.messages.middleware.MessageMiddleware", - "django.middleware.clickjacking.XFrameOptionsMiddleware", -] - -ROOT_URLCONF = "tests.urls" - -TEMPLATES = [ - { - "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [], - "APP_DIRS": True, - "OPTIONS": { - "context_processors": [ - "django.template.context_processors.debug", - "django.template.context_processors.request", - "django.contrib.auth.context_processors.auth", - "django.contrib.messages.context_processors.messages", - ], - }, - }, -] - -WSGI_APPLICATION = "tests.wsgi.application" - - -# Database -# https://docs.djangoproject.com/en/3.0/ref/settings/#databases - -DATABASES = { - "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": os.path.join(BASE_DIR, "db.sqlite3"), - } -} - - -# Password validation -# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", - }, -] - - -# Internationalization -# https://docs.djangoproject.com/en/3.0/topics/i18n/ - -LANGUAGE_CODE = "en-us" - -TIME_ZONE = "UTC" - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/3.0/howto/static-files/ - -STATIC_URL = "/static/" diff --git a/ext/opentelemetry-ext-django/tests/test_middleware.py b/ext/opentelemetry-ext-django/tests/test_middleware.py index cd1df79a75a..ae678bd267a 100644 --- a/ext/opentelemetry-ext-django/tests/test_middleware.py +++ b/ext/opentelemetry-ext-django/tests/test_middleware.py @@ -14,17 +14,15 @@ from sys import modules -from django.test import Client from django.conf import settings from django.conf.urls import url -from django.test.utils import ( - setup_test_environment, teardown_test_environment -) +from django.test import Client +from django.test.utils import setup_test_environment, teardown_test_environment +from opentelemetry.ext.django import DjangoInstrumentor +from opentelemetry.test.wsgitestutil import WsgiTestBase from opentelemetry.trace import SpanKind from opentelemetry.trace.status import StatusCanonicalCode -from opentelemetry.test.wsgitestutil import WsgiTestBase -from opentelemetry.ext.django import DjangoInstrumentor from .views import error, traced # pylint: disable=import-error @@ -37,7 +35,6 @@ # class TestDjangoOpenTracingMiddleware(WsgiTestBase, SimpleTestCase): class TestDjangoOpenTracingMiddleware(WsgiTestBase): - @classmethod def setUpClass(cls): super().setUpClass() @@ -102,12 +99,11 @@ def test_error(self): self.assertEqual(span.name, "error") self.assertEqual(span.kind, SpanKind.SERVER) - self.assertEqual(span.status.canonical_code, StatusCanonicalCode.OK) - self.assertEqual(span.attributes["http.method"], "POST") self.assertEqual( - span.attributes["http.url"], "http://testserver/traced/" + span.status.canonical_code, StatusCanonicalCode.UNKNOWN + ) + self.assertEqual(span.attributes["http.method"], "GET") + self.assertEqual( + span.attributes["http.url"], "http://testserver/error/" ) self.assertEqual(span.attributes["http.scheme"], "http") - self.assertEqual(span.attributes["http.status_code"], 500) - # FIXME should the status_text be "OK" - self.assertEqual(span.attributes["http.status_text"], "OK") diff --git a/ext/opentelemetry-ext-django/tests/urls.py b/ext/opentelemetry-ext-django/tests/urls.py deleted file mode 100644 index 59f65245111..00000000000 --- a/ext/opentelemetry-ext-django/tests/urls.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.conf.urls import url - -from .views import error, traced # pylint: disable=import-error - -urlpatterns = [ - url(r"^traced/", traced), - url(r"^error/", error), -] diff --git a/ext/opentelemetry-ext-django/tests/views.py b/ext/opentelemetry-ext-django/tests/views.py index 71beb97ce86..498a4518eda 100644 --- a/ext/opentelemetry-ext-django/tests/views.py +++ b/ext/opentelemetry-ext-django/tests/views.py @@ -1,9 +1,9 @@ from django.http import HttpResponse -def traced(request): # pylint: disable=unused-argumentt +def traced(request): # pylint: disable=unused-argument return HttpResponse() -def error(request): +def error(request): # pylint: disable=unused-argument raise ValueError("error") From b2b7f42a41d1c09f2abf72b933624a2187077e53 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 23 Apr 2020 15:38:45 -0600 Subject: [PATCH 28/58] Fix URL Co-authored-by: Mathieu Hinderyckx --- ext/opentelemetry-ext-django/setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-django/setup.cfg b/ext/opentelemetry-ext-django/setup.cfg index c02527d1691..f2d483df4cf 100644 --- a/ext/opentelemetry-ext-django/setup.cfg +++ b/ext/opentelemetry-ext-django/setup.cfg @@ -19,7 +19,7 @@ long_description = file: README.rst long_description_content_type = text/x-rst author = OpenTelemetry Authors author_email = cncf-opentelemetry-contributors@lists.cncf.io -url = https://github.com/open-telemetry/opentelemetry-python/ext/opentelemetry-ext-django +url = https://github.com/open-telemetry/opentelemetry-python/tree/master/ext/opentelemetry-ext-django platforms = any license = Apache-2.0 classifiers = From bf82666145eee3ba43e02cef354634b0519966ef Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 23 Apr 2020 15:44:29 -0600 Subject: [PATCH 29/58] Remove version check Co-authored-by: Mathieu Hinderyckx --- .../src/opentelemetry/ext/django/middleware.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py index 50f84f2ed8a..90b4d673f69 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py @@ -14,7 +14,7 @@ from logging import getLogger -from django import VERSION +from django.utils.deprecation import MiddlewareMixin from opentelemetry.context import attach, detach from opentelemetry.ext.django.version import __version__ @@ -26,22 +26,6 @@ from opentelemetry.propagators import extract from opentelemetry.trace import SpanKind, get_tracer -if VERSION >= (1, 10, 0): - # Read more about django.utils.deprecation.MiddlewareMixin here: - # https://docs.djangoproject.com/en/3.0/topics/http/middleware/#django.utils.deprecation.MiddlewareMixin - - # This mixin provides these methods: - # def __init__(self, get_response=None): - # self._get_response = get_reponse - # def __call__(self, request): - # self.process_request(request) - # self.get_response(request) - # self.process_response(request, response) - # return response - from django.utils.deprecation import MiddlewareMixin -else: - MiddlewareMixin = object - _logger = getLogger(__name__) From 97f2b81e77288aab9244afe7d340126eb6d0a2b4 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 23 Apr 2020 15:46:18 -0600 Subject: [PATCH 30/58] Add support for Django versions greater than 2.2 Co-authored-by: Mathieu Hinderyckx --- ext/opentelemetry-ext-django/setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-django/setup.cfg b/ext/opentelemetry-ext-django/setup.cfg index f2d483df4cf..52fe186a8a5 100644 --- a/ext/opentelemetry-ext-django/setup.cfg +++ b/ext/opentelemetry-ext-django/setup.cfg @@ -45,7 +45,7 @@ install_requires = [options.extras_require] test = - django~=3.0.4 + django > 2.2 opentelemetry-test == 0.7.dev0 [options.packages.find] From 5ba80ce8d817d8f92ec8920633addd74cfe81293 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 23 Apr 2020 15:49:48 -0600 Subject: [PATCH 31/58] Adding comment regarding middleware ordering Co-authored-by: Mathieu Hinderyckx --- .../src/opentelemetry/ext/django/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py index cdb0cb88e14..85c7d96208f 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py @@ -67,6 +67,12 @@ def _instrument(self, **kwargs): ): return + # This can not be solved, but is an inherent problem of this approach: + # the order of middleware entries matters, and here you have no control + # on that: + # https://docs.djangoproject.com/en/3.0/topics/http/middleware/#activating-middleware + # https://docs.djangoproject.com/en/3.0/ref/middleware/#middleware-ordering + settings_middleware = getattr(settings, self._middleware_setting, []) settings_middleware.append(self._opentelemetry_middleware) From 18c1ab47981387f85ad1ca205f70abe726659a5d Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 23 Apr 2020 16:40:17 -0600 Subject: [PATCH 32/58] Fix dependencies Co-authored-by: Mathieu Hinderyckx --- ext/opentelemetry-ext-django/setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-django/setup.cfg b/ext/opentelemetry-ext-django/setup.cfg index 52fe186a8a5..17e6a7bfc87 100644 --- a/ext/opentelemetry-ext-django/setup.cfg +++ b/ext/opentelemetry-ext-django/setup.cfg @@ -38,7 +38,7 @@ package_dir= =src packages=find_namespace: install_requires = - django~=3.0.4 + django > 2.2 opentelemetry-ext-wsgi == 0.7.dev0 opentelemetry-auto-instrumentation == 0.7.dev0 opentelemetry-api == 0.7.dev0 From 9ceba035be957901066c7fb5b09bd5845d917456 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Sat, 25 Apr 2020 15:36:52 -0600 Subject: [PATCH 33/58] Remove raise --- opentelemetry-api/src/opentelemetry/context/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/context/__init__.py b/opentelemetry-api/src/opentelemetry/context/__init__.py index f55636e3232..adf1bc0869f 100644 --- a/opentelemetry-api/src/opentelemetry/context/__init__.py +++ b/opentelemetry-api/src/opentelemetry/context/__init__.py @@ -148,4 +148,3 @@ def detach(token: object) -> None: _RUNTIME_CONTEXT.detach(token) # type: ignore except Exception: # pylint: disable=broad-except logger.error("Failed to detach context") - raise From a1a5cbb4a0f61ffe16a06dd101cc62aa6b9585f0 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Sat, 25 Apr 2020 19:53:29 -0600 Subject: [PATCH 34/58] Add documentation and gitignore --- .../example/.gitignore | 1 + .../example/README.rst | 146 ++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 ext/opentelemetry-ext-django/example/.gitignore create mode 100644 ext/opentelemetry-ext-django/example/README.rst diff --git a/ext/opentelemetry-ext-django/example/.gitignore b/ext/opentelemetry-ext-django/example/.gitignore new file mode 100644 index 00000000000..49ef2557b16 --- /dev/null +++ b/ext/opentelemetry-ext-django/example/.gitignore @@ -0,0 +1 @@ +db.sqlite3 diff --git a/ext/opentelemetry-ext-django/example/README.rst b/ext/opentelemetry-ext-django/example/README.rst new file mode 100644 index 00000000000..45f34201eae --- /dev/null +++ b/ext/opentelemetry-ext-django/example/README.rst @@ -0,0 +1,146 @@ +OpenTelemetry Django Instrumentation Example +============================================ + +This shows how to use `opentelemetry-ext-django` to automatically instrument a +Django app. + +For more user convenience, a Django app is already provided in this directory. + +Preparation +----------- + +This example will be executed in a separate virtual environment: + +.. code-block:: + + $ mkdir django_auto_instrumentation + $ virtualenv django_auto_instrumentation + $ source django_auto_instrumentation/bin/activate + + +Installation +------------ + +.. code-block:: + + $ pip install opentelemetry-api + $ pip install opentelemetry-sdk + $ pip install opentelemetry-auto-instrumentation + $ pip install ext/opentelemetry-ext-django + $ pip install requests + + +Execution +--------- + +Execution of the Django app +........................... + +Set these environment variables first: + +#. `export OPENTELEMETRY_PYTHON_DJANGO_INSTRUMENT=True` +#. `export DJANGO_SETTINGS_MODULE=instrumentation_example.settings` + +Clone the `opentelemetry-python` repository and go to `opentelemetry-python/ext/opentelemetry-ext-django/example`. + +Once there run this command: + +`opentelemetry-python-autoinstrumentation python3 manage.py runserver` + +Execution of the client +....................... + +Open up a new console and activate the previous virtual environment there too: + +`source django_auto_instrumentation/bin/activate` + +Go to `opentelemetry-python/ext/opentelemetry-ext-django/example`, once there +run the client with: + +`python client.py hello` + +This should produce output similar to this one: + +.. code-block:: + + { + "name": "client-server", + "context": { + "trace_id": "0xa97f61b977a8cd4a21e3c43b8fae3708", + "span_id": "0x6290f0a6c49a5e9c", + "trace_state": "{}" + }, + "kind": "SpanKind.INTERNAL", + "parent_id": "0x0af82d771221ac0a", + "start_time": "2020-04-26T01:48:51.793095Z", + "end_time": "2020-04-26T01:48:51.798319Z", + "status": { + "canonical_code": "OK" + }, + "attributes": {}, + "events": [], + "links": [] + } + { + "name": "client", + "context": { + "trace_id": "0xa97f61b977a8cd4a21e3c43b8fae3708", + "span_id": "0x0af82d771221ac0a", + "trace_state": "{}" + }, + "kind": "SpanKind.INTERNAL", + "parent_id": null, + "start_time": "2020-04-26T01:48:51.792938Z", + "end_time": "2020-04-26T01:48:51.798535Z", + "status": { + "canonical_code": "OK" + }, + "attributes": {}, + "events": [], + "links": [] + } + +Go to the previous console, where the Django app is running. You should see +output similar to this one: + +.. code-block:: + + { + "name": "home_page_view", + "context": { + "trace_id": "0xed88755c56d95d05a506f5f70e7849b9", + "span_id": "0x0a94c7a60e0650d5", + "trace_state": "{}" + }, + "kind": "SpanKind.SERVER", + "parent_id": "0x3096ef92e621c22d", + "start_time": "2020-04-26T01:49:57.205833Z", + "end_time": "2020-04-26T01:49:57.206214Z", + "status": { + "canonical_code": "OK" + }, + "attributes": { + "component": "http", + "http.method": "GET", + "http.server_name": "localhost", + "http.scheme": "http", + "host.port": 8000, + "http.host": "localhost:8000", + "http.url": "http://localhost:8000/?param=hello", + "net.peer.ip": "127.0.0.1", + "http.flavor": "1.1", + "http.status_text": "OK", + "http.status_code": 200 + }, + "events": [], + "links": [] + } + +This last output are spans automatically generated by the OpenTelemetry Django +Instrumentation package. + +References +---------- + +* `OpenTelemetry Project `_ +* `OpenTelemetry Django extension `_ From b689b4e753a16d78dd4fb6dcb161466e657b6043 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Sun, 26 Apr 2020 18:24:32 -0600 Subject: [PATCH 35/58] Fix lint --- .../src/opentelemetry/ext/django/middleware.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py index 90b4d673f69..4188c6be9a7 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py @@ -26,7 +26,6 @@ from opentelemetry.propagators import extract from opentelemetry.trace import SpanKind, get_tracer - _logger = getLogger(__name__) From 769fa83f5957f265ef0d525d725d9cb246e8dcea Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 29 Apr 2020 12:19:30 -0600 Subject: [PATCH 36/58] Update code to latest configuration changes --- .../src/opentelemetry/ext/django/__init__.py | 6 ++---- ext/opentelemetry-ext-django/tests/test_middleware.py | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py index 85c7d96208f..ee912943c07 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py @@ -61,10 +61,8 @@ def _instrument(self, **kwargs): # FIXME Probably the evaluation of strings into boolean values can be # built inside the Configuration class itself with the magic method # __bool__ - if ( - hasattr(Configuration(), "django_instrument") - and Configuration().django_instrument != "True" - ): + + if Configuration().DJANGO_INSTRUMENT != "True": return # This can not be solved, but is an inherent problem of this approach: diff --git a/ext/opentelemetry-ext-django/tests/test_middleware.py b/ext/opentelemetry-ext-django/tests/test_middleware.py index ae678bd267a..afee6acf77a 100644 --- a/ext/opentelemetry-ext-django/tests/test_middleware.py +++ b/ext/opentelemetry-ext-django/tests/test_middleware.py @@ -33,8 +33,7 @@ _django_instrumentor = DjangoInstrumentor() -# class TestDjangoOpenTracingMiddleware(WsgiTestBase, SimpleTestCase): -class TestDjangoOpenTracingMiddleware(WsgiTestBase): +class TestMiddleware(WsgiTestBase): @classmethod def setUpClass(cls): super().setUpClass() From 1b0b21f1b9a50cde915eaca1156761f9148caed3 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 29 Apr 2020 12:47:53 -0600 Subject: [PATCH 37/58] Add docs entry --- docs/ext/django/django.rst | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 docs/ext/django/django.rst diff --git a/docs/ext/django/django.rst b/docs/ext/django/django.rst new file mode 100644 index 00000000000..1a2c844e28c --- /dev/null +++ b/docs/ext/django/django.rst @@ -0,0 +1,7 @@ +OpenTelemetry Django Instrumentation +==================================== + +.. automodule:: opentelemetry.ext.django + :members: + :undoc-members: + :show-inheritance: From b2252b1330d1ebaebd853990efdd21d02587f37c Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 29 Apr 2020 13:28:00 -0600 Subject: [PATCH 38/58] Update Django versions --- ext/opentelemetry-ext-django/setup.cfg | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/opentelemetry-ext-django/setup.cfg b/ext/opentelemetry-ext-django/setup.cfg index 17e6a7bfc87..2d26833f551 100644 --- a/ext/opentelemetry-ext-django/setup.cfg +++ b/ext/opentelemetry-ext-django/setup.cfg @@ -38,14 +38,13 @@ package_dir= =src packages=find_namespace: install_requires = - django > 2.2 + django >= 2.2 opentelemetry-ext-wsgi == 0.7.dev0 opentelemetry-auto-instrumentation == 0.7.dev0 opentelemetry-api == 0.7.dev0 [options.extras_require] test = - django > 2.2 opentelemetry-test == 0.7.dev0 [options.packages.find] From 7617dd6b7fa1830fda04f22c9c0d2770c3f6c367 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 29 Apr 2020 13:30:10 -0600 Subject: [PATCH 39/58] Remove version checking Co-authored-by: Mathieu Hinderyckx --- .../src/opentelemetry/ext/django/__init__.py | 33 ++++--------------- .../opentelemetry/ext/django/middleware.py | 2 +- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py index ee912943c07..aad6193593d 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py @@ -14,12 +14,11 @@ from logging import getLogger -from django import VERSION from django.conf import settings from opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor from opentelemetry.configuration import Configuration -from opentelemetry.ext.django.middleware import OpenTelemetryMiddleware +from opentelemetry.ext.django.middleware import DjangoMiddleware _logger = getLogger(__name__) @@ -31,29 +30,9 @@ class DjangoInstrumentor(BaseInstrumentor): """ _opentelemetry_middleware = ".".join( - [ - OpenTelemetryMiddleware.__module__, - OpenTelemetryMiddleware.__qualname__, - ] + [DjangoMiddleware.__module__, DjangoMiddleware.__qualname__] ) - def __init__(self): - super().__init__() - # Django Middleware is code that is executed before and/or after a - # request. Read about Django middleware here: - # https://docs.djangoproject.com/en/3.0/topics/http/middleware/ - - # This method must set this Django settings attribute: - # MIDDLEWARE: for Django version > 1.0 - # MIDDLEWARE_CLASSES: for Django version <= 1.0 - - # Django settings.MIDDLEWARE is a list of strings, each one a Python - # path to a class or a function that acts as middleware. - - self._middleware_setting = ( - "MIDDLEWARE" if VERSION >= (1, 10, 0) else "MIDDLEWARE_CLASSES" - ) - def _instrument(self, **kwargs): # FIXME this is probably a pattern that will show up in the rest of the @@ -71,13 +50,13 @@ def _instrument(self, **kwargs): # https://docs.djangoproject.com/en/3.0/topics/http/middleware/#activating-middleware # https://docs.djangoproject.com/en/3.0/ref/middleware/#middleware-ordering - settings_middleware = getattr(settings, self._middleware_setting, []) + settings_middleware = getattr(settings, "MIDDLEWARE", []) settings_middleware.append(self._opentelemetry_middleware) - setattr(settings, self._middleware_setting, settings_middleware) + setattr(settings, "MIDDLEWARE", settings_middleware) def _uninstrument(self, **kwargs): - settings_middleware = getattr(settings, self._middleware_setting, None) + settings_middleware = getattr(settings, "MIDDLEWARE", None) # FIXME This is starting to smell like trouble. We have 2 mechanisms # that may make this condition be True, one implemented in @@ -90,4 +69,4 @@ def _uninstrument(self, **kwargs): return settings_middleware.remove(self._opentelemetry_middleware) - setattr(settings, self._middleware_setting, settings_middleware) + setattr(settings, "MIDDLEWARE", settings_middleware) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py index 4188c6be9a7..0c85c968d31 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py @@ -29,7 +29,7 @@ _logger = getLogger(__name__) -class OpenTelemetryMiddleware(MiddlewareMixin): +class DjangoMiddleware(MiddlewareMixin): """Django Middleware for OpenTelemetry """ From 7e5b1b023e5cf3cf88593e990e9a33645c98543a Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 29 Apr 2020 18:42:13 -0600 Subject: [PATCH 40/58] Move example to docs --- .gitignore | 4 ++++ .../example => docs/examples/django}/README.rst | 0 .../example => docs/examples/django}/client.py | 0 .../examples/django}/instrumentation_example/__init__.py | 0 .../examples/django}/instrumentation_example/asgi.py | 0 .../examples/django}/instrumentation_example/settings.py | 0 .../examples/django}/instrumentation_example/urls.py | 0 .../examples/django}/instrumentation_example/wsgi.py | 0 .../example => docs/examples/django}/manage.py | 0 .../example => docs/examples/django}/pages/__init__.py | 0 .../example => docs/examples/django}/pages/admin.py | 0 .../example => docs/examples/django}/pages/apps.py | 0 .../examples/django}/pages/migrations/__init__.py | 0 .../example => docs/examples/django}/pages/models.py | 0 .../example => docs/examples/django}/pages/tests.py | 0 .../example => docs/examples/django}/pages/urls.py | 0 .../example => docs/examples/django}/pages/views.py | 0 ext/opentelemetry-ext-django/example/.gitignore | 1 - 18 files changed, 4 insertions(+), 1 deletion(-) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/README.rst (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/client.py (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/instrumentation_example/__init__.py (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/instrumentation_example/asgi.py (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/instrumentation_example/settings.py (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/instrumentation_example/urls.py (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/instrumentation_example/wsgi.py (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/manage.py (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/pages/__init__.py (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/pages/admin.py (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/pages/apps.py (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/pages/migrations/__init__.py (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/pages/models.py (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/pages/tests.py (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/pages/urls.py (100%) rename {ext/opentelemetry-ext-django/example => docs/examples/django}/pages/views.py (100%) delete mode 100644 ext/opentelemetry-ext-django/example/.gitignore diff --git a/.gitignore b/.gitignore index 42e6d0bf043..75cdf092930 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,7 @@ _build/ # mypy .mypy_cache/ target + +# Django example + +docs/examples/django/db.sqlite3 diff --git a/ext/opentelemetry-ext-django/example/README.rst b/docs/examples/django/README.rst similarity index 100% rename from ext/opentelemetry-ext-django/example/README.rst rename to docs/examples/django/README.rst diff --git a/ext/opentelemetry-ext-django/example/client.py b/docs/examples/django/client.py similarity index 100% rename from ext/opentelemetry-ext-django/example/client.py rename to docs/examples/django/client.py diff --git a/ext/opentelemetry-ext-django/example/instrumentation_example/__init__.py b/docs/examples/django/instrumentation_example/__init__.py similarity index 100% rename from ext/opentelemetry-ext-django/example/instrumentation_example/__init__.py rename to docs/examples/django/instrumentation_example/__init__.py diff --git a/ext/opentelemetry-ext-django/example/instrumentation_example/asgi.py b/docs/examples/django/instrumentation_example/asgi.py similarity index 100% rename from ext/opentelemetry-ext-django/example/instrumentation_example/asgi.py rename to docs/examples/django/instrumentation_example/asgi.py diff --git a/ext/opentelemetry-ext-django/example/instrumentation_example/settings.py b/docs/examples/django/instrumentation_example/settings.py similarity index 100% rename from ext/opentelemetry-ext-django/example/instrumentation_example/settings.py rename to docs/examples/django/instrumentation_example/settings.py diff --git a/ext/opentelemetry-ext-django/example/instrumentation_example/urls.py b/docs/examples/django/instrumentation_example/urls.py similarity index 100% rename from ext/opentelemetry-ext-django/example/instrumentation_example/urls.py rename to docs/examples/django/instrumentation_example/urls.py diff --git a/ext/opentelemetry-ext-django/example/instrumentation_example/wsgi.py b/docs/examples/django/instrumentation_example/wsgi.py similarity index 100% rename from ext/opentelemetry-ext-django/example/instrumentation_example/wsgi.py rename to docs/examples/django/instrumentation_example/wsgi.py diff --git a/ext/opentelemetry-ext-django/example/manage.py b/docs/examples/django/manage.py similarity index 100% rename from ext/opentelemetry-ext-django/example/manage.py rename to docs/examples/django/manage.py diff --git a/ext/opentelemetry-ext-django/example/pages/__init__.py b/docs/examples/django/pages/__init__.py similarity index 100% rename from ext/opentelemetry-ext-django/example/pages/__init__.py rename to docs/examples/django/pages/__init__.py diff --git a/ext/opentelemetry-ext-django/example/pages/admin.py b/docs/examples/django/pages/admin.py similarity index 100% rename from ext/opentelemetry-ext-django/example/pages/admin.py rename to docs/examples/django/pages/admin.py diff --git a/ext/opentelemetry-ext-django/example/pages/apps.py b/docs/examples/django/pages/apps.py similarity index 100% rename from ext/opentelemetry-ext-django/example/pages/apps.py rename to docs/examples/django/pages/apps.py diff --git a/ext/opentelemetry-ext-django/example/pages/migrations/__init__.py b/docs/examples/django/pages/migrations/__init__.py similarity index 100% rename from ext/opentelemetry-ext-django/example/pages/migrations/__init__.py rename to docs/examples/django/pages/migrations/__init__.py diff --git a/ext/opentelemetry-ext-django/example/pages/models.py b/docs/examples/django/pages/models.py similarity index 100% rename from ext/opentelemetry-ext-django/example/pages/models.py rename to docs/examples/django/pages/models.py diff --git a/ext/opentelemetry-ext-django/example/pages/tests.py b/docs/examples/django/pages/tests.py similarity index 100% rename from ext/opentelemetry-ext-django/example/pages/tests.py rename to docs/examples/django/pages/tests.py diff --git a/ext/opentelemetry-ext-django/example/pages/urls.py b/docs/examples/django/pages/urls.py similarity index 100% rename from ext/opentelemetry-ext-django/example/pages/urls.py rename to docs/examples/django/pages/urls.py diff --git a/ext/opentelemetry-ext-django/example/pages/views.py b/docs/examples/django/pages/views.py similarity index 100% rename from ext/opentelemetry-ext-django/example/pages/views.py rename to docs/examples/django/pages/views.py diff --git a/ext/opentelemetry-ext-django/example/.gitignore b/ext/opentelemetry-ext-django/example/.gitignore deleted file mode 100644 index 49ef2557b16..00000000000 --- a/ext/opentelemetry-ext-django/example/.gitignore +++ /dev/null @@ -1 +0,0 @@ -db.sqlite3 From 73c493113195047e267f7d97db001e94f83ace32 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 29 Apr 2020 18:55:57 -0600 Subject: [PATCH 41/58] Added initial release --- ext/opentelemetry-ext-django/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/opentelemetry-ext-django/CHANGELOG.md b/ext/opentelemetry-ext-django/CHANGELOG.md index 1512c421622..3e04402cea9 100644 --- a/ext/opentelemetry-ext-django/CHANGELOG.md +++ b/ext/opentelemetry-ext-django/CHANGELOG.md @@ -1,3 +1,5 @@ # Changelog ## Unreleased + +- Initial release From 62b69584c2390c7a6e384cfd57350c5d0bbb02df Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 29 Apr 2020 18:56:48 -0600 Subject: [PATCH 42/58] Update package name in README --- docs/examples/django/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/django/README.rst b/docs/examples/django/README.rst index 45f34201eae..579a7ec3db0 100644 --- a/docs/examples/django/README.rst +++ b/docs/examples/django/README.rst @@ -26,7 +26,7 @@ Installation $ pip install opentelemetry-api $ pip install opentelemetry-sdk $ pip install opentelemetry-auto-instrumentation - $ pip install ext/opentelemetry-ext-django + $ pip install opentelemetry-ext-django $ pip install requests From 62a0063e8d711103ac34ec0e38a6b61655d26f8d Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 29 Apr 2020 19:02:30 -0600 Subject: [PATCH 43/58] Add missing copyright --- .../django/instrumentation_example/asgi.py | 13 +++++++++++++ .../django/instrumentation_example/settings.py | 13 +++++++++++++ .../django/instrumentation_example/urls.py | 13 +++++++++++++ .../django/instrumentation_example/wsgi.py | 13 +++++++++++++ docs/examples/django/manage.py | 14 ++++++++++++++ docs/examples/django/pages/admin.py | 13 +++++++++++++ docs/examples/django/pages/apps.py | 13 +++++++++++++ docs/examples/django/pages/models.py | 13 +++++++++++++ docs/examples/django/pages/tests.py | 13 +++++++++++++ docs/examples/django/pages/urls.py | 13 +++++++++++++ docs/examples/django/pages/views.py | 13 +++++++++++++ 11 files changed, 144 insertions(+) diff --git a/docs/examples/django/instrumentation_example/asgi.py b/docs/examples/django/instrumentation_example/asgi.py index b75cbf51ef9..dd8fb568f4a 100644 --- a/docs/examples/django/instrumentation_example/asgi.py +++ b/docs/examples/django/instrumentation_example/asgi.py @@ -1,3 +1,16 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. """ ASGI config for instrumentation_example project. diff --git a/docs/examples/django/instrumentation_example/settings.py b/docs/examples/django/instrumentation_example/settings.py index 1b4fbd5b441..ac0f5781b6f 100644 --- a/docs/examples/django/instrumentation_example/settings.py +++ b/docs/examples/django/instrumentation_example/settings.py @@ -1,3 +1,16 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. """ Django settings for instrumentation_example project. diff --git a/docs/examples/django/instrumentation_example/urls.py b/docs/examples/django/instrumentation_example/urls.py index 724aeafca0f..292467155f8 100644 --- a/docs/examples/django/instrumentation_example/urls.py +++ b/docs/examples/django/instrumentation_example/urls.py @@ -1,3 +1,16 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. """instrumentation_example URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: diff --git a/docs/examples/django/instrumentation_example/wsgi.py b/docs/examples/django/instrumentation_example/wsgi.py index d1031bb8ec3..70ea9e0db56 100644 --- a/docs/examples/django/instrumentation_example/wsgi.py +++ b/docs/examples/django/instrumentation_example/wsgi.py @@ -1,3 +1,16 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. """ WSGI config for instrumentation_example project. diff --git a/docs/examples/django/manage.py b/docs/examples/django/manage.py index 592d0f377b4..38e08c023c0 100755 --- a/docs/examples/django/manage.py +++ b/docs/examples/django/manage.py @@ -1,4 +1,18 @@ #!/usr/bin/env python +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. + """Django"s command-line utility for administrative tasks.""" import os import sys diff --git a/docs/examples/django/pages/admin.py b/docs/examples/django/pages/admin.py index 8c38f3f3dad..9fc0d43c932 100644 --- a/docs/examples/django/pages/admin.py +++ b/docs/examples/django/pages/admin.py @@ -1,3 +1,16 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. from django.contrib import admin # Register your models here. diff --git a/docs/examples/django/pages/apps.py b/docs/examples/django/pages/apps.py index 344e0f0cf68..0f12b7b66ca 100644 --- a/docs/examples/django/pages/apps.py +++ b/docs/examples/django/pages/apps.py @@ -1,3 +1,16 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. from django.apps import AppConfig diff --git a/docs/examples/django/pages/models.py b/docs/examples/django/pages/models.py index 71a83623907..d6a83686abd 100644 --- a/docs/examples/django/pages/models.py +++ b/docs/examples/django/pages/models.py @@ -1,3 +1,16 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. from django.db import models # Create your models here. diff --git a/docs/examples/django/pages/tests.py b/docs/examples/django/pages/tests.py index 7ce503c2dd9..dbff933a8fa 100644 --- a/docs/examples/django/pages/tests.py +++ b/docs/examples/django/pages/tests.py @@ -1,3 +1,16 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. from django.test import TestCase # Create your tests here. diff --git a/docs/examples/django/pages/urls.py b/docs/examples/django/pages/urls.py index 485f176f8f5..99c95765a42 100644 --- a/docs/examples/django/pages/urls.py +++ b/docs/examples/django/pages/urls.py @@ -1,3 +1,16 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. from django.urls import path from .views import home_page_view diff --git a/docs/examples/django/pages/views.py b/docs/examples/django/pages/views.py index 3f20467cd70..d54633c3298 100644 --- a/docs/examples/django/pages/views.py +++ b/docs/examples/django/pages/views.py @@ -1,3 +1,16 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. from django.http import HttpResponse from opentelemetry import trace From 39af40bedbd58aea64545186c69e3fea8d8f4bac Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 29 Apr 2020 19:10:42 -0600 Subject: [PATCH 44/58] Fix docstring --- .../src/opentelemetry/ext/django/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py index aad6193593d..796ad474aa3 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py @@ -24,7 +24,7 @@ class DjangoInstrumentor(BaseInstrumentor): - """A instrumentor for flask.Django + """An instrumentor for Django See `BaseInstrumentor` """ From 68eb941c5c551bbc1aaf4ff2c848ce46a5ee5bc6 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 29 Apr 2020 20:07:42 -0600 Subject: [PATCH 45/58] Remove unnecessary lines --- tox.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/tox.ini b/tox.ini index 6d3b43ab0f9..7ad6b78666f 100644 --- a/tox.ini +++ b/tox.ini @@ -174,10 +174,8 @@ commands_pre = grpc: pip install {toxinidir}/ext/opentelemetry-ext-grpc[test] - ext: pip install {toxinidir}/opentelemetry-api wsgi,flask,django: pip install {toxinidir}/tests/util wsgi,flask,django: pip install {toxinidir}/ext/opentelemetry-ext-wsgi - wsgi,flask,django: pip install {toxinidir}/opentelemetry-sdk flask,django: pip install {toxinidir}/opentelemetry-auto-instrumentation flask: pip install {toxinidir}/ext/opentelemetry-ext-flask[test] From bf56249d858bd0dd819403429c8e1688dbcb5ec3 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 1 May 2020 11:11:05 -0600 Subject: [PATCH 46/58] Remove unnecessary files --- docs/examples/django/pages/admin.py | 16 ---------------- docs/examples/django/pages/models.py | 16 ---------------- docs/examples/django/pages/tests.py | 16 ---------------- 3 files changed, 48 deletions(-) delete mode 100644 docs/examples/django/pages/admin.py delete mode 100644 docs/examples/django/pages/models.py delete mode 100644 docs/examples/django/pages/tests.py diff --git a/docs/examples/django/pages/admin.py b/docs/examples/django/pages/admin.py deleted file mode 100644 index 9fc0d43c932..00000000000 --- a/docs/examples/django/pages/admin.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# 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. -from django.contrib import admin - -# Register your models here. diff --git a/docs/examples/django/pages/models.py b/docs/examples/django/pages/models.py deleted file mode 100644 index d6a83686abd..00000000000 --- a/docs/examples/django/pages/models.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# 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. -from django.db import models - -# Create your models here. diff --git a/docs/examples/django/pages/tests.py b/docs/examples/django/pages/tests.py deleted file mode 100644 index dbff933a8fa..00000000000 --- a/docs/examples/django/pages/tests.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# 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. -from django.test import TestCase - -# Create your tests here. From f10e451562e806c9612a9bc1a57a8f5ca783902d Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 1 May 2020 11:28:24 -0600 Subject: [PATCH 47/58] Small improvement in documentation --- docs/examples/django/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/django/README.rst b/docs/examples/django/README.rst index 579a7ec3db0..1fdd7a97a65 100644 --- a/docs/examples/django/README.rst +++ b/docs/examples/django/README.rst @@ -136,7 +136,7 @@ output similar to this one: "links": [] } -This last output are spans automatically generated by the OpenTelemetry Django +The last output shows spans automatically generated by the OpenTelemetry Django Instrumentation package. References From 1e535cf5f37359aaa1f3d475e68ad57ed0fff645 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 1 May 2020 11:38:32 -0600 Subject: [PATCH 48/58] Remove warning Co-authored-by: Mathieu Hinderyckx --- tox.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/tox.ini b/tox.ini index 7ad6b78666f..84dd157dbaa 100644 --- a/tox.ini +++ b/tox.ini @@ -28,8 +28,6 @@ envlist = py3{4,5,6,7,8}-test-example-http pypy3-test-example-http - ; FIXME find versions of Django that can work with these old Python versions - ; opentelemetry-ext-django py3{6,7,8}-test-ext-django pypy3-test-ext-django From fe1a6b5bf88218aaafe4e2b6b0bc47405cc4954f Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 1 May 2020 12:44:10 -0600 Subject: [PATCH 49/58] Linking pages configuration Co-authored-by: Mathieu Hinderyckx --- .../django/instrumentation_example/settings.py | 1 - docs/examples/django/pages/__init__.py | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/examples/django/instrumentation_example/settings.py b/docs/examples/django/instrumentation_example/settings.py index ac0f5781b6f..b5b8897b91b 100644 --- a/docs/examples/django/instrumentation_example/settings.py +++ b/docs/examples/django/instrumentation_example/settings.py @@ -50,7 +50,6 @@ "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", - "pages.apps.PagesConfig", ] MIDDLEWARE = [ diff --git a/docs/examples/django/pages/__init__.py b/docs/examples/django/pages/__init__.py index e69de29bb2d..5855e41f3a5 100644 --- a/docs/examples/django/pages/__init__.py +++ b/docs/examples/django/pages/__init__.py @@ -0,0 +1,15 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# 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. + +default_app_config = "pages.apps.PagesConfig" From 27ee3e6030a13527bdf51e387d2c7a58909c2444 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 1 May 2020 14:22:06 -0600 Subject: [PATCH 50/58] Update docs/examples/django/README.rst Co-authored-by: alrex --- docs/examples/django/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/django/README.rst b/docs/examples/django/README.rst index 1fdd7a97a65..ee22f7de0a9 100644 --- a/docs/examples/django/README.rst +++ b/docs/examples/django/README.rst @@ -41,7 +41,7 @@ Set these environment variables first: #. `export OPENTELEMETRY_PYTHON_DJANGO_INSTRUMENT=True` #. `export DJANGO_SETTINGS_MODULE=instrumentation_example.settings` -Clone the `opentelemetry-python` repository and go to `opentelemetry-python/ext/opentelemetry-ext-django/example`. +Clone the `opentelemetry-python` repository and go to `opentelemetry-python/docs/examples/django`. Once there run this command: From d1c6ec8bd968186817b9853feea474d2f16a05c4 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 1 May 2020 14:22:21 -0600 Subject: [PATCH 51/58] Update docs/examples/django/README.rst Co-authored-by: alrex --- docs/examples/django/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/django/README.rst b/docs/examples/django/README.rst index ee22f7de0a9..ca6d7abdc9c 100644 --- a/docs/examples/django/README.rst +++ b/docs/examples/django/README.rst @@ -45,7 +45,7 @@ Clone the `opentelemetry-python` repository and go to `opentelemetry-python/docs Once there run this command: -`opentelemetry-python-autoinstrumentation python3 manage.py runserver` +`opentelemetry-auto-instrumentation python3 manage.py runserver` Execution of the client ....................... From 780ec0e63110bb5f8304296bf09e4ac40cd4ffb6 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 1 May 2020 14:29:09 -0600 Subject: [PATCH 52/58] Add link to Django website --- docs/examples/django/README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/examples/django/README.rst b/docs/examples/django/README.rst index ca6d7abdc9c..42f41a489b2 100644 --- a/docs/examples/django/README.rst +++ b/docs/examples/django/README.rst @@ -142,5 +142,6 @@ Instrumentation package. References ---------- +* `Django `_ * `OpenTelemetry Project `_ * `OpenTelemetry Django extension `_ From c8b44af80e0001dce154e2b7b90b6b844043327e Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 6 May 2020 09:37:59 -0600 Subject: [PATCH 53/58] Refactoring example --- docs/examples/django/README.rst | 48 ++----------------- docs/examples/django/manage.py | 6 +++ .../src/opentelemetry/ext/django/__init__.py | 4 +- .../opentelemetry/ext/django/middleware.py | 2 +- .../opentelemetry/ext/sqlalchemy/__init__.py | 6 ++- 5 files changed, 17 insertions(+), 49 deletions(-) diff --git a/docs/examples/django/README.rst b/docs/examples/django/README.rst index 42f41a489b2..a4a1474dedf 100644 --- a/docs/examples/django/README.rst +++ b/docs/examples/django/README.rst @@ -23,9 +23,7 @@ Installation .. code-block:: - $ pip install opentelemetry-api $ pip install opentelemetry-sdk - $ pip install opentelemetry-auto-instrumentation $ pip install opentelemetry-ext-django $ pip install requests @@ -43,9 +41,10 @@ Set these environment variables first: Clone the `opentelemetry-python` repository and go to `opentelemetry-python/docs/examples/django`. -Once there run this command: +Once there, open the `manage.py` file. The call to `DjangoInstrumentor.instrument()` +in `main` is all that is needed to make the app be instrumented. -`opentelemetry-auto-instrumentation python3 manage.py runserver` +Run the Django app with `python manage.py runserver`. Execution of the client ....................... @@ -59,47 +58,6 @@ run the client with: `python client.py hello` -This should produce output similar to this one: - -.. code-block:: - - { - "name": "client-server", - "context": { - "trace_id": "0xa97f61b977a8cd4a21e3c43b8fae3708", - "span_id": "0x6290f0a6c49a5e9c", - "trace_state": "{}" - }, - "kind": "SpanKind.INTERNAL", - "parent_id": "0x0af82d771221ac0a", - "start_time": "2020-04-26T01:48:51.793095Z", - "end_time": "2020-04-26T01:48:51.798319Z", - "status": { - "canonical_code": "OK" - }, - "attributes": {}, - "events": [], - "links": [] - } - { - "name": "client", - "context": { - "trace_id": "0xa97f61b977a8cd4a21e3c43b8fae3708", - "span_id": "0x0af82d771221ac0a", - "trace_state": "{}" - }, - "kind": "SpanKind.INTERNAL", - "parent_id": null, - "start_time": "2020-04-26T01:48:51.792938Z", - "end_time": "2020-04-26T01:48:51.798535Z", - "status": { - "canonical_code": "OK" - }, - "attributes": {}, - "events": [], - "links": [] - } - Go to the previous console, where the Django app is running. You should see output similar to this one: diff --git a/docs/examples/django/manage.py b/docs/examples/django/manage.py index 38e08c023c0..fdf32287c5c 100755 --- a/docs/examples/django/manage.py +++ b/docs/examples/django/manage.py @@ -17,8 +17,14 @@ import os import sys +from opentelemetry.ext.django import DjangoInstrumentor + def main(): + + # This call is what makes the Django application be instrumented + DjangoInstrumentor().instrument() + os.environ.setdefault( "DJANGO_SETTINGS_MODULE", "instrumentation_example.settings" ) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py index 796ad474aa3..f59d90b4903 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/__init__.py @@ -18,7 +18,7 @@ from opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor from opentelemetry.configuration import Configuration -from opentelemetry.ext.django.middleware import DjangoMiddleware +from opentelemetry.ext.django.middleware import _DjangoMiddleware _logger = getLogger(__name__) @@ -30,7 +30,7 @@ class DjangoInstrumentor(BaseInstrumentor): """ _opentelemetry_middleware = ".".join( - [DjangoMiddleware.__module__, DjangoMiddleware.__qualname__] + [_DjangoMiddleware.__module__, _DjangoMiddleware.__qualname__] ) def _instrument(self, **kwargs): diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py index 0c85c968d31..01090a477e3 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py @@ -29,7 +29,7 @@ _logger = getLogger(__name__) -class DjangoMiddleware(MiddlewareMixin): +class _DjangoMiddleware(MiddlewareMixin): """Django Middleware for OpenTelemetry """ diff --git a/ext/opentelemetry-ext-sqlalchemy/src/opentelemetry/ext/sqlalchemy/__init__.py b/ext/opentelemetry-ext-sqlalchemy/src/opentelemetry/ext/sqlalchemy/__init__.py index 19078fe0a5c..34d16e75941 100644 --- a/ext/opentelemetry-ext-sqlalchemy/src/opentelemetry/ext/sqlalchemy/__init__.py +++ b/ext/opentelemetry-ext-sqlalchemy/src/opentelemetry/ext/sqlalchemy/__init__.py @@ -46,6 +46,7 @@ import wrapt from wrapt import wrap_function_wrapper as _w +from opentelemetry.configuration import Configuration from opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor from opentelemetry.ext.sqlalchemy.engine import ( EngineTracer, @@ -84,7 +85,10 @@ def _instrument(self, **kwargs): """ _w("sqlalchemy", "create_engine", _wrap_create_engine) _w("sqlalchemy.engine", "create_engine", _wrap_create_engine) - if kwargs.get("engine") is not None: + + engine = kwargs.get("engine", Configuration.SQL_ALCHEMY_ENGINE) + + if engine is not None: return EngineTracer( _get_tracer( kwargs.get("engine"), kwargs.get("tracer_provider") From 3aff951f16c3aae1045df6c610fd4fee7489693b Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 6 May 2020 09:40:58 -0600 Subject: [PATCH 54/58] Add emphasis --- docs/examples/django/README.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/examples/django/README.rst b/docs/examples/django/README.rst index a4a1474dedf..2b2f158e7a6 100644 --- a/docs/examples/django/README.rst +++ b/docs/examples/django/README.rst @@ -39,9 +39,12 @@ Set these environment variables first: #. `export OPENTELEMETRY_PYTHON_DJANGO_INSTRUMENT=True` #. `export DJANGO_SETTINGS_MODULE=instrumentation_example.settings` +The way to achieve OpenTelemetry instrumentation for your Django app is to use +an `opentelemetry.ext.django.DjangoInstrumentor` to instrument the app. + Clone the `opentelemetry-python` repository and go to `opentelemetry-python/docs/examples/django`. -Once there, open the `manage.py` file. The call to `DjangoInstrumentor.instrument()` +Once there, open the `manage.py` file. The call to `DjangoInstrumentor().instrument()` in `main` is all that is needed to make the app be instrumented. Run the Django app with `python manage.py runserver`. From 5e862e6e124f5126af368d31d14ce2308eebe686 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 6 May 2020 09:42:45 -0600 Subject: [PATCH 55/58] Use instrumentation --- ext/opentelemetry-ext-django/setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-django/setup.cfg b/ext/opentelemetry-ext-django/setup.cfg index 2d26833f551..c308a6f3521 100644 --- a/ext/opentelemetry-ext-django/setup.cfg +++ b/ext/opentelemetry-ext-django/setup.cfg @@ -14,7 +14,7 @@ # [metadata] name = opentelemetry-ext-django -description = OpenTelemetry Instrumentor for Django +description = OpenTelemetry Instrumentation for Django long_description = file: README.rst long_description_content_type = text/x-rst author = OpenTelemetry Authors From 5fd372b7e4ac596f546e0872151a43384c5ac116 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 6 May 2020 09:46:32 -0600 Subject: [PATCH 56/58] Fixed Readme --- ext/opentelemetry-ext-django/README.rst | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/ext/opentelemetry-ext-django/README.rst b/ext/opentelemetry-ext-django/README.rst index 425d6b06d6b..b922046ca6f 100644 --- a/ext/opentelemetry-ext-django/README.rst +++ b/ext/opentelemetry-ext-django/README.rst @@ -1,15 +1,24 @@ -OpenTelemetry Django Instrumentation -==================================== +OpenTelemetry Django Tracing +============================ -This instrumentation creates OpenTelemetry spans for Django applications. +|pypi| -Once this package is installed, run your Django application like this: +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-ext-django.svg + :target: https://pypi.org/project/opentelemetry-ext-django/ + +This library allows tracing requests for Django applications. + +Installation +------------ + +:: + + pip install opentelemetry-ext-django -`opentelemetry-python-autoinstrumentation python3 manage.py runserver` References ---------- +* `Django `_ +* `OpenTelemetry Django Tracing `_ * `OpenTelemetry Project `_ -* `OpenTelemetry WSGI extension `_ -* `OpenTelemetry Django extension `_ From 853c1e5e361d9ae1c68d05c001f2faedcc030391 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 6 May 2020 09:56:02 -0600 Subject: [PATCH 57/58] Add checks --- .../opentelemetry/ext/django/middleware.py | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py index 01090a477e3..642afaaf4ed 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py @@ -79,18 +79,22 @@ def process_exception(self, request, exception): # Django can call this method and process_response later. In order # to avoid __exit__ and detach from being called twice then, the # respective keys are being removed here. - request.META[self._environ_activation_key].__exit__( - type(exception), - exception, - getattr(exception, "__traceback__", None), - ) - request.META.pop(self._environ_activation_key) + if self._environ_activation_key in request.META.keys(): + request.META[self._environ_activation_key].__exit__( + type(exception), + exception, + getattr(exception, "__traceback__", None), + ) + request.META.pop(self._environ_activation_key) - detach(request.environ[self._environ_token]) - request.META.pop(self._environ_token) + detach(request.environ[self._environ_token]) + request.META.pop(self._environ_token, None) def process_response(self, request, response): - if self._environ_activation_key in request.META.keys(): + 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), From 07af392880403911bb22f11085d6471cef60e2cb Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 6 May 2020 10:28:28 -0600 Subject: [PATCH 58/58] Make lint pass --- .flake8 | 3 ++- .../src/opentelemetry/ext/django/middleware.py | 4 ++-- .../src/opentelemetry/ext/sqlalchemy/__init__.py | 6 +----- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.flake8 b/.flake8 index 5922f31d8f3..f511c4c3e01 100644 --- a/.flake8 +++ b/.flake8 @@ -2,7 +2,8 @@ ignore = E501 # line too long, defer to black F401 # unused import, defer to pylint - W503 # allow line breaks after binary ops, not after + W503 # allow line breaks before binary ops + W504 # allow line breaks after binary ops E203 # allow whitespace before ':' (https://github.com/psf/black#slices) exclude = .bzr diff --git a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py index 642afaaf4ed..5974c5e5030 100644 --- a/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py +++ b/ext/opentelemetry-ext-django/src/opentelemetry/ext/django/middleware.py @@ -92,8 +92,8 @@ def process_exception(self, request, exception): def process_response(self, request, response): if ( - self._environ_activation_key in request.META.keys() and - self._environ_span_key in request.META.keys() + 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], diff --git a/ext/opentelemetry-ext-sqlalchemy/src/opentelemetry/ext/sqlalchemy/__init__.py b/ext/opentelemetry-ext-sqlalchemy/src/opentelemetry/ext/sqlalchemy/__init__.py index 34d16e75941..19078fe0a5c 100644 --- a/ext/opentelemetry-ext-sqlalchemy/src/opentelemetry/ext/sqlalchemy/__init__.py +++ b/ext/opentelemetry-ext-sqlalchemy/src/opentelemetry/ext/sqlalchemy/__init__.py @@ -46,7 +46,6 @@ import wrapt from wrapt import wrap_function_wrapper as _w -from opentelemetry.configuration import Configuration from opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor from opentelemetry.ext.sqlalchemy.engine import ( EngineTracer, @@ -85,10 +84,7 @@ def _instrument(self, **kwargs): """ _w("sqlalchemy", "create_engine", _wrap_create_engine) _w("sqlalchemy.engine", "create_engine", _wrap_create_engine) - - engine = kwargs.get("engine", Configuration.SQL_ALCHEMY_ENGINE) - - if engine is not None: + if kwargs.get("engine") is not None: return EngineTracer( _get_tracer( kwargs.get("engine"), kwargs.get("tracer_provider")