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
19 changes: 19 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,22 @@ task :release do

sh "mkwheelhouse s3://#{s3_bucket}/#{s3_dir}/ ."
end

task :clean do
sh 'rm -rf build *egg*'
end


task :docs do
Dir.chdir 'docs' do
sh "make html"
end
end

task :'docs:loop' do
# FIXME do something real here
while true do
sleep 2
Rake::Task["docs"].execute
end
end
5 changes: 4 additions & 1 deletion ddtrace/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
"""Datadog Tracing client"""
"""
"""

from .tracer import Tracer
from .span import Span

__version__ = '0.2.0'

Expand Down
1 change: 0 additions & 1 deletion ddtrace/contrib/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@


def func_name(f):
""" Return a human readable version of the function's name. """
return "%s.%s" % (f.__module__, f.__name__)
18 changes: 18 additions & 0 deletions ddtrace/contrib/django/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
"""
The Django middleware will trace requests, database calls and template
renders.

To install the Django tracing middleware, add it to the list of your
application's installed in middleware in settings.py::


MIDDLEWARE_CLASSES = (
...
'ddtrace.contrib.django.TraceMiddleware',
...
)

DATADOG_SERVICE = 'my-app'

"""

from ..util import require_modules

required_modules = ['django']
Expand Down
20 changes: 20 additions & 0 deletions ddtrace/contrib/flask/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
"""
The flask trace middleware will track request timings and templates. It
requires the `Blinker <https://pythonhosted.org/blinker/>`_ library, which
Flask uses for signalling.

To install the middleware, do the following::

from flask import Flask
from ddtrace import tracer

app = Flask(...)

traced_app = TraceMiddleware(app, tracer, service="my-flask-app")

@app.route("/")
def home():
return "hello world"

"""

from ..util import require_modules

