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
8 changes: 8 additions & 0 deletions ddtrace/contrib/cassandra/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from ...compat import stringify
from ...util import deep_getattr, safe_patch
from ...ext import net as netx, cassandra as cassx
from ...ext import AppTypes

# 3p
import cassandra.cluster
Expand All @@ -29,6 +30,13 @@ def get_traced_cassandra(tracer, service=DEFAULT_SERVICE, meta=None):

def _get_traced_cluster(cassandra, tracer, service="cassandra", meta=None):
""" Trace synchronous cassandra commands by patching the Session class """

tracer.set_service_info(
service=service,
app="cassandra",
app_type=AppTypes.db,
)

class TracedSession(cassandra.Session):
_datadog_tracer = tracer
_datadog_service = service
Expand Down
9 changes: 9 additions & 0 deletions ddtrace/contrib/django/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

# project
from ...ext import sql as sqlx
from ...ext import AppTypes


log = logging.getLogger(__name__)
Expand Down Expand Up @@ -40,6 +41,12 @@ def __init__(self, tracer, conn, cursor):
self._name = "%s.%s" % (prefix, "query") # e.g sqlite3.query
self._service = "%s%s" % (self._alias or prefix, "db") # e.g. defaultdb or postgresdb

self.tracer.set_service_info(
service=self._service,
app=prefix,
app_type=AppTypes.db,
)

def _trace(self, func, sql, params):
with self.tracer.trace(self._name, resource=sql, service=self._service, span_type=sqlx.TYPE) as span:
span.set_tag(sqlx.QUERY, sql)
Expand Down Expand Up @@ -82,5 +89,7 @@ def _vendor_to_prefix(vendor):
return "db" # should this ever happen?
elif vendor == "sqlite":
return "sqlite3" # for consistency with the sqlite3 integration
elif vendor == "postgresql":
return "postgres"
else:
return vendor
8 changes: 7 additions & 1 deletion ddtrace/contrib/django/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# project
from ... import tracer
from ...ext import http
from ...ext import http, AppTypes
from ...contrib import func_name
from .templates import patch_template
from .db import patch_db
Expand All @@ -22,6 +22,12 @@ def __init__(self):
self.tracer = tracer
self.service = getattr(settings, 'DATADOG_SERVICE', 'django')

self.tracer.set_service_info(
service=self.service,
app='django',
app_type=AppTypes.web,
)

try:
patch_template(self.tracer)
except Exception:
Expand Down
7 changes: 7 additions & 0 deletions ddtrace/contrib/elasticsearch/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@
from .quantize import quantize
from . import metadata
from ...compat import json, urlencode
from ...ext import AppTypes

DEFAULT_SERVICE = 'elasticsearch'
SPAN_TYPE = 'elasticsearch'


def get_traced_transport(datadog_tracer, datadog_service=DEFAULT_SERVICE):

datadog_tracer.set_service_info(
service=datadog_service,
app=SPAN_TYPE,
app_type=AppTypes.db,
)

class TracedTransport(Transport):
"""Extend elasticseach transport layer to allow Datadog tracer to catch any performed request"""

Expand Down
8 changes: 7 additions & 1 deletion ddtrace/contrib/flask/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import logging

# project
from ...ext import http, errors
from ...ext import http, errors, AppTypes

# 3p
from flask import g, request, signals
Expand All @@ -26,6 +26,12 @@ def __init__(self, app, tracer, service="flask", use_signals=True):
self._tracer = tracer
self._service = service

self._tracer.set_service_info(
service=service,
app="flask",
app_type=AppTypes.web,
)

self.use_signals = use_signals

if self.use_signals and signals.signals_available:
Expand Down
14 changes: 8 additions & 6 deletions ddtrace/contrib/psycopg/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from ...ext import net
from ...ext import sql as sqlx
from ...ext import AppTypes

# 3p
from psycopg2.extensions import connection, cursor
Expand All @@ -23,6 +24,13 @@ def connection_factory(tracer, service="postgres"):
>>> factory = connection_factor(my_tracer, service="my_db_service")
>>> conn = pyscopg2.connect(..., connection_factory=factory)
"""

tracer.set_service_info(
service=service,
app="postgres",
app_type=AppTypes.db,
)

return functools.partial(TracedConnection,
datadog_tracer=tracer,
datadog_service=service,
Expand Down Expand Up @@ -89,12 +97,6 @@ def __init__(self, *args, **kwargs):
datadog_tags=self._datadog_tags,
)

# DogTrace.register_service(
# service=self._dogtrace_service,
# app="postgres",
# app_type="sql",
# )

def cursor(self, *args, **kwargs):
""" register our custom cursor factory """
kwargs.setdefault('cursor_factory', self._datadog_cursor_class)
Expand Down
7 changes: 7 additions & 0 deletions ddtrace/contrib/pylons/middleware.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging

from ...ext import http
from ...ext import AppTypes

log = logging.getLogger(__name__)

Expand All @@ -12,6 +13,12 @@ def __init__(self, app, tracer, service="pylons"):
self._service = service
self._tracer = tracer

self._tracer.set_service_info(
service=service,
app="pylons",
app_type=AppTypes.web,
)

def __call__(self, environ, start_response):
with self._tracer.trace("pylons.request", service=self._service, span_type=http.TYPE) as span:

Expand Down
9 changes: 8 additions & 1 deletion ddtrace/contrib/redis/tracers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# dogtrace
from .util import format_command_args, _extract_conn_tags
from ...ext import redis as redisx
from ...ext import AppTypes


DEFAULT_SERVICE = 'redis'
Expand All @@ -27,9 +28,15 @@ def _get_traced_redis(ddtracer, baseclass, service, meta):
basepipeline = StrictPipeline
try:
basepipeline = baseclass().pipeline().__class__
except:
except Exception:
pass

ddtracer.set_service_info(
service=service,
app="redis",
app_type=AppTypes.db,
)

class TracedPipeline(basepipeline):
_datadog_tracer = ddtracer
_datadog_service = service
Expand Down
8 changes: 8 additions & 0 deletions ddtrace/contrib/sqlite3/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from sqlite3 import Connection, Cursor
from ...ext import sql as sqlx
from ...ext import AppTypes


def connection_factory(tracer, service="sqlite3"):
Expand All @@ -11,6 +12,13 @@ def connection_factory(tracer, service="sqlite3"):
>>> factory = connection_factor(my_tracer, service="my_db_service")
>>> conn = sqlite3.connect(":memory:", factory=factory)
"""

tracer.set_service_info(
service=service,
app="sqlite3",
app_type=AppTypes.db,
)

return functools.partial(TracedConnection,
datadog_tracer=tracer,
datadog_service=service,
Expand Down
6 changes: 6 additions & 0 deletions ddtrace/ext/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

class AppTypes(object):

web = "web"
db = "db"
cache = "cache"
1 change: 1 addition & 0 deletions ddtrace/ext/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

1 change: 1 addition & 0 deletions ddtrace/ext/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@

# template render span type
TEMPLATE = 'template'

3 changes: 2 additions & 1 deletion ddtrace/reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@


class AgentReporter(object):
SERVICES_FLUSH_INTERVAL = 60

SERVICES_FLUSH_INTERVAL = 120

def __init__(self):
self.transport = ThreadedHTTPTransport()
Expand Down
23 changes: 22 additions & 1 deletion ddtrace/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ def __init__(self, enabled=True, writer=None, span_buffer=None, sample_rate=1):
self._spans_lock = threading.Lock()
self._spans = []

# a collection of registered services by name.
self._services = {}

self.sampler = RateSampler(sample_rate)

# A hook for local debugging. shouldn't be needed or used
Expand Down Expand Up @@ -111,4 +114,22 @@ def write(self, spans):
for span in spans:
log.debug("\n%s", span.pprint())

self._writer.write(spans)
self._writer.write(spans, self._services)

def set_service_info(self, service, app, app_type):
"""
Set the information about the given service.

@service: the internal name of the service (e.g. acme_search, datadog_web)
@app: the off the shelf name of the application (e.g. rails, postgres, custom-app)
@app_type: the type of the application (e.g. db, web)
"""
self._services[service] = {
"app" : app,
"app_type": app_type,
}

if self.debug_logging:
log.debug("set_service_info: service:%s app:%s type:%s",
service, app, app_type)

5 changes: 3 additions & 2 deletions ddtrace/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ class AgentWriter(object):
def __init__(self):
self._reporter = AgentReporter()

def write(self, spans):
self._reporter.report(spans, [])
def write(self, spans, services=None):
self._reporter.report(spans, services)

7 changes: 7 additions & 0 deletions tests/contrib/flask/test_flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,13 @@ def test_success(self):
eq_(s.error, 0)
eq_(s.meta.get(http.STATUS_CODE), '200')

services = writer.pop_services()
expected = {
service : {"app":"flask", "app_type":"web"}
}
eq_(services, expected)


def test_template(self):
start = time.time()
rv = app.get('/tmpl')
Expand Down
10 changes: 10 additions & 0 deletions tests/contrib/psycopg/test_psycopg.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,13 @@ def test_wrap():
eq_(span.meta["out.host"], 'localhost')
eq_(span.meta["out.port"], '5432')
eq_(span.span_type, "sql")

# ensure we have the service types
services = writer.pop_services()
expected = {
"db" : {"app":"postgres", "app_type":"db"},
"another" : {"app":"postgres", "app_type":"db"},
}
eq_(services, expected)


7 changes: 7 additions & 0 deletions tests/contrib/redis/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ def test_basic_class(self):
eq_(span.meta, {'out.host': u'localhost', 'redis.raw_command': u'GET cheese', 'out.port': u'6379', 'redis.args_length': u'2', 'out.redis_db': u'0'})
eq_(span.resource, 'GET cheese')

services = writer.pop_services()
expected = {
self.SERVICE: {"app":"redis", "app_type":"db"}
}
eq_(services, expected)


def test_meta_override(self):
writer = DummyWriter()
tracer = Tracer(writer=writer)
Expand Down
9 changes: 9 additions & 0 deletions tests/contrib/sqlite3/test_sqlite3.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,13 @@ def test_foo():
assert 'OperationalError' in span.get_tag(errors.ERROR_TYPE)
assert 'no such table' in span.get_tag(errors.ERROR_MSG)

# ensure we have the service types
services = writer.pop_services()
expected = {
"db" : {"app":"sqlite3", "app_type":"db"},
"another" : {"app":"sqlite3", "app_type":"db"},
}
eq_(services, expected)



13 changes: 12 additions & 1 deletion tests/test_tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,22 @@ class DummyWriter(object):

def __init__(self):
self.spans = []
self.services = {}

def write(self, spans):
def write(self, spans, services=None):
self.spans += spans
if services:
self.services.update(services)

# dummy methods

def pop(self):
s = self.spans
self.spans = []
return s

def pop_services(self):
s = self.services
self.services = {}
return s