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 ddtrace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .tracer import Tracer
from .settings import config

__version__ = '0.17.0'
__version__ = '0.17.1'

# a global tracer instance with integration settings
tracer = Tracer()
Expand Down
6 changes: 4 additions & 2 deletions ddtrace/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@
'compatibility_mode': False,
'fallback': 'v0.3'},
'v0.3': {'traces': '/v0.3/traces',
'services': '/v0.3/services',
'services': '/v0.3/services',
'compatibility_mode': False,
'fallback': 'v0.2'},
'v0.2': {'traces': '/v0.2/traces',
'services': '/v0.2/services',
'services': '/v0.2/services',
'compatibility_mode': True,
'fallback': None}}


def _parse_response_json(response):
"""
Parse the content of a response object, and return the right type,
Expand All @@ -48,6 +49,7 @@ def _parse_response_json(response):
except (ValueError, TypeError) as err:
log.debug("unable to load JSON '%s': %s" % (body, err))


class API(object):
"""
Send data to the trace agent using the HTTP protocol and JSON format
Expand Down
4 changes: 2 additions & 2 deletions ddtrace/commands/ddtrace_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
DATADOG_PRIORITY_SAMPLING=true|false : (default: false): enables Priority Sampling.
""" # noqa


def _ddtrace_root():
from ddtrace import __file__
return os.path.dirname(__file__)
Expand All @@ -46,8 +47,7 @@ def _add_bootstrap_to_pythonpath(bootstrap_dir):
python_path = os.environ.get('PYTHONPATH', '')

if python_path:
new_path = "%s%s%s" % (bootstrap_dir, os.path.pathsep,
os.environ['PYTHONPATH'])
new_path = "%s%s%s" % (bootstrap_dir, os.path.pathsep, os.environ['PYTHONPATH'])
os.environ['PYTHONPATH'] = new_path
else:
os.environ['PYTHONPATH'] = bootstrap_dir
Expand Down
3 changes: 2 additions & 1 deletion ddtrace/contrib/bottle/patch.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import os

from .trace import TracePlugin
Expand All @@ -7,6 +6,7 @@

import wrapt


def patch():
"""Patch the bottle.Bottle class
"""
Expand All @@ -16,6 +16,7 @@ def patch():
setattr(bottle, '_datadog_patch', True)
wrapt.wrap_function_wrapper('bottle', 'Bottle.__init__', traced_init)


def traced_init(wrapped, instance, args, kwargs):
wrapped(*args, **kwargs)

Expand Down
1 change: 1 addition & 0 deletions ddtrace/contrib/bottle/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

SPAN_TYPE = 'web'


class TracePlugin(object):
name = 'trace'
api = 2
Expand Down
20 changes: 17 additions & 3 deletions ddtrace/contrib/cassandra/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,26 @@
# Original connect connect function
_connect = cassandra.cluster.Cluster.connect


def patch():
""" patch will add tracing to the cassandra library. """
setattr(cassandra.cluster.Cluster, 'connect',
wrapt.FunctionWrapper(_connect, traced_connect))
Pin(service=SERVICE, app=SERVICE, app_type="db").onto(cassandra.cluster.Cluster)


def unpatch():
cassandra.cluster.Cluster.connect = _connect


def traced_connect(func, instance, args, kwargs):
session = func(*args, **kwargs)
if not isinstance(session.execute, wrapt.FunctionWrapper):
# FIXME[matt] this should probably be private.
setattr(session, 'execute_async', wrapt.FunctionWrapper(session.execute_async, traced_execute_async))
return session


def _close_span_on_success(result, future):
span = getattr(future, CURRENT_SPAN, None)
if not span:
Expand All @@ -54,11 +58,13 @@ def _close_span_on_success(result, future):
span.finish()
delattr(future, CURRENT_SPAN)


def traced_set_final_result(func, instance, args, kwargs):
result = args[0]
_close_span_on_success(result, instance)
return func(*args, **kwargs)


def _close_span_on_error(exc, future):
span = getattr(future, CURRENT_SPAN, None)
if not span:
Expand All @@ -76,11 +82,13 @@ def _close_span_on_error(exc, future):
span.finish()
delattr(future, CURRENT_SPAN)


def traced_set_final_exception(func, instance, args, kwargs):
exc = args[0]
_close_span_on_error(exc, instance)
return func(*args, **kwargs)


def traced_start_fetching_next_page(func, instance, args, kwargs):
has_more_pages = getattr(instance, 'has_more_pages', True)
if not has_more_pages:
Expand All @@ -106,11 +114,12 @@ def traced_start_fetching_next_page(func, instance, args, kwargs):
setattr(instance, CURRENT_SPAN, span)
try:
return func(*args, **kwargs)
except:
except Exception:
with span:
span.set_exc_info(*sys.exc_info())
raise


def traced_execute_async(func, instance, args, kwargs):
cluster = getattr(instance, 'cluster', None)
pin = Pin.get_from(cluster)
Expand Down Expand Up @@ -161,11 +170,12 @@ def traced_execute_async(func, instance, args, kwargs):
)
result.clear_callbacks()
return result
except:
except Exception:
with span:
span.set_exc_info(*sys.exc_info())
raise


def _start_span_and_set_tags(pin, query, session, cluster):
service = pin.service
tracer = pin.tracer
Expand All @@ -175,6 +185,7 @@ def _start_span_and_set_tags(pin, query, session, cluster):
span.set_tags(_extract_cluster_metas(cluster))
return span


def _extract_session_metas(session):
metas = {}

Expand All @@ -185,6 +196,7 @@ def _extract_session_metas(session):

return metas


def _extract_cluster_metas(cluster):
metas = {}
if deep_getattr(cluster, "metadata.cluster_name"):
Expand All @@ -194,6 +206,7 @@ def _extract_cluster_metas(cluster):

return metas


def _extract_result_metas(result):
metas = {}
if result is None:
Expand Down Expand Up @@ -230,6 +243,7 @@ def _extract_result_metas(result):

return metas


def _sanitize_query(span, query):
# TODO (aaditya): fix this hacky type check. we need it to avoid circular imports
t = type(query).__name__
Expand All @@ -250,7 +264,7 @@ def _sanitize_query(span, query):
elif t == 'str':
resource = query
else:
resource = 'unknown-query-type' # FIXME[matt] what else do to here?
resource = 'unknown-query-type' # FIXME[matt] what else do to here?

span.resource = stringify(resource)[:RESOURCE_MAX_LENGTH]

Expand Down
2 changes: 1 addition & 1 deletion ddtrace/contrib/celery/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
retrieve_span,
)


log = logging.getLogger(__name__)
SPAN_TYPE = 'worker'


def trace_prerun(*args, **kwargs):
# safe-guard to avoid crashes in case the signals API
# changes in Celery
Expand Down
1 change: 1 addition & 0 deletions ddtrace/contrib/celery/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def patch_task(task, pin=None):
patch_app(task.app)
return task


def unpatch_task(task):
"""Deprecated API. The new API uses signals that can be deactivated
via unpatch() API. This API is now a no-op implementation so it doesn't
Expand Down
1 change: 1 addition & 0 deletions ddtrace/contrib/dbapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ def rollback(self, *args, **kwargs):
span_name = '{}.{}'.format(self._self_datadog_name, 'rollback')
return self._trace_method(self.__wrapped__.rollback, span_name, {}, *args, **kwargs)