required_modules = ['flask']
Expand Down
4 changes: 4 additions & 0 deletions ddtrace/contrib/sqlite3/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ def connection_factory(tracer, service="sqlite3"):
""" Return a connection factory class that will can be used to trace
sqlite queries.


:param ddtrace.Tracer tracer: the tracer that will report the spans.
:param str service: the name of the database's service.

>>> factory = connection_factor(my_tracer, service="my_db_service")
>>> conn = sqlite3.connect(":memory:", factory=factory)
"""
Expand Down
83 changes: 49 additions & 34 deletions ddtrace/span.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,21 @@ def __init__(self,
parent_id=None,
start=None):
"""
tracer: a link to the tracer that will store this span
name: the name of the operation we're measuring.
service: the name of the service that is being measured
resource: an optional way of specifying the 'normalized' params
of the request (i.e. the sql query, the url handler, etc)
start: the start time of request as a unix epoch in seconds
Create a new span. You must call `finish` on all spans.

:param Tracer tracer: the tracer that will submit this span when
finished.
:param str name: the name of the traced operation.
:param str service: the service name
:param str resource: the resource name

:param int start: the start time of the span in seconds from the epoch

:param int trace_id: the id of this trace's root span.
:param int parent_id: the id of this span's direct parent span.
:param int span_id: the id of this span.

:param int start: the start time of request as a unix epoch in seconds
"""
# required span info
self.name = name
Expand Down Expand Up @@ -61,15 +70,47 @@ def __init__(self,
self._parent = None

def finish(self, finish_time=None):
""" Mark the end time of the span and submit it to the tracer. """
""" Mark the end time of the span and submit it to the tracer.

:param int finish_time: the end time of the span in seconds.
Defaults to now.
"""
ft = finish_time or time.time()
# be defensive so we don't die if start isn't set
self.duration = ft - (self.start or ft)
if self._tracer:
self._tracer.record(self)

def set_tag(self, key, value):
""" Set the given key / value tag pair on the span. Keys and values
must be strings (or stringable). If a casting error occurs, it will
be ignored.
"""
try:
self.meta[key] = stringify(value)
except Exception:
log.warning("error setting tag. ignoring", exc_info=True)

def get_tag(self, key):
""" Return the given tag or None if it doesn't exist.
"""
return self.meta.get(key, None)

def set_tags(self, tags):
""" Set a dictionary of tags on the given span. Keys and values
must be strings (or stringable)
"""
if tags:
for k, v in iter(tags.items()):
self.set_tag(k, v)

def set_meta(self, k, v):
self.set_tag(k, v)

def set_metas(self, kvs):
self.set_tags(kvs)

def to_dict(self):
""" Return a json serializable dictionary of the span's attributes. """
d = {
'trace_id' : self.trace_id,
'parent_id' : self.parent_id,
Expand All @@ -95,32 +136,6 @@ def to_dict(self):

return d

def set_tag(self, key, value):
""" Set the given key / value tag pair on the span. Keys and values
must be strings (or stringable). If a casting error occurs, it will
be ignored.
"""
try:
self.meta[key] = stringify(value)
except Exception:
log.warning("error setting tag. ignoring", exc_info=True)

def get_tag(self, key):
""" Return the given tag or None if it doesn't exist"""
return self.meta.get(key, None)

def set_tags(self, tags):
""" Set a dictionary of tags on the given span. Keys and values
must be strings (or stringable)
"""
if tags:
for k, v in iter(tags.items()):
self.set_tag(k, v)

# backwards compatilibility, kill this
set_meta = set_tag
set_metas = set_tags

def set_traceback(self):
""" If the current stack has a traceback, tag the span with the
relevant error info.
Expand Down
57 changes: 40 additions & 17 deletions ddtrace/tracer.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import logging
import threading

Expand All @@ -11,16 +12,22 @@


class Tracer(object):
"""
Tracer is used to create, sample and submit spans that measure the
execution time of sections of code.

If you're running an application that will serve a single trace per thread,
you can use the global traced instance:

>>> from ddtrace import tracer
>>> tracer.trace("foo").finish()
"""

def __init__(self, enabled=True, writer=None, span_buffer=None, sampler=None):
"""
Create a new tracer object.
Create a new tracer.

enabled: if False, no spans will be submitted to the writer
writer: an instance of Writer
span_buffer: a span buffer instance. used to store inflight traces. by
default, will use thread local storage
sampler: Trace sampler.
:param bool enabled: If true, finished traces will be submitted to the API. Otherwise they'll be dropped.
"""
self.enabled = enabled

Expand All @@ -43,15 +50,32 @@ def trace(self, name, service=None, resource=None, span_type=None):
"""
Return a span that will trace an operation called `name`.

It will store the created span in the span buffer and until it's
finished, any new spans will be a child of this span.
:param str name: the name of the operation being traced
:param str service: the name of the service being traced. If not set,
it will inherit the service from it's parent.
:param str resource: an optional name of the resource being tracked.

You must call `finish` on all spans, either directly or with a context
manager.

>>> span = tracer.trace("web.request")
try:
# do something
finally:
span.finish()
>>> with tracer.trace("web.request") as span:
# do something

Trace will store the created span and subsequent child traces will
become it's children.

>>> tracer = Tracer()
>>> parent = tracer.trace("parent") # has no parent span
>>> child = tracer.child("child") # is a child of a parent
>>> parent = tracer.trace("parent") # has no parent span
>>> child = tracer.trace("child") # is a child of a parent
>>> child.finish()
>>> parent.finish()
>>> parent2 = tracer.trace("parent2") # has no parent span
>>> parent2 = tracer.trace("parent2") # has no parent span
>>> parent2.finish()
"""
span = None
parent = self._span_buffer.get()
Expand All @@ -61,11 +85,11 @@ def trace(self, name, service=None, resource=None, span_type=None):
span = Span(
self,
name,
trace_id=parent.trace_id,
parent_id=parent.span_id,
service=(service or parent.service),
resource=resource,
span_type=span_type,
trace_id=parent.trace_id,
parent_id=parent.span_id,
)
span._parent = parent
span.sampled = parent.sampled
Expand Down Expand Up @@ -103,7 +127,6 @@ def record(self, span):
self.write(spans)

def write(self, spans):
""" Submit the given spans to the agent. """
if not spans:
return # nothing to do

Expand All @@ -120,9 +143,9 @@ 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)
:param str service: the internal name of the service (e.g. acme_search, datadog_web)
:param str app: the off the shelf name of the application (e.g. rails, postgres, custom-app)
:param str app_type: the type of the application (e.g. db, web)
"""
self._services[service] = {
"app" : app,
Expand Down
Loading