Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ python:
- "3.4"
- "3.5"
- "3.6"
install: "pip install -r test_requirements.txt"
install: "pip install -r requirements-test.txt"
script: nosetests -v
1 change: 1 addition & 0 deletions instana/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# Import & initialize instrumentation
# noqa: ignore=W0611
from .instrumentation import urllib3 # noqa
from .instrumentation import sudsjurko # noqa

"""
The Instana package has two core components: the sensor and the tracer.
Expand Down
48 changes: 48 additions & 0 deletions instana/instrumentation/sudsjurko.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from __future__ import absolute_import
import instana
from instana.log import logger
import opentracing
import opentracing.ext.tags as ext
import wrapt


try:
import suds # noqa

if (suds.version.__version__ <= '0.6'):
class_method = 'SoapClient.send'
else:
class_method = '_SoapClient.send'

@wrapt.patch_function_wrapper('suds.client', class_method)
def send_with_instana(wrapped, instance, args, kwargs):
context = instana.internal_tracer.current_context()

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

try:
span = instana.internal_tracer.start_span("soap", child_of=context)
span.set_tag('soap.action', instance.method.name)
span.set_tag(ext.HTTP_URL, instance.method.location)
span.set_tag(ext.HTTP_METHOD, 'POST')

instana.internal_tracer.inject(span.context, opentracing.Format.HTTP_HEADERS,
instance.options.headers)

rv = wrapped(*args, **kwargs)

except Exception as e:
span.log_exception(e)
span.set_tag(ext.HTTP_STATUS_CODE, 500)
raise
else:
span.set_tag(ext.HTTP_STATUS_CODE, 200)
return rv
finally:
span.finish()

logger.debug("Instrumenting suds-jurko")
except ImportError:
pass
7 changes: 7 additions & 0 deletions instana/json_span.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Data(object):
baggage = None
custom = None
sdk = None
soap = None

def __init__(self, **kwds):
self.__dict__.update(kwds)
Expand All @@ -36,6 +37,12 @@ class HttpData(object):
def __init__(self, **kwds):
self.__dict__.update(kwds)

class SoapData(object):
action = None

def __init__(self, **kwds):
self.__dict__.update(kwds)


class CustomData(object):
tags = None
Expand Down
17 changes: 7 additions & 10 deletions instana/meter.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,10 @@ def collect_snapshot(self):
s = Snapshot(name=appname, version=sys.version)
s.version = sys.version
s.versions = self.collect_modules()

return s
except Exception as e:
log.debug("collect_snapshot: ", str(e))

return None
log.debug(e.message)
else:
return s

def jsonable(self, value):
try:
Expand All @@ -174,8 +172,8 @@ def jsonable(self, value):

def collect_modules(self):
try:
m = sys.modules
r = {}
m = sys.modules
for k in m:
# Don't report submodules (e.g. django.x, django.y, django.z)
if ('.' in k):
Expand All @@ -193,11 +191,10 @@ def collect_modules(self):
r[k] = "unknown"
log.debug("collect_modules: could not process module ", k, str(e))

return r
except Exception as e:
log.debug("collect_modules: ", str(e))

return None
log.debug(e.message)
else:
return r

def collect_metrics(self):
u = resource.getrusage(resource.RUSAGE_SELF)
Expand Down
11 changes: 8 additions & 3 deletions instana/recorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import opentracing.ext.tags as ext
from basictracer import Sampler, SpanRecorder
from .json_span import CustomData, Data, HttpData, JsonSpan, SDKData
from .json_span import CustomData, Data, HttpData, SoapData, JsonSpan, SDKData
from .agent_const import AGENT_TRACES_URL

import sys
Expand All @@ -18,9 +18,10 @@

class InstanaRecorder(SpanRecorder):
sensor = None
registered_spans = ("django", "memcache", "rpc-client", "rpc-server", "urllib3", "wsgi")
registered_spans = ("django", "memcache", "rpc-client", "rpc-server",
"soap", "urllib3", "wsgi")
entry_kind = ["entry", "server", "consumer"]
exit_kind = ["exit", "client", "producer"]
exit_kind = ["exit", "client", "producer", "soap"]
queue = queue.Queue()

def __init__(self, sensor):
Expand Down Expand Up @@ -84,9 +85,11 @@ def build_registered_span(self, span):
url=self.get_string_tag(span, ext.HTTP_URL),
method=self.get_string_tag(span, ext.HTTP_METHOD),
status=self.get_tag(span, ext.HTTP_STATUS_CODE)),
soap=SoapData(action=self.get_tag(span, 'soap.action')),
baggage=span.context.baggage,
custom=CustomData(tags=span.tags,
logs=self.collect_logs(span)))

entityFrom = {'e': self.sensor.agent.from_.pid,
'h': self.sensor.agent.from_.agentUuid}

Expand Down Expand Up @@ -124,6 +127,8 @@ def build_sdk_span(self, span):
d=int(round(span.duration * 1000)),
n="sdk",
f=entityFrom,
# ec=self.get_tag(span, "ec"),
# error=self.get_tag(span, "error"),
data=data)

def get_tag(self, span, tag):
Expand Down
12 changes: 12 additions & 0 deletions instana/span.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,15 @@ def finish(self, finish_time=None):
sampled=True)
self.tracer.cur_ctx = pctx
super(InstanaSpan, self).finish(finish_time)

def log_exception(self, e):
if hasattr(e, 'message'):
self.log_kv({'message': e.message})
elif hasattr(e, '__str__'):
self.log_kv({'message': e.__str__()})
else:
self.log_kv({'message': str(e)})

self.set_tag("error", True)
ec = self.tags.get('ec', 0)
self.set_tag("ec", ec+1)
2 changes: 2 additions & 0 deletions requirements-test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# See setup.py for dependencies
-e .[test]
6 changes: 2 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
fysom>=2.1.2
opentracing>=1.2.1
basictracer>=2.2.0
autowrapt>=1.0
# See setup.py for dependencies
-e .
53 changes: 31 additions & 22 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
from setuptools import setup, find_packages

setup(name='instana',
version='0.7.12',
download_url='https://github.com/instana/python-sensor',
url='https://www.instana.com/',
license='MIT',
author='Instana Inc.',
author_email='peter.lombardo@instana.com',
description='Metrics sensor and trace collector for Instana',
packages=find_packages(exclude=['tests', 'examples']),
long_description="The instana package provides Python metrics and traces for Instana.",
zip_safe=False,
setup_requires=['nose>=1.0', 'flask>=0.12.2'],
install_requires=['autowrapt>=1.0',
'fysom>=2.1.2',
'opentracing>=1.2.1,<1.3',
'basictracer>=2.2.0'],
entry_points={'django': ['django.core.handlers.base = instana.django:hook'],
'django19': ['django.core.handlers.base = instana.django:hook19'],
'flask': ['flask = instana.flaskana:hook'],
'runtime': ['string = instana.runtime:hook']},
test_suite='nose.collector',
keywords=['performance', 'opentracing', 'metrics', 'monitoring'],
classifiers=[
version='0.7.12',
download_url='https://github.com/instana/python-sensor',
url='https://www.instana.com/',
license='MIT',
author='Instana Inc.',
author_email='peter.lombardo@instana.com',
description='Metrics sensor and trace collector for Instana',
packages=find_packages(exclude=['tests', 'examples']),
long_description="The instana package provides Python metrics and traces for Instana.",
zip_safe=False,
install_requires=['autowrapt>=1.0',
'fysom>=2.1.2',
'opentracing>=1.2.1,<1.3',
'basictracer>=2.2.0'],
entry_points={'django': ['django.core.handlers.base = instana.django:hook'],
'django19': ['django.core.handlers.base = instana.django:hook19'],
'flask': ['flask = instana.flaskana:hook'],
'runtime': ['string = instana.runtime:hook']},
extras_require={
'test': [
'nose>=1.0',
'flask>=0.12.2',
'requests>=2.17.1',
'spyne>=2.9',
'lxml>=3.4',
'suds-jurko>=0.6'
],
},
test_suite='nose.collector',
keywords=['performance', 'opentracing', 'metrics', 'monitoring'],
classifiers=[
'Development Status :: 5 - Production/Stable',
'Framework :: Django',
'Framework :: Flask',
Expand Down
8 changes: 0 additions & 8 deletions test_requirements.txt

This file was deleted.

23 changes: 21 additions & 2 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,33 @@
import time
import threading
from .apps.flaskalino import app as flaskalino
from .apps.soapserver4132 import soapserver

os.environ["INSTANA_TEST"] = "true"


# Background Flask application
#
# Spawn our background Flask app that the tests will throw
# requests at. Don't continue until the test app is fully
# up and running.
timer = threading.Thread(target=flaskalino.run)
timer.daemon = True
timer.name = "Test Flask app"
print("Starting background test app")
timer.name = "Background Flask app"
print("Starting background Flask app...")
timer.start()


# Background Soap Server
#
# Spawn our background Flask app that the tests will throw
# requests at. Don't continue until the test app is fully
# up and running.
timer = threading.Thread(target=soapserver.serve_forever)
timer.daemon = True
timer.name = "Background Soap server"
print("Starting background Soap server...")
timer.start()


time.sleep(1)
55 changes: 55 additions & 0 deletions tests/apps/soapserver4132.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# vim: set fileencoding=UTF-8 :
import logging
from spyne import Application, rpc, ServiceBase, Iterable, Integer, Unicode, Fault
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
from wsgiref.simple_server import make_server
from instana.wsgi import iWSGIMiddleware

# Simple in test suite SOAP server to test suds client instrumentation against.
# Configured to listen on localhost port 4132
# WSDL: http://localhost:4232/?wsdl

class StanSoapService(ServiceBase):
@rpc(Unicode, Integer, _returns=Iterable(Unicode))
def ask_question(ctx, question, answer):
"""Ask Stan a question!
<b>Ask Stan questions as a Service</b>

@param name the name to say hello to
@param times the number of times to say hello
@return the completed array
"""

yield u'To an artificial mind, all reality is virtual. How do they know that the real world isn\'t just another simulation? How do you?'


@rpc()
def server_exception(ctx):
raise Exception("Server side exception example.")

@rpc()
def server_fault(ctx):
raise Fault("Server", "Server side fault example.")

@rpc()
def client_fault(ctx):
raise Fault("Client", "Client side fault example")



app = Application([StanSoapService], 'instana.tests.app.ask_question',
in_protocol=Soap11(validator='lxml'), out_protocol=Soap11())

# Use Instana middleware so we can test context passing and Soap server traces.
wsgi_app = iWSGIMiddleware(WsgiApplication(app))
soapserver = make_server('127.0.0.1', 4132, wsgi_app)

logging.basicConfig(level=logging.WARN)
logging.getLogger('suds').setLevel(logging.WARN)
logging.getLogger('suds.resolver').setLevel(logging.WARN)
logging.getLogger('spyne.protocol.xml').setLevel(logging.WARN)
logging.getLogger('spyne.model.complex').setLevel(logging.WARN)

if __name__ == '__main__':
soapserver.serve_forever()
Loading