Skip to content

Commit

Permalink
Merge pull request #783 from DataDog/0.19-dev
Browse files Browse the repository at this point in the history
0.19.0
  • Loading branch information
brettlangdon authored Dec 28, 2018
2 parents a0bdd48 + 6b1243e commit e8b1070
Show file tree
Hide file tree
Showing 51 changed files with 1,647 additions and 810 deletions.
25 changes: 24 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,25 @@ jobs:
- flake8.results
- *save_cache_step

# Test that we can build the package properly and package long description will render
test_build:
docker:
- *test_runner
resource_class: *resource_class
steps:
- checkout
- *restore_cache_step
# Install required dependencies
- run: pip install twine readme_renderer[md]
# Ensure we didn't cache from previous runs
- run: rm -rf dist/
# Ensure package will build
- run: python setup.py sdist
# Ensure package long description is valid and will render
# https://github.com/pypa/twine/tree/6c4d5ecf2596c72b89b969ccc37b82c160645df8#twine-check
- run: twine check dist/*
- *save_cache_step

tracer:
docker:
- *test_runner
Expand Down Expand Up @@ -931,11 +950,11 @@ workflows:
<<: *deploy_docs_filters
requires:
- approve_docs_deployment

test:
jobs:
- build_docs
- flake8
- test_build
- aiobotocore:
requires:
- flake8
Expand Down Expand Up @@ -1076,8 +1095,12 @@ workflows:
- flake8
- wait_all_tests:
requires:
# Initial jobs
- build_docs
- flake8
- test_build

# flake8 dependent jobs
- aiobotocore
- aiohttp
- aiopg
Expand Down
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.18.0'
__version__ = '0.19.0'

# a global tracer instance with integration settings
tracer = Tracer()
Expand Down
5 changes: 4 additions & 1 deletion ddtrace/constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
FILTERS_KEY = 'FILTERS'
SAMPLE_RATE_METRIC_KEY = "_sample_rate"
SAMPLE_RATE_METRIC_KEY = '_sample_rate'
SAMPLING_PRIORITY_KEY = '_sampling_priority_v1'
EVENT_SAMPLE_RATE_KEY = '_dd1.sr.eausr'

NUMERIC_TAGS = (EVENT_SAMPLE_RATE_KEY, )
9 changes: 7 additions & 2 deletions ddtrace/contrib/aiohttp/middlewares.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import asyncio

from ..asyncio import context_provider
from ...ext import AppTypes, http
from ...compat import stringify
from ...context import Context
from ...constants import EVENT_SAMPLE_RATE_KEY
from ...ext import AppTypes, http
from ...propagation.http import HTTPPropagator
from ...settings import config


CONFIG_KEY = 'datadog_trace'
Expand Down Expand Up @@ -45,6 +46,10 @@ def attach_context(request):
span_type=http.TYPE,
)

# Configure trace search sample rate
if config.aiohttp.event_sample_rate is not None:
request_span.set_tag(EVENT_SAMPLE_RATE_KEY, config.aiohttp.event_sample_rate)

# attach the context and the root span to the request; the Context
# may be freely used by the application code
request[REQUEST_CONTEXT_KEY] = request_span.context
Expand Down
6 changes: 6 additions & 0 deletions ddtrace/contrib/bottle/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
from ddtrace.ext import http, AppTypes

# project
from ...constants import EVENT_SAMPLE_RATE_KEY
from ...propagation.http import HTTPPropagator
from ...settings import config

SPAN_TYPE = 'web'

Expand Down Expand Up @@ -41,6 +43,10 @@ def wrapped(*args, **kwargs):
self.tracer.context_provider.activate(context)

with self.tracer.trace('bottle.request', service=self.service, resource=resource, span_type=SPAN_TYPE) as s:
# Configure trace search sample rate
if config.bottle.event_sample_rate is not None:
s.set_tag(EVENT_SAMPLE_RATE_KEY, config.bottle.event_sample_rate)

code = 0
try:
return callback(*args, **kwargs)
Expand Down
50 changes: 35 additions & 15 deletions ddtrace/contrib/dbapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@

from ddtrace import Pin
from ddtrace.ext import AppTypes, sql
from ddtrace.settings import config
from ddtrace.utils.formats import asbool, get_env

log = logging.getLogger(__name__)

config._add('dbapi2', dict(
trace_fetch_methods=asbool(get_env('dbapi2', 'trace_fetch_methods', 'false')),
))


class TracedCursor(wrapt.ObjectProxy):
""" TracedCursor wraps a psql cursor and traces it's queries. """
Expand Down Expand Up @@ -72,6 +78,27 @@ def execute(self, query, *args, **kwargs):
self._trace_method(self.__wrapped__.execute, self._self_datadog_name, query, {}, query, *args, **kwargs)
return self

def callproc(self, proc, args):
""" Wraps the cursor.callproc method"""
self._self_last_execute_operation = proc
return self._trace_method(self.__wrapped__.callproc, self._self_datadog_name, proc, {}, proc, args)

def __enter__(self):
# previous versions of the dbapi didn't support context managers. let's
# reference the func that would be called to ensure that errors
# messages will be the same.
self.__wrapped__.__enter__

# and finally, yield the traced cursor.
return self


class FetchTracedCursor(TracedCursor):
"""
Sub-class of :class:`TracedCursor` that also instruments `fetchone`, `fetchall`, and `fetchmany` methods.
We do not trace these functions by default since they can get very noisy (e.g. `fetchone` with 100k rows).
"""
def fetchone(self, *args, **kwargs):
""" Wraps the cursor.fetchone method"""
span_name = '{}.{}'.format(self._self_datadog_name, 'fetchone')
Expand Down Expand Up @@ -101,25 +128,18 @@ def fetchmany(self, *args, **kwargs):
return self._trace_method(self.__wrapped__.fetchmany, span_name, self._self_last_execute_operation, extra_tags,
*args, **kwargs)

def callproc(self, proc, args):
""" Wraps the cursor.callproc method"""
self._self_last_execute_operation = proc
return self._trace_method(self.__wrapped__.callproc, self._self_datadog_name, proc, {}, proc, args)

def __enter__(self):
# previous versions of the dbapi didn't support context managers. let's
# reference the func that would be called to ensure that errors
# messages will be the same.
self.__wrapped__.__enter__

# and finally, yield the traced cursor.
return self


class TracedConnection(wrapt.ObjectProxy):
""" TracedConnection wraps a Connection with tracing code. """

def __init__(self, conn, pin=None, cursor_cls=TracedCursor):
def __init__(self, conn, pin=None, cursor_cls=None):
# Set default cursor class if one was not provided
if not cursor_cls:
# Do not trace `fetch*` methods by default
cursor_cls = TracedCursor
if config.dbapi2.trace_fetch_methods:
cursor_cls = FetchTracedCursor

super(TracedConnection, self).__init__(conn)
name = _get_vendor(conn)
self._self_datadog_name = '{}.connection'.format(name)
Expand Down
8 changes: 7 additions & 1 deletion ddtrace/contrib/django/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
from .conf import settings
from .compat import user_is_authenticated, get_resolver

from ...ext import http
from ...constants import EVENT_SAMPLE_RATE_KEY
from ...contrib import func_name
from ...ext import http
from ...propagation.http import HTTPPropagator
from ...settings import config

# 3p
from django.core.exceptions import MiddlewareNotUsed
Expand Down Expand Up @@ -118,6 +120,10 @@ def process_request(self, request):
span_type=http.TYPE,
)

# Configure trace search sample rate
if config.django.event_sample_rate is not None:
span.set_tag(EVENT_SAMPLE_RATE_KEY, config.django.event_sample_rate)

span.set_tag(http.METHOD, request.method)
span.set_tag(http.URL, request.path)
_set_req_span(request, span)
Expand Down
6 changes: 6 additions & 0 deletions ddtrace/contrib/falcon/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
from ddtrace.ext import http as httpx
from ddtrace.http import store_request_headers, store_response_headers
from ddtrace.propagation.http import HTTPPropagator

from ...compat import iteritems
from ...constants import EVENT_SAMPLE_RATE_KEY
from ...ext import AppTypes
from ...settings import config

Expand Down Expand Up @@ -37,6 +39,10 @@ def process_request(self, req, resp):
span_type=httpx.TYPE,
)

# Configure trace search sample rate
if config.falcon.event_sample_rate is not None:
span.set_tag(EVENT_SAMPLE_RATE_KEY, config.falcon.event_sample_rate)

span.set_tag(httpx.METHOD, req.method)
span.set_tag(httpx.URL, req.url)

Expand Down
5 changes: 5 additions & 0 deletions ddtrace/contrib/flask/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from ddtrace import config, Pin

from ...constants import EVENT_SAMPLE_RATE_KEY
from ...ext import AppTypes
from ...ext import http
from ...propagation.http import HTTPPropagator
Expand Down Expand Up @@ -284,6 +285,10 @@ def traced_wsgi_app(pin, wrapped, instance, args, kwargs):
# We will override this below in `traced_dispatch_request` when we have a `RequestContext` and possibly a url rule
resource = u'{} {}'.format(request.method, request.path)
with pin.tracer.trace('flask.request', service=pin.service, resource=resource, span_type=http.TYPE) as s:
# Configure trace search sample rate
if config.flask.event_sample_rate is not None:
s.set_tag(EVENT_SAMPLE_RATE_KEY, config.flask.event_sample_rate)

s.set_tag(FLASK_VERSION, flask_version_str)

# Wrap the `start_response` handler to extract response code
Expand Down
5 changes: 5 additions & 0 deletions ddtrace/contrib/molten/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import molten

from ... import Pin, config
from ...constants import EVENT_SAMPLE_RATE_KEY
from ...ext import AppTypes, http
from ...propagation.http import HTTPPropagator
from ...utils.formats import asbool, get_env
Expand Down Expand Up @@ -83,6 +84,10 @@ def patch_app_call(wrapped, instance, args, kwargs):
pin.tracer.context_provider.activate(context)

with pin.tracer.trace('molten.request', service=pin.service, resource=resource) as span:
# Configure trace search sample rate
if config.molten.event_sample_rate is not None:
span.set_tag(EVENT_SAMPLE_RATE_KEY, config.molten.event_sample_rate)

@wrapt.function_wrapper
def _w_start_response(wrapped, instance, args, kwargs):
""" Patch respond handling to set metadata """
Expand Down
19 changes: 13 additions & 6 deletions ddtrace/contrib/psycopg/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import wrapt

# project
from ddtrace import Pin
from ddtrace import Pin, config
from ddtrace.contrib import dbapi
from ddtrace.ext import sql, net, db

Expand Down Expand Up @@ -51,14 +51,21 @@ def _trace_method(self, method, name, resource, extra_tags, *args, **kwargs):
return super(Psycopg2TracedCursor, self)._trace_method(method, name, resource, extra_tags, *args, **kwargs)


class Psycopg2FetchTracedCursor(Psycopg2TracedCursor, dbapi.FetchTracedCursor):
""" FetchTracedCursor for psycopg2 """


class Psycopg2TracedConnection(dbapi.TracedConnection):
""" TracedConnection wraps a Connection with tracing code. """

def __init__(self, conn, pin=None, cursor_cls=Psycopg2TracedCursor):
super(Psycopg2TracedConnection, self).__init__(conn, pin)
# wrapt requires prefix of `_self` for attributes that are only in the
# proxy (since some of our source objects will use `__slots__`)
self._self_cursor_cls = cursor_cls
def __init__(self, conn, pin=None, cursor_cls=None):
if not cursor_cls:
# Do not trace `fetch*` methods by default
cursor_cls = Psycopg2TracedCursor
if config.dbapi2.trace_fetch_methods:
cursor_cls = Psycopg2FetchTracedCursor

super(Psycopg2TracedConnection, self).__init__(conn, pin, cursor_cls=cursor_cls)


def patch_conn(conn, traced_conn_cls=Psycopg2TracedConnection):
Expand Down
7 changes: 7 additions & 0 deletions ddtrace/contrib/pylons/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
from .constants import CONFIG_MIDDLEWARE

from ...compat import reraise
from ...constants import EVENT_SAMPLE_RATE_KEY
from ...ext import http, AppTypes
from ...propagation.http import HTTPPropagator
from ...settings import config as ddconfig


log = logging.getLogger(__name__)

Expand Down Expand Up @@ -49,6 +52,10 @@ def __call__(self, environ, start_response):
# set as early as possible when different services share one single agent.
span.span_type = http.TYPE

# Configure trace search sample rate
if ddconfig.pylons.event_sample_rate is not None:
span.set_tag(EVENT_SAMPLE_RATE_KEY, ddconfig.pylons.event_sample_rate)

if not span.sampled:
return self.app(environ, start_response)

Expand Down
6 changes: 6 additions & 0 deletions ddtrace/contrib/pyramid/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

# project
import ddtrace
from ...constants import EVENT_SAMPLE_RATE_KEY
from ...ext import http, AppTypes
from ...propagation.http import HTTPPropagator
from ...settings import config
from .constants import (
SETTINGS_TRACER,
SETTINGS_SERVICE,
Expand Down Expand Up @@ -76,6 +78,10 @@ def trace_tween(request):
if context.trace_id:
tracer.context_provider.activate(context)
with tracer.trace('pyramid.request', service=service, resource='404') as span:
# Configure trace search sample rate
if config.pyramid.event_sample_rate is not None:
span.set_tag(EVENT_SAMPLE_RATE_KEY, config.pyramid.event_sample_rate)

setattr(request, DD_SPAN, span) # used to find the tracer in templates
response = None
try:
Expand Down
6 changes: 6 additions & 0 deletions ddtrace/contrib/tornado/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

from .constants import CONFIG_KEY, REQUEST_CONTEXT_KEY, REQUEST_SPAN_KEY
from .stack_context import TracerStackContext
from ...constants import EVENT_SAMPLE_RATE_KEY
from ...ext import http
from ...propagation.http import HTTPPropagator
from ...settings import config


def execute(func, handler, args, kwargs):
Expand Down Expand Up @@ -35,6 +37,10 @@ def execute(func, handler, args, kwargs):
service=service,
span_type=http.TYPE
)
# Configure trace search sample rate
if config.tornado.event_sample_rate is not None:
request_span.set_tag(EVENT_SAMPLE_RATE_KEY, config.tornado.event_sample_rate)

setattr(handler.request, REQUEST_SPAN_KEY, request_span)

return func(*args, **kwargs)
Expand Down
9 changes: 7 additions & 2 deletions ddtrace/opentracer/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import ddtrace
# DEV: If `asyncio` or `gevent` are unavailable we do not throw an error,
# `context_provider` will just not be set and we'll get an `AttributeError` instead
import ddtrace.contrib.asyncio
import ddtrace.contrib.gevent

from ddtrace.provider import DefaultContextProvider


def get_context_provider_for_scope_manager(scope_manager):
Expand All @@ -13,6 +18,6 @@ def get_context_provider_for_scope_manager(scope_manager):
elif scope_manager_type == "GeventScopeManager":
dd_context_provider = ddtrace.contrib.gevent.context_provider
else:
dd_context_provider = ddtrace.provider.DefaultContextProvider()
dd_context_provider = DefaultContextProvider()

return dd_context_provider
Loading

0 comments on commit e8b1070

Please sign in to comment.