Skip to content

Commit 3275a03

Browse files
authored
Smarter, better, easier Django initialization (#77)
* Smarter, better, easier Django initialization * Report Django middleware; Detect py console * Priority to newer Django conventions; store list of middleware * Add Python flavor and architecture into snapshot * Default list type. * Standardize on import style; clean up load order * Move to use only one autowrapt entry point. * Better version comparison. * runtime is now deprecated; remove the file. * Update documentation to follow changes.
1 parent 2f0517e commit 3275a03

File tree

11 files changed

+140
-147
lines changed

11 files changed

+140
-147
lines changed

INSTALLATION.md

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,50 +12,70 @@ or to alternatively update an existing installation:
1212

1313
# Automated
1414

15-
The Instana package sensor can be enabled without any code modifications. To do this, set the following environment variable for your Python application:
15+
The Instana package sensor can be enabled without any code modifications required. To do this, install the package and set the following environment variable for your Python application:
1616

17-
AUTOWRAPT_BOOTSTRAP=runtime
17+
AUTOWRAPT_BOOTSTRAP=instana
1818

19-
This will cause the Instana Python package to automatically instrument your Python application. Once it finds the host agent, it will begin to report Python metrics.
19+
This will cause the Instana Python package to automatically instrument your Python application. Once it finds the Instana host agent, it will begin to report Python metrics.
2020

2121
# Manual
2222

23-
In any Python 2.7 or great application, to manually enable the Instana sensor, simply import the package:
23+
In any Python 2.7 or greater application, to manually enable the Instana sensor, simply import the package:
2424

2525
import instana
2626

27-
This will initialize the package and it will begin to report key Python metrics.
27+
# Flask
2828

29-
# Django (Automated)
29+
To enable the Flask instrumentation, set the following environment variable in your _application boot environment_ and then restart your application:
3030

31-
The Instana package offers a method to automatically instrument your Django application without any code changes required. To enable the Django instrumetation, set the environment variable `AUTOWRAPT_BOOTSTRAP=django` (use `django19` for Django version 1.9.x) for your Python application.
31+
`export AUTOWRAPT_BOOTSTRAP=flask`
32+
33+
# WSGI Stacks
3234

33-
# Django (Manual Installation)
35+
The Instana sensor includes WSGI middleware that can be added to any WSGI compliant stack. This is automated for various stacks but can also be done manually for those we haven't added support for yet.
3436

35-
To instead manually install the Instana Django middleware into your Django application, import the package and add `instana.django.InstanaMiddleware` to the top of your `MIDDLEWARE` (or `MIDDLEWARE_CLASSES` for earlier versions) in your `settings.py`:
37+
The general usage is:
3638

37-
```Python
39+
```python
3840
import instana
41+
from instana.wsgi import iWSGIMiddleware
3942

40-
MIDDLEWARE = [
41-
'instana.django.InstanaMiddleware',
42-
# ...
43-
'django.contrib.messages.middleware.MessageMiddleware',
44-
'django.middleware.clickjacking.XFrameOptionsMiddleware',
45-
]
43+
# Wrap the wsgi app in Instana middleware (iWSGIMiddleware)
44+
wsgiapp = iWSGIMiddleware(MyWSGIApplication())
4645
```
4746

48-
# Flask
47+
We are working to automate this for all major frameworks but in the meantime, here are some specific quick starts for those we don't have automatic support for yet.
4948

50-
To enable the Flask instrumentation, set the following environment variable in your _application boot environment_ and then restart your application:
49+
## CherryPy WSGI
5150

52-
`export AUTOWRAPT_BOOTSTRAP=flask`
51+
```python
52+
import cherrypy
53+
import instana
54+
from instana.wsgi import iWSGIMiddleware
5355

54-
# WSGI Stacks
56+
# My CherryPy application
57+
class Root(object):
58+
@cherrypy.expose
59+
def index(self):
60+
return "hello world"
61+
62+
cherrypy.config.update({'engine.autoreload.on': False})
63+
cherrypy.server.unsubscribe()
64+
cherrypy.engine.start()
65+
66+
# Wrap the wsgi app in Instana middleware (iWSGIMiddleware)
67+
wsgiapp = iWSGIMiddleware(cherrypy.tree.mount(Root()))
68+
```
69+
70+
In this example, we use uwsgi as the webserver and booted with:
71+
72+
uwsgi --socket 127.0.0.1:8080 --protocol=http --wsgi-file mycherry.py --callable wsgiapp -H ~/.local/share/virtualenvs/cherrypyapp-C1BUba0z
73+
74+
Where `~/.local/share/virtualenvs/cherrypyapp-C1BUba0z` is the path to my local virtualenv from pipenv
5575

56-
The Instana sensor bundles with it WSGI middleware. The usage of this middleware is automated for various frameworks but for those that arent' supported yet, see the [WSGI documentation](WSGI.md) for details on how to manually add it to your stack.
76+
# uWSGI Webserver
5777

58-
# uWSGI
78+
tldr; Make sure `enable-threads` and `lazy-apps` is enabled for uwsgi.
5979

6080
## Threads
6181

README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,15 @@ The instana package will automatically collect key metrics from your Python proc
1919

2020
`pip install instana` into the virtual-env or container ([hosted on pypi](https://pypi.python.org/pypi/instana))
2121

22-
See our detailed [Installation document](INSTALLATION.md) for environment specific information covering Django, Flask, End-user Monitoring (EUM) and more.
22+
The Instana package can then be activated _without any code changes required_ by setting the following environment variable for your Python application:
23+
24+
export AUTOWRAPT_BOOTSTRAP=instana
25+
26+
alternatively, if you prefer the manual method, simply import the `instana` package inside of your Python application:
27+
28+
import instana
29+
30+
See our detailed [Installation document](INSTALLATION.md) for additional information covering Django, Flask, End-user Monitoring (EUM) and more.
2331

2432
## OpenTracing
2533

@@ -47,8 +55,7 @@ Also note that under evented systems such as gevent, concurrence and/or greenlet
4755

4856
## Configuration
4957

50-
See [Configuration.md](https://github.com/instana/python-sensor/blob/master/Configuration.md)
51-
58+
For details on how to configure the Instana Python package, see [Configuration.md](https://github.com/instana/python-sensor/blob/master/Configuration.md)
5259

5360
## Documentation
5461

WSGI.md

Lines changed: 0 additions & 40 deletions
This file was deleted.

instana/__init__.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,6 @@
55
from .tracer import InstanaTracer
66
from .options import Options
77

8-
if "INSTANA_DISABLE_AUTO_INSTR" not in os.environ:
9-
# Import & initialize instrumentation
10-
# noqa: ignore=W0611
11-
from .instrumentation import urllib3 # noqa
12-
from .instrumentation import sudsjurko # noqa
13-
148
"""
159
The Instana package has two core components: the sensor and the tracer.
1610
@@ -29,6 +23,18 @@
2923
__maintainer__ = 'Peter Giacomo Lombardo'
3024
__email__ = '[email protected]'
3125

26+
27+
def load(module):
28+
"""
29+
Method used to activate the Instana sensor via AUTOWRAPT_BOOTSTRAP
30+
environment variable.
31+
"""
32+
if "INSTANA_DEV" in os.environ:
33+
print("==========================================================")
34+
print("Instana: Loading...")
35+
print("==========================================================")
36+
37+
3238
# For any given Python process, we only want one sensor as multiple would
3339
# collect/report metrics in duplicate, triplicate etc..
3440
#
@@ -65,3 +71,10 @@
6571

6672
if "INSTANA_SERVICE_NAME" in os.environ:
6773
service_name = os.environ["INSTANA_SERVICE_NAME"]
74+
75+
if "INSTANA_DISABLE_AUTO_INSTR" not in os.environ:
76+
# Import & initialize instrumentation
77+
# noqa: ignore=W0611
78+
from .instrumentation import urllib3 # noqa
79+
from .instrumentation import sudsjurko # noqa
80+
from .instrumentation.django import middleware # noqa

instana/instrumentation/django/__init__.py

Whitespace-only changes.

instana/django.py renamed to instana/instrumentation/django/middleware.py

Lines changed: 30 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
from __future__ import print_function
1+
from __future__ import absolute_import
22
import opentracing as ot
3-
from instana import internal_tracer
4-
from instana.log import logger
53
import opentracing.ext.tags as ext
64
import wrapt
7-
import os
5+
import sys
6+
from ...log import logger
7+
from ... import internal_tracer
88

99

10-
DJ_INSTANA_MIDDLEWARE = 'instana.django.InstanaMiddleware'
10+
11+
DJ_INSTANA_MIDDLEWARE = 'instana.instrumentation.django.middleware.InstanaMiddleware'
1112

1213
try:
1314
from django.utils.deprecation import MiddlewareMixin
@@ -63,65 +64,50 @@ def process_exception(self, request, exception):
6364
ec = self.span.tags.get('ec', 0)
6465
self.span.set_tag("ec", ec+1)
6566

67+
6668
def load_middleware_wrapper(wrapped, instance, args, kwargs):
6769
try:
6870
from django.conf import settings
6971

7072
# Django >=1.10 to <2.0 support old-style MIDDLEWARE_CLASSES so we
7173
# do as well here
72-
if hasattr(settings, 'MIDDLEWARE_CLASSES'):
73-
if DJ_INSTANA_MIDDLEWARE in settings.MIDDLEWARE_CLASSES:
74-
return wrapped(*args, **kwargs)
75-
76-
if type(settings.MIDDLEWARE_CLASSES) is tuple:
77-
settings.MIDDLEWARE_CLASSES = (DJ_INSTANA_MIDDLEWARE,) + settings.MIDDLEWARE_CLASSES
78-
elif type(settings.MIDDLEWARE_CLASSES) is list:
79-
settings.MIDDLEWARE_CLASSES = [DJ_INSTANA_MIDDLEWARE] + settings.MIDDLEWARE_CLASSES
80-
else:
81-
logger.warn("Instana: Couldn't add InstanaMiddleware to Django")
82-
elif hasattr(settings, 'MIDDLEWARE'):
74+
if hasattr(settings, 'MIDDLEWARE'):
8375
if DJ_INSTANA_MIDDLEWARE in settings.MIDDLEWARE:
8476
return wrapped(*args, **kwargs)
8577

78+
# Save the list of middleware for Snapshot reporting
79+
internal_tracer.sensor.meter.djmw = settings.MIDDLEWARE
80+
8681
if type(settings.MIDDLEWARE) is tuple:
8782
settings.MIDDLEWARE = (DJ_INSTANA_MIDDLEWARE,) + settings.MIDDLEWARE
8883
elif type(settings.MIDDLEWARE) is list:
8984
settings.MIDDLEWARE = [DJ_INSTANA_MIDDLEWARE] + settings.MIDDLEWARE
9085
else:
9186
logger.warn("Instana: Couldn't add InstanaMiddleware to Django")
92-
else:
93-
logger.warn("Instana: Couldn't find middleware settings")
9487

95-
return wrapped(*args, **kwargs)
96-
except Exception as e:
97-
logger.warn("Instana: Couldn't add InstanaMiddleware to Django: ", e)
88+
elif hasattr(settings, 'MIDDLEWARE_CLASSES'):
89+
if DJ_INSTANA_MIDDLEWARE in settings.MIDDLEWARE_CLASSES:
90+
return wrapped(*args, **kwargs)
9891

99-
def hook(module):
100-
""" Hook method to install the Instana middleware into Django >= 1.10 """
101-
if "INSTANA_DEV" in os.environ:
102-
print("==============================================================")
103-
print("Instana: Running django hook")
104-
print("==============================================================")
105-
wrapt.wrap_function_wrapper(module, 'BaseHandler.load_middleware', load_middleware_wrapper)
92+
# Save the list of middleware for Snapshot reporting
93+
internal_tracer.sensor.meter.djmw = settings.MIDDLEWARE_CLASSES
10694

95+
if type(settings.MIDDLEWARE_CLASSES) is tuple:
96+
settings.MIDDLEWARE_CLASSES = (DJ_INSTANA_MIDDLEWARE,) + settings.MIDDLEWARE_CLASSES
97+
elif type(settings.MIDDLEWARE_CLASSES) is list:
98+
settings.MIDDLEWARE_CLASSES = [DJ_INSTANA_MIDDLEWARE] + settings.MIDDLEWARE_CLASSES
99+
else:
100+
logger.warn("Instana: Couldn't add InstanaMiddleware to Django")
107101

108-
def hook19(module):
109-
try:
110-
""" Hook method to install the Instana middleware into Django <= 1.9 """
111-
if "INSTANA_DEV" in os.environ:
112-
print("==============================================================")
113-
print("Instana: Running django19 hook")
114-
print("==============================================================")
115-
116-
if DJ_INSTANA_MIDDLEWARE in module.settings.MIDDLEWARE_CLASSES:
117-
return
118-
119-
if type(module.settings.MIDDLEWARE_CLASSES) is tuple:
120-
module.settings.MIDDLEWARE_CLASSES = (DJ_INSTANA_MIDDLEWARE,) + module.settings.MIDDLEWARE_CLASSES
121-
elif type(module.settings.MIDDLEWARE_CLASSES) is list:
122-
module.settings.MIDDLEWARE_CLASSES = [DJ_INSTANA_MIDDLEWARE] + module.settings.MIDDLEWARE_CLASSES
123102
else:
124-
logger.warn("Instana: Couldn't add InstanaMiddleware to Django")
103+
logger.warn("Instana: Couldn't find middleware settings")
125104

105+
return wrapped(*args, **kwargs)
126106
except Exception as e:
127107
logger.warn("Instana: Couldn't add InstanaMiddleware to Django: ", e)
108+
109+
try:
110+
if 'django' in sys.modules:
111+
wrapt.wrap_function_wrapper('django.core.handlers.base', 'BaseHandler.load_middleware', load_middleware_wrapper)
112+
except:
113+
pass

instana/instrumentation/sudsjurko.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,35 @@
11
from __future__ import absolute_import
2-
import instana
3-
from instana.log import logger
2+
from distutils.version import LooseVersion
43
import opentracing
54
import opentracing.ext.tags as ext
65
import wrapt
6+
from ..log import logger
7+
from .. import internal_tracer
78

89

910
try:
1011
import suds # noqa
1112

12-
if (suds.version.__version__ <= '0.6'):
13+
if (LooseVersion(suds.version.__version__) <= LooseVersion('0.6')):
1314
class_method = 'SoapClient.send'
1415
else:
1516
class_method = '_SoapClient.send'
1617

1718
@wrapt.patch_function_wrapper('suds.client', class_method)
1819
def send_with_instana(wrapped, instance, args, kwargs):
19-
context = instana.internal_tracer.current_context()
20+
context = internal_tracer.current_context()
2021

2122
# If we're not tracing, just return
2223
if context is None:
2324
return wrapped(*args, **kwargs)
2425

2526
try:
26-
span = instana.internal_tracer.start_span("soap", child_of=context)
27+
span = internal_tracer.start_span("soap", child_of=context)
2728
span.set_tag('soap.action', instance.method.name)
2829
span.set_tag(ext.HTTP_URL, instance.method.location)
2930
span.set_tag(ext.HTTP_METHOD, 'POST')
3031

31-
instana.internal_tracer.inject(span.context, opentracing.Format.HTTP_HEADERS,
32+
internal_tracer.inject(span.context, opentracing.Format.HTTP_HEADERS,
3233
instance.options.headers)
3334

3435
rv = wrapped(*args, **kwargs)

0 commit comments

Comments
 (0)