def _get_vendor(conn):
""" Return the vendor (e.g postgres, mysql) of the given
database.
Expand Down
5 changes: 3 additions & 2 deletions ddtrace/contrib/django/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ def _trace_operation(fn, method_name):
def wrapped(self, *args, **kwargs):
# get the original function method
method = getattr(self, DATADOG_NAMESPACE.format(method=method_name))
with tracer.trace('django.cache',
span_type=TYPE, service=cache_service_name) as span:
with tracer.trace('django.cache', span_type=TYPE, service=cache_service_name) as span:
# update the resource name and tag the cache backend
span.resource = _resource_from_cache_prefix(method_name, self)
cache_backend = '{}.{}'.format(self.__module__, self.__class__.__name__)
Expand Down Expand Up @@ -93,6 +92,7 @@ def _wrap_method(cls, method_name):
for method in TRACED_METHODS:
_wrap_method(cache, method)


def unpatch_method(cls, method_name):
method = getattr(cls, DATADOG_NAMESPACE.format(method=method_name), None)
if method is None:
Expand All @@ -101,6 +101,7 @@ def unpatch_method(cls, method_name):
setattr(cls, method_name, method)
delattr(cls, DATADOG_NAMESPACE.format(method=method_name))


def unpatch_cache():
cache_backends = set([cache['BACKEND'] for cache in django_settings.CACHES.values()])
for cache_module in cache_backends:
Expand Down
6 changes: 4 additions & 2 deletions ddtrace/contrib/django/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@ def import_from_string(val, setting_name):
return getattr(module, class_name)
except (ImportError, AttributeError) as e:
msg = 'Could not import "{}" for setting "{}". {}: {}.'.format(
val, setting_name,
e.__class__.__name__, e
val,
setting_name,
e.__class__.__name__,
e,
)

raise ImportError(msg)
Expand Down
9 changes: 8 additions & 1 deletion ddtrace/contrib/django/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,26 +47,31 @@ def get_middleware_insertion_point():
return MIDDLEWARE, middleware
return MIDDLEWARE_CLASSES, getattr(django_settings, MIDDLEWARE_CLASSES, None)


def insert_trace_middleware():
middleware_attribute, middleware = get_middleware_insertion_point()
if middleware is not None and TRACE_MIDDLEWARE not in set(middleware):
setattr(django_settings, middleware_attribute, type(middleware)((TRACE_MIDDLEWARE,)) + middleware)


def remove_trace_middleware():
_, middleware = get_middleware_insertion_point()
if middleware and TRACE_MIDDLEWARE in set(middleware):
middleware.remove(TRACE_MIDDLEWARE)


def insert_exception_middleware():
middleware_attribute, middleware = get_middleware_insertion_point()
if middleware is not None and EXCEPTION_MIDDLEWARE not in set(middleware):
setattr(django_settings, middleware_attribute, middleware + type(middleware)((EXCEPTION_MIDDLEWARE,)))


def remove_exception_middleware():
_, middleware = get_middleware_insertion_point()
if middleware and EXCEPTION_MIDDLEWARE in set(middleware):
middleware.remove(EXCEPTION_MIDDLEWARE)


class InstrumentationMixin(MiddlewareClass):
"""
Useful mixin base class for tracing middlewares
Expand All @@ -88,7 +93,7 @@ def process_exception(self, request, exception):
span = _get_req_span(request)
if span:
span.set_tag(http.STATUS_CODE, '500')
span.set_traceback() # will set the exception info
span.set_traceback() # will set the exception info
except Exception:
log.debug("error processing exception", exc_info=True)

Expand Down Expand Up @@ -172,10 +177,12 @@ def _get_req_span(request):
""" Return the datadog span from the given request. """
return getattr(request, '_datadog_request_span', None)


def _set_req_span(request, span):
""" Set the datadog span on the given request. """
return setattr(request, '_datadog_request_span', span)


def _set_auth_tags(span, request):
""" Patch any available auth tags from the request onto the span. """
user = getattr(request, 'user', None)
Expand Down
2 changes: 2 additions & 0 deletions ddtrace/contrib/django/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import django


def patch():
"""Patch the instrumented methods
"""
Expand All @@ -12,6 +13,7 @@ def patch():
_w = wrapt.wrap_function_wrapper
_w('django', 'setup', traced_setup)


def traced_setup(wrapped, instance, args, kwargs):
from django.conf import settings

Expand Down
3 changes: 2 additions & 1 deletion ddtrace/contrib/django/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
# 3p
from django.template import Template


log = logging.getLogger(__name__)

RENDER_ATTR = '_datadog_original_render'


def patch_template(tracer):
""" will patch django's template rendering function to include timing
and trace information.
Expand All @@ -42,6 +42,7 @@ def traced_render(self, context):

Template.render = traced_render


def unpatch_template():
render = getattr(Template, RENDER_ATTR, None)
if render is None:
Expand Down
1 change: 1 addition & 0 deletions ddtrace/contrib/elasticsearch/quantize.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
INDEX_REGEXP = re.compile(r'[0-9]{2,}')
INDEX_PLACEHOLDER = r'?'


def quantize(span):
"""Quantize an elasticsearch span

Expand Down
1 change: 1 addition & 0 deletions ddtrace/contrib/falcon/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def patch():
setattr(falcon, '_datadog_patch', True)
wrapt.wrap_function_wrapper('falcon', 'API.__init__', traced_init)


def traced_init(wrapped, instance, args, kwargs):
mw = kwargs.pop('middleware', [])
service = os.environ.get('DATADOG_SERVICE_NAME') or 'falcon'
Expand Down
2 changes: 2 additions & 0 deletions ddtrace/contrib/flask/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ def _finish_span(self, span, exception=None):
span.set_tag(http.METHOD, method)
span.finish()


def _set_error_on_span(span, exception):
# The 3 next lines might not be strictly required, since `set_traceback`
# also get the exception from the sys.exc_info (and fill the error meta).
Expand All @@ -188,6 +189,7 @@ def _set_error_on_span(span, exception):
# so attach the stack trace with `set_traceback`.
span.set_traceback()


def _patch_render(tracer):
""" patch flask's render template methods with the given tracer. """
# fall back to patching global method
Expand Down
Loading