Skip to content

Commit 967b575

Browse files
committed
Merge branch 'master' into better-django
2 parents 341269b + f0c70f9 commit 967b575

21 files changed

+390
-110
lines changed

Configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ The sensor tries to communicate with the Instana agent via IP 127.0.0.1 and as a
77
To use these, these environment variables should be set in the environment of the running Python process.
88

99
```shell
10-
export INSTANA_AGENT_IP = '127.0.0.1'
10+
export INSTANA_AGENT_HOST = '127.0.0.1'
1111
export INSTANA_AGENT_PORT = '42699'
1212
```
1313

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ For Django version 1.9.x, instead set:
3434
To enable the Flask instrumentation, set the following environment variable in your _application boot environment_ and then restart your application:
3535

3636
`export AUTOWRAPT_BOOTSTRAP=flask`
37+
38+
## WSGI Compliant Stacks
39+
40+
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.
3741

3842
## Runtime Monitoring Only
3943

@@ -53,6 +57,26 @@ This Python instrumentation spawns a lightweight background thread to periodical
5357

5458
If you use uWSGI in forking workers mode, you must specify `--lazy-apps` (or `lazy-apps = true` in ini style) to load the application in the worker instead of the master process.
5559

60+
### uWSGI Example: Command-line
61+
62+
```sh
63+
uwsgi --socket 0.0.0.0:5000 --protocol=http -w wsgi -p 4 --enable-threads --lazy-apps
64+
```
65+
66+
### uWSGI Example: ini file
67+
68+
```ini
69+
[uwsgi]
70+
http = :5000
71+
master = true
72+
processes = 4
73+
enable-threads = true # required
74+
lazy-apps = true # if using "processes", set lazy-apps to true
75+
76+
# Set the Instana sensor environment variable here
77+
env = AUTOWRAPT_BOOTSTRAP=flask
78+
```
79+
5680
## Usage
5781

5882
The instana package will automatically collect key metrics from your Python processes. Just install and go.

WSGI.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
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.
2+
3+
The general usage is:
4+
5+
```python
6+
import instana
7+
from instana.wsgi import iWSGIMiddleware
8+
9+
# Wrap the wsgi app in Instana middleware (iWSGIMiddleware)
10+
wsgiapp = iWSGIMiddleware(MyWSGIApplication())
11+
```
12+
13+
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.
14+
15+
## CherryPy
16+
17+
```python
18+
import cherrypy
19+
import instana
20+
from instana.wsgi import iWSGIMiddleware
21+
22+
# My CherryPy application
23+
class Root(object):
24+
@cherrypy.expose
25+
def index(self):
26+
return "hello world"
27+
28+
cherrypy.config.update({'engine.autoreload.on': False})
29+
cherrypy.server.unsubscribe()
30+
cherrypy.engine.start()
31+
32+
# Wrap the wsgi app in Instana middleware (iWSGIMiddleware)
33+
wsgiapp = iWSGIMiddleware(cherrypy.tree.mount(Root()))
34+
```
35+
36+
In this example, we use uwsgi as the webserver and booted with:
37+
38+
`uwsgi --socket 127.0.0.1:8080 --protocol=http --wsgi-file mycherry.py --callable wsgiapp -H ~/.local/share/virtualenvs/cherrypyapp-C1BUba0z`
39+
40+
Where `~/.local/share/virtualenvs/cherrypyapp-C1BUba0z` is the path to my local virtualenv from pipenv
File renamed without changes.

instana/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
__copyright__ = 'Copyright 2017 Instana Inc.'
2525
__credits__ = ['Pavlo Baron', 'Peter Giacomo Lombardo']
2626
__license__ = 'MIT'
27-
__version__ = '0.7.5'
27+
__version__ = '0.7.8'
2828
__maintainer__ = 'Peter Giacomo Lombardo'
2929
__email__ = '[email protected]'
3030

instana/fsm.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,14 @@ def lookup_agent_host(self, e):
7070
host = a.AGENT_DEFAULT_HOST
7171
port = a.AGENT_DEFAULT_PORT
7272

73-
if "INSTANA_AGENT_IP" in os.environ:
73+
if "INSTANA_AGENT_HOST" in os.environ:
74+
host = os.environ["INSTANA_AGENT_HOST"]
75+
if "INSTANA_AGENT_PORT" in os.environ:
76+
port = int(os.environ["INSTANA_AGENT_PORT"])
77+
78+
elif "INSTANA_AGENT_IP" in os.environ:
79+
# Deprecated: INSTANA_AGENT_IP environment variable
80+
# To be removed in a future version
7481
host = os.environ["INSTANA_AGENT_IP"]
7582
if "INSTANA_AGENT_PORT" in os.environ:
7683
port = int(os.environ["INSTANA_AGENT_PORT"])

instana/instrumentation/urllib3.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,40 @@
11
from __future__ import absolute_import
2-
import opentracing.ext.tags as ext
32
import instana
3+
from instana.log import logger
44
import opentracing
5+
import opentracing.ext.tags as ext
56
import wrapt
67

78

89
try:
9-
import urllib3
10+
import urllib3 # noqa
11+
12+
def collect(instance, args, kwargs):
13+
""" Build and return a fully qualified URL for this request """
14+
try:
15+
kvs = {}
16+
17+
kvs['host'] = instance.host
18+
kvs['port'] = instance.port
1019

11-
@wrapt.patch_function_wrapper('urllib3', 'PoolManager.urlopen')
20+
if args is not None and len(args) is 2:
21+
kvs['method'] = args[0]
22+
kvs['path'] = args[1]
23+
else:
24+
kvs['method'] = kwargs['method']
25+
kvs['path'] = kwargs['url']
26+
27+
if type(instance) is urllib3.connectionpool.HTTPSConnectionPool:
28+
kvs['url'] = 'https://%s:%d%s' % (kvs['host'], kvs['port'], kvs['path'])
29+
else:
30+
kvs['url'] = 'http://%s:%d%s' % (kvs['host'], kvs['port'], kvs['path'])
31+
except Exception as e:
32+
logger.debug(e)
33+
return kvs
34+
else:
35+
return kvs
36+
37+
@wrapt.patch_function_wrapper('urllib3', 'HTTPConnectionPool.urlopen')
1238
def urlopen_with_instana(wrapped, instance, args, kwargs):
1339
context = instana.internal_tracer.current_context()
1440

@@ -18,8 +44,12 @@ def urlopen_with_instana(wrapped, instance, args, kwargs):
1844

1945
try:
2046
span = instana.internal_tracer.start_span("urllib3", child_of=context)
21-
span.set_tag(ext.HTTP_URL, args[1])
22-
span.set_tag(ext.HTTP_METHOD, args[0])
47+
48+
kvs = collect(instance, args, kwargs)
49+
if 'url' in kvs:
50+
span.set_tag(ext.HTTP_URL, kvs['url'])
51+
if 'method' in kvs:
52+
span.set_tag(ext.HTTP_METHOD, kvs['method'])
2353

2454
instana.internal_tracer.inject(span.context, opentracing.Format.HTTP_HEADERS, kwargs["headers"])
2555
rv = wrapped(*args, **kwargs)

instana/json_span.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
class JsonSpan(object):
2+
t = 0
3+
p = None
4+
s = 0
5+
ts = 0
6+
ta = "py"
7+
d = 0
8+
n = None
9+
f = None
10+
ec = 0
11+
error = None
12+
data = None
13+
14+
def __init__(self, **kwds):
15+
for key in kwds:
16+
self.__dict__[key] = kwds[key]
17+
18+
19+
class Data(object):
20+
service = None
21+
http = None
22+
baggage = None
23+
custom = None
24+
sdk = None
25+
26+
def __init__(self, **kwds):
27+
self.__dict__.update(kwds)
28+
29+
30+
class HttpData(object):
31+
host = None
32+
url = None
33+
status = 0
34+
method = None
35+
36+
def __init__(self, **kwds):
37+
self.__dict__.update(kwds)
38+
39+
40+
class CustomData(object):
41+
tags = None
42+
logs = None
43+
44+
def __init__(self, **kwds):
45+
self.__dict__.update(kwds)
46+
47+
48+
class SDKData(object):
49+
name = None
50+
Type = None
51+
arguments = None
52+
Return = None
53+
custom = None
54+
55+
def __init__(self, **kwds):
56+
self.__dict__.update(kwds)

instana/options.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@ def __init__(self, **kwds):
1616
self.log_level = logging.DEBUG
1717

1818
if "INSTANA_AGENT_IP" in os.environ:
19+
# Deprecated: INSTANA_AGENT_IP environment variable
20+
# To be removed in a future version
1921
self.agent_host = os.environ["INSTANA_AGENT_IP"]
2022

23+
if "INSTANA_AGENT_HOST" in os.environ:
24+
self.agent_host = os.environ["INSTANA_AGENT_HOST"]
25+
2126
if "INSTANA_AGENT_PORT" in os.environ:
2227
self.agent_port = os.environ["INSTANA_AGENT_PORT"]
2328

instana/recorder.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import opentracing.ext.tags as ext
88
from basictracer import Sampler, SpanRecorder
9-
from .span import CustomData, Data, HttpData, InstanaSpan, SDKData
9+
from .json_span import CustomData, Data, HttpData, JsonSpan, SDKData
1010
from .agent_const import AGENT_TRACES_URL
1111

1212
import sys
@@ -65,21 +65,21 @@ def clear_spans(self):
6565

6666
def record_span(self, span):
6767
"""
68-
Convert the passed BasicSpan into an InstanaSpan and
68+
Convert the passed BasicSpan into an JsonSpan and
6969
add it to the span queue
7070
"""
7171
if self.sensor.agent.can_send() or "INSTANA_TEST" in os.environ:
72-
instana_span = None
72+
json_span = None
7373

7474
if span.operation_name in self.registered_spans:
75-
instana_span = self.build_registered_span(span)
75+
json_span = self.build_registered_span(span)
7676
else:
77-
instana_span = self.build_sdk_span(span)
77+
json_span = self.build_sdk_span(span)
7878

79-
self.queue.put(instana_span)
79+
self.queue.put(json_span)
8080

8181
def build_registered_span(self, span):
82-
""" Takes a BasicSpan and converts it into a registered InstanaSpan """
82+
""" Takes a BasicSpan and converts it into a registered JsonSpan """
8383
data = Data(http=HttpData(host=self.get_host_name(span),
8484
url=self.get_string_tag(span, ext.HTTP_URL),
8585
method=self.get_string_tag(span, ext.HTTP_METHOD),
@@ -90,7 +90,7 @@ def build_registered_span(self, span):
9090
entityFrom = {'e': self.sensor.agent.from_.pid,
9191
'h': self.sensor.agent.from_.agentUuid}
9292

93-
return InstanaSpan(
93+
return JsonSpan(
9494
n=span.operation_name,
9595
t=span.context.trace_id,
9696
p=span.parent_id,
@@ -103,7 +103,7 @@ def build_registered_span(self, span):
103103
data=data)
104104

105105
def build_sdk_span(self, span):
106-
""" Takes a BasicSpan and converts into an SDK type InstanaSpan """
106+
""" Takes a BasicSpan and converts into an SDK type JsonSpan """
107107

108108
custom_data = CustomData(tags=span.tags,
109109
logs=self.collect_logs(span))
@@ -116,7 +116,7 @@ def build_sdk_span(self, span):
116116
entityFrom = {'e': self.sensor.agent.from_.pid,
117117
'h': self.sensor.agent.from_.agentUuid}
118118

119-
return InstanaSpan(
119+
return JsonSpan(
120120
t=span.context.trace_id,
121121
p=span.parent_id,
122122
s=span.context.span_id,

0 commit comments

Comments
 (0)