Skip to content

Commit 38fd216

Browse files
ocelotlmauriciovasquezbernalmhinderycodebotentoumorokoshi
authored
Add Django instrumentation (#593)
Initial Instrumentation Co-authored-by: Mauricio Vásquez <mauricio@kinvolk.io> Co-authored-by: Mathieu Hinderyckx <mathieu.hinderyckx@gmail.com> Co-authored-by: alrex <alrex.boten@gmail.com> Co-authored-by: Yusuke Tsutsumi <yusuke@tsutsumi.io>
1 parent 0889895 commit 38fd216

File tree

28 files changed

+984
-4
lines changed

28 files changed

+984
-4
lines changed

.flake8

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
ignore =
33
E501 # line too long, defer to black
44
F401 # unused import, defer to pylint
5-
W503 # allow line breaks after binary ops, not after
5+
W503 # allow line breaks before binary ops
6+
W504 # allow line breaks after binary ops
67
E203 # allow whitespace before ':' (https://github.com/psf/black#slices)
78
exclude =
89
.bzr

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,7 @@ _build/
5656
# mypy
5757
.mypy_cache/
5858
target
59+
60+
# Django example
61+
62+
docs/examples/django/db.sqlite3

docs/examples/django/README.rst

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
OpenTelemetry Django Instrumentation Example
2+
============================================
3+
4+
This shows how to use `opentelemetry-ext-django` to automatically instrument a
5+
Django app.
6+
7+
For more user convenience, a Django app is already provided in this directory.
8+
9+
Preparation
10+
-----------
11+
12+
This example will be executed in a separate virtual environment:
13+
14+
.. code-block::
15+
16+
$ mkdir django_auto_instrumentation
17+
$ virtualenv django_auto_instrumentation
18+
$ source django_auto_instrumentation/bin/activate
19+
20+
21+
Installation
22+
------------
23+
24+
.. code-block::
25+
26+
$ pip install opentelemetry-sdk
27+
$ pip install opentelemetry-ext-django
28+
$ pip install requests
29+
30+
31+
Execution
32+
---------
33+
34+
Execution of the Django app
35+
...........................
36+
37+
Set these environment variables first:
38+
39+
#. `export OPENTELEMETRY_PYTHON_DJANGO_INSTRUMENT=True`
40+
#. `export DJANGO_SETTINGS_MODULE=instrumentation_example.settings`
41+
42+
The way to achieve OpenTelemetry instrumentation for your Django app is to use
43+
an `opentelemetry.ext.django.DjangoInstrumentor` to instrument the app.
44+
45+
Clone the `opentelemetry-python` repository and go to `opentelemetry-python/docs/examples/django`.
46+
47+
Once there, open the `manage.py` file. The call to `DjangoInstrumentor().instrument()`
48+
in `main` is all that is needed to make the app be instrumented.
49+
50+
Run the Django app with `python manage.py runserver`.
51+
52+
Execution of the client
53+
.......................
54+
55+
Open up a new console and activate the previous virtual environment there too:
56+
57+
`source django_auto_instrumentation/bin/activate`
58+
59+
Go to `opentelemetry-python/ext/opentelemetry-ext-django/example`, once there
60+
run the client with:
61+
62+
`python client.py hello`
63+
64+
Go to the previous console, where the Django app is running. You should see
65+
output similar to this one:
66+
67+
.. code-block::
68+
69+
{
70+
"name": "home_page_view",
71+
"context": {
72+
"trace_id": "0xed88755c56d95d05a506f5f70e7849b9",
73+
"span_id": "0x0a94c7a60e0650d5",
74+
"trace_state": "{}"
75+
},
76+
"kind": "SpanKind.SERVER",
77+
"parent_id": "0x3096ef92e621c22d",
78+
"start_time": "2020-04-26T01:49:57.205833Z",
79+
"end_time": "2020-04-26T01:49:57.206214Z",
80+
"status": {
81+
"canonical_code": "OK"
82+
},
83+
"attributes": {
84+
"component": "http",
85+
"http.method": "GET",
86+
"http.server_name": "localhost",
87+
"http.scheme": "http",
88+
"host.port": 8000,
89+
"http.host": "localhost:8000",
90+
"http.url": "http://localhost:8000/?param=hello",
91+
"net.peer.ip": "127.0.0.1",
92+
"http.flavor": "1.1",
93+
"http.status_text": "OK",
94+
"http.status_code": 200
95+
},
96+
"events": [],
97+
"links": []
98+
}
99+
100+
The last output shows spans automatically generated by the OpenTelemetry Django
101+
Instrumentation package.
102+
103+
References
104+
----------
105+
106+
* `Django <https://djangoproject.com/>`_
107+
* `OpenTelemetry Project <https://opentelemetry.io/>`_
108+
* `OpenTelemetry Django extension <https://github.com/open-telemetry/opentelemetry-python/tree/master/ext/opentelemetry-ext-django>`_

docs/examples/django/client.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from sys import argv
16+
17+
from requests import get
18+
19+
from opentelemetry import propagators, trace
20+
from opentelemetry.sdk.trace import TracerProvider
21+
from opentelemetry.sdk.trace.export import (
22+
ConsoleSpanExporter,
23+
SimpleExportSpanProcessor,
24+
)
25+
26+
trace.set_tracer_provider(TracerProvider())
27+
tracer = trace.get_tracer_provider().get_tracer(__name__)
28+
29+
trace.get_tracer_provider().add_span_processor(
30+
SimpleExportSpanProcessor(ConsoleSpanExporter())
31+
)
32+
33+
34+
with tracer.start_as_current_span("client"):
35+
36+
with tracer.start_as_current_span("client-server"):
37+
headers = {}
38+
propagators.inject(dict.__setitem__, headers)
39+
requested = get(
40+
"http://localhost:8000",
41+
params={"param": argv[1]},
42+
headers=headers,
43+
)
44+
45+
assert requested.status_code == 200

docs/examples/django/instrumentation_example/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""
15+
ASGI config for instrumentation_example project.
16+
17+
It exposes the ASGI callable as a module-level variable named ``application``.
18+
19+
For more information on this file, see
20+
https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/
21+
"""
22+
23+
import os
24+
25+
from django.core.asgi import get_asgi_application
26+
27+
os.environ.setdefault(
28+
"DJANGO_SETTINGS_MODULE", "instrumentation_example.settings"
29+
)
30+
31+
application = get_asgi_application()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""
15+
Django settings for instrumentation_example project.
16+
17+
Generated by "django-admin startproject" using Django 3.0.4.
18+
19+
For more information on this file, see
20+
https://docs.djangoproject.com/en/3.0/topics/settings/
21+
22+
For the full list of settings and their values, see
23+
https://docs.djangoproject.com/en/3.0/ref/settings/
24+
"""
25+
26+
import os
27+
28+
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
29+
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
30+
31+
32+
# Quick-start development settings - unsuitable for production
33+
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
34+
35+
# SECURITY WARNING: keep the secret key used in production secret!
36+
SECRET_KEY = "it%*!=l2(fcawu=!m-06n&#j(iq2j#%$fu6)myi*b9i5ojk+6+"
37+
38+
# SECURITY WARNING: don"t run with debug turned on in production!
39+
DEBUG = True
40+
41+
ALLOWED_HOSTS = []
42+
43+
44+
# Application definition
45+
46+
INSTALLED_APPS = [
47+
"django.contrib.admin",
48+
"django.contrib.auth",
49+
"django.contrib.contenttypes",
50+
"django.contrib.sessions",
51+
"django.contrib.messages",
52+
"django.contrib.staticfiles",
53+
]
54+
55+
MIDDLEWARE = [
56+
"django.middleware.security.SecurityMiddleware",
57+
"django.contrib.sessions.middleware.SessionMiddleware",
58+
"django.middleware.common.CommonMiddleware",
59+
"django.middleware.csrf.CsrfViewMiddleware",
60+
"django.contrib.auth.middleware.AuthenticationMiddleware",
61+
"django.contrib.messages.middleware.MessageMiddleware",
62+
"django.middleware.clickjacking.XFrameOptionsMiddleware",
63+
]
64+
65+
ROOT_URLCONF = "instrumentation_example.urls"
66+
67+
TEMPLATES = [
68+
{
69+
"BACKEND": "django.template.backends.django.DjangoTemplates",
70+
"DIRS": [],
71+
"APP_DIRS": True,
72+
"OPTIONS": {
73+
"context_processors": [
74+
"django.template.context_processors.debug",
75+
"django.template.context_processors.request",
76+
"django.contrib.auth.context_processors.auth",
77+
"django.contrib.messages.context_processors.messages",
78+
],
79+
},
80+
},
81+
]
82+
83+
WSGI_APPLICATION = "instrumentation_example.wsgi.application"
84+
85+
86+
# Database
87+
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
88+
89+
DATABASES = {
90+
"default": {
91+
"ENGINE": "django.db.backends.sqlite3",
92+
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
93+
}
94+
}
95+
96+
97+
# Password validation
98+
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
99+
100+
AUTH_PASSWORD_VALIDATORS = [
101+
{
102+
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
103+
},
104+
{
105+
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
106+
},
107+
{
108+
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
109+
},
110+
{
111+
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
112+
},
113+
]
114+
115+
116+
# Internationalization
117+
# https://docs.djangoproject.com/en/3.0/topics/i18n/
118+
119+
LANGUAGE_CODE = "en-us"
120+
121+
TIME_ZONE = "UTC"
122+
123+
USE_I18N = True
124+
125+
USE_L10N = True
126+
127+
USE_TZ = True
128+
129+
130+
# Static files (CSS, JavaScript, Images)
131+
# https://docs.djangoproject.com/en/3.0/howto/static-files/
132+
133+
STATIC_URL = "/static/"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""instrumentation_example URL Configuration
15+
16+
The `urlpatterns` list routes URLs to views. For more information please see:
17+
https://docs.djangoproject.com/en/3.0/topics/http/urls/
18+
Examples:
19+
Function views
20+
1. Add an import: from my_app import views
21+
2. Add a URL to urlpatterns: path("", views.home, name="home")
22+
Class-based views
23+
1. Add an import: from other_app.views import Home
24+
2. Add a URL to urlpatterns: path("", Home.as_view(), name="home")
25+
Including another URLconf
26+
1. Import the include() function: from django.urls import include, path
27+
2. Add a URL to urlpatterns: path("blog/", include("blog.urls"))
28+
"""
29+
from django.contrib import admin
30+
from django.urls import include, path
31+
32+
urlpatterns = [
33+
path("admin/", admin.site.urls),
34+
path("", include("pages.urls")),
35+
]

0 commit comments

Comments
 (0)