Skip to content

Commit abac9cb

Browse files
authored
suds-jurko instrumentation (#75)
* Add background Soap server to test suite. * Better exception capture & logging in metric thread * Initial suds-jurko instrumentation * Basic Soap request tests. * Add SoapData section; Add soap as registered span * Cleanup and organize package dependencies * New test dependencies for Travis * Version sensitive class & method * Add requests to test bundle * Remove unicode characters; silence spyne logging * Exception & fault logging plus tests * Move exception logging out to span * Python 2 <-> 3 compatibility change * Moar HTTP tags. * Remove debug remnant and fix logger call
1 parent d64d14d commit abac9cb

File tree

14 files changed

+407
-50
lines changed

14 files changed

+407
-50
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ python:
55
- "3.4"
66
- "3.5"
77
- "3.6"
8-
install: "pip install -r test_requirements.txt"
8+
install: "pip install -r requirements-test.txt"
99
script: nosetests -v

instana/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
# Import & initialize instrumentation
1010
# noqa: ignore=W0611
1111
from .instrumentation import urllib3 # noqa
12+
from .instrumentation import sudsjurko # noqa
1213

1314
"""
1415
The Instana package has two core components: the sensor and the tracer.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from __future__ import absolute_import
2+
import instana
3+
from instana.log import logger
4+
import opentracing
5+
import opentracing.ext.tags as ext
6+
import wrapt
7+
8+
9+
try:
10+
import suds # noqa
11+
12+
if (suds.version.__version__ <= '0.6'):
13+
class_method = 'SoapClient.send'
14+
else:
15+
class_method = '_SoapClient.send'
16+
17+
@wrapt.patch_function_wrapper('suds.client', class_method)
18+
def send_with_instana(wrapped, instance, args, kwargs):
19+
context = instana.internal_tracer.current_context()
20+
21+
# If we're not tracing, just return
22+
if context is None:
23+
return wrapped(*args, **kwargs)
24+
25+
try:
26+
span = instana.internal_tracer.start_span("soap", child_of=context)
27+
span.set_tag('soap.action', instance.method.name)
28+
span.set_tag(ext.HTTP_URL, instance.method.location)
29+
span.set_tag(ext.HTTP_METHOD, 'POST')
30+
31+
instana.internal_tracer.inject(span.context, opentracing.Format.HTTP_HEADERS,
32+
instance.options.headers)
33+
34+
rv = wrapped(*args, **kwargs)
35+
36+
except Exception as e:
37+
span.log_exception(e)
38+
span.set_tag(ext.HTTP_STATUS_CODE, 500)
39+
raise
40+
else:
41+
span.set_tag(ext.HTTP_STATUS_CODE, 200)
42+
return rv
43+
finally:
44+
span.finish()
45+
46+
logger.debug("Instrumenting suds-jurko")
47+
except ImportError:
48+
pass

instana/json_span.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class Data(object):
2222
baggage = None
2323
custom = None
2424
sdk = None
25+
soap = None
2526

2627
def __init__(self, **kwds):
2728
self.__dict__.update(kwds)
@@ -36,6 +37,12 @@ class HttpData(object):
3637
def __init__(self, **kwds):
3738
self.__dict__.update(kwds)
3839

40+
class SoapData(object):
41+
action = None
42+
43+
def __init__(self, **kwds):
44+
self.__dict__.update(kwds)
45+
3946

4047
class CustomData(object):
4148
tags = None

instana/meter.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -153,12 +153,10 @@ def collect_snapshot(self):
153153
s = Snapshot(name=appname, version=sys.version)
154154
s.version = sys.version
155155
s.versions = self.collect_modules()
156-
157-
return s
158156
except Exception as e:
159-
log.debug("collect_snapshot: ", str(e))
160-
161-
return None
157+
log.debug(e.message)
158+
else:
159+
return s
162160

163161
def jsonable(self, value):
164162
try:
@@ -174,8 +172,8 @@ def jsonable(self, value):
174172

175173
def collect_modules(self):
176174
try:
177-
m = sys.modules
178175
r = {}
176+
m = sys.modules
179177
for k in m:
180178
# Don't report submodules (e.g. django.x, django.y, django.z)
181179
if ('.' in k):
@@ -193,11 +191,10 @@ def collect_modules(self):
193191
r[k] = "unknown"
194192
log.debug("collect_modules: could not process module ", k, str(e))
195193

196-
return r
197194
except Exception as e:
198-
log.debug("collect_modules: ", str(e))
199-
200-
return None
195+
log.debug(e.message)
196+
else:
197+
return r
201198

202199
def collect_metrics(self):
203200
u = resource.getrusage(resource.RUSAGE_SELF)

instana/recorder.py

Lines changed: 8 additions & 3 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 .json_span import CustomData, Data, HttpData, JsonSpan, SDKData
9+
from .json_span import CustomData, Data, HttpData, SoapData, JsonSpan, SDKData
1010
from .agent_const import AGENT_TRACES_URL
1111

1212
import sys
@@ -18,9 +18,10 @@
1818

1919
class InstanaRecorder(SpanRecorder):
2020
sensor = None
21-
registered_spans = ("django", "memcache", "rpc-client", "rpc-server", "urllib3", "wsgi")
21+
registered_spans = ("django", "memcache", "rpc-client", "rpc-server",
22+
"soap", "urllib3", "wsgi")
2223
entry_kind = ["entry", "server", "consumer"]
23-
exit_kind = ["exit", "client", "producer"]
24+
exit_kind = ["exit", "client", "producer", "soap"]
2425
queue = queue.Queue()
2526

2627
def __init__(self, sensor):
@@ -84,9 +85,11 @@ def build_registered_span(self, span):
8485
url=self.get_string_tag(span, ext.HTTP_URL),
8586
method=self.get_string_tag(span, ext.HTTP_METHOD),
8687
status=self.get_tag(span, ext.HTTP_STATUS_CODE)),
88+
soap=SoapData(action=self.get_tag(span, 'soap.action')),
8789
baggage=span.context.baggage,
8890
custom=CustomData(tags=span.tags,
8991
logs=self.collect_logs(span)))
92+
9093
entityFrom = {'e': self.sensor.agent.from_.pid,
9194
'h': self.sensor.agent.from_.agentUuid}
9295

@@ -124,6 +127,8 @@ def build_sdk_span(self, span):
124127
d=int(round(span.duration * 1000)),
125128
n="sdk",
126129
f=entityFrom,
130+
# ec=self.get_tag(span, "ec"),
131+
# error=self.get_tag(span, "error"),
127132
data=data)
128133

129134
def get_tag(self, span, tag):

instana/span.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,15 @@ def finish(self, finish_time=None):
1414
sampled=True)
1515
self.tracer.cur_ctx = pctx
1616
super(InstanaSpan, self).finish(finish_time)
17+
18+
def log_exception(self, e):
19+
if hasattr(e, 'message'):
20+
self.log_kv({'message': e.message})
21+
elif hasattr(e, '__str__'):
22+
self.log_kv({'message': e.__str__()})
23+
else:
24+
self.log_kv({'message': str(e)})
25+
26+
self.set_tag("error", True)
27+
ec = self.tags.get('ec', 0)
28+
self.set_tag("ec", ec+1)

requirements-test.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# See setup.py for dependencies
2+
-e .[test]

requirements.txt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
1-
fysom>=2.1.2
2-
opentracing>=1.2.1
3-
basictracer>=2.2.0
4-
autowrapt>=1.0
1+
# See setup.py for dependencies
2+
-e .

setup.py

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,37 @@
11
from setuptools import setup, find_packages
22

33
setup(name='instana',
4-
version='0.7.12',
5-
download_url='https://github.com/instana/python-sensor',
6-
url='https://www.instana.com/',
7-
license='MIT',
8-
author='Instana Inc.',
9-
author_email='[email protected]',
10-
description='Metrics sensor and trace collector for Instana',
11-
packages=find_packages(exclude=['tests', 'examples']),
12-
long_description="The instana package provides Python metrics and traces for Instana.",
13-
zip_safe=False,
14-
setup_requires=['nose>=1.0', 'flask>=0.12.2'],
15-
install_requires=['autowrapt>=1.0',
16-
'fysom>=2.1.2',
17-
'opentracing>=1.2.1,<1.3',
18-
'basictracer>=2.2.0'],
19-
entry_points={'django': ['django.core.handlers.base = instana.django:hook'],
20-
'django19': ['django.core.handlers.base = instana.django:hook19'],
21-
'flask': ['flask = instana.flaskana:hook'],
22-
'runtime': ['string = instana.runtime:hook']},
23-
test_suite='nose.collector',
24-
keywords=['performance', 'opentracing', 'metrics', 'monitoring'],
25-
classifiers=[
4+
version='0.7.12',
5+
download_url='https://github.com/instana/python-sensor',
6+
url='https://www.instana.com/',
7+
license='MIT',
8+
author='Instana Inc.',
9+
author_email='[email protected]',
10+
description='Metrics sensor and trace collector for Instana',
11+
packages=find_packages(exclude=['tests', 'examples']),
12+
long_description="The instana package provides Python metrics and traces for Instana.",
13+
zip_safe=False,
14+
install_requires=['autowrapt>=1.0',
15+
'fysom>=2.1.2',
16+
'opentracing>=1.2.1,<1.3',
17+
'basictracer>=2.2.0'],
18+
entry_points={'django': ['django.core.handlers.base = instana.django:hook'],
19+
'django19': ['django.core.handlers.base = instana.django:hook19'],
20+
'flask': ['flask = instana.flaskana:hook'],
21+
'runtime': ['string = instana.runtime:hook']},
22+
extras_require={
23+
'test': [
24+
'nose>=1.0',
25+
'flask>=0.12.2',
26+
'requests>=2.17.1',
27+
'spyne>=2.9',
28+
'lxml>=3.4',
29+
'suds-jurko>=0.6'
30+
],
31+
},
32+
test_suite='nose.collector',
33+
keywords=['performance', 'opentracing', 'metrics', 'monitoring'],
34+
classifiers=[
2635
'Development Status :: 5 - Production/Stable',
2736
'Framework :: Django',
2837
'Framework :: Flask',

0 commit comments

Comments
 (0)