Skip to content

Commit

Permalink
ref: Instrument SQL, and change schema as discussed
Browse files Browse the repository at this point in the history
  • Loading branch information
untitaker committed May 28, 2019
1 parent cf9eda2 commit 9cde1cd
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 58 deletions.
38 changes: 24 additions & 14 deletions sentry_sdk/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,8 @@ def span(self, span=None, **kwargs):
span.finish()
self.capture_trace(span)

def trace(self, *args, **kwargs):
return self.span(self.start_trace(*args, **kwargs))
def trace(self, **kwargs):
return self.span(self.start_trace(**kwargs))

def start_span(self, **kwargs):
_, scope = self._stack[-1]
Expand All @@ -376,26 +376,36 @@ def start_span(self, **kwargs):
return span.new_span(**kwargs)
return None

def start_trace(self, transaction, **kwargs):
_, scope = self._stack[-1]
scope.span = span = Span.start_trace(transaction, **kwargs)
return span
def start_trace(self, **kwargs):
span = Span.start_trace(**kwargs)

def capture_trace(self, span):
if span.transaction is None:
if self.client is None or span.sampled is False:
return None

client = self.client

if client is None:
sample_rate = self.client.options["traces_sample_rate"]
if (
sample_rate < 1.0
and random.random() >= sample_rate
and span.sampled is None
):
return None

sample_rate = client.options["traces_sample_rate"]
if sample_rate < 1.0 and random.random() >= sample_rate:
_, scope = self._stack[-1]
scope.span = span
return span

def capture_trace(self, span):
if span.transaction is None or span.timestamp is None:
return None

return self.capture_event(
{"type": "none", "spans": [s.to_json() for s in span._finished_spans]}
{
"type": "transaction",
"contexts": {"trace": span.get_trace_context()},
"timestamp": span.timestamp,
"start_timestamp": span.start_timestamp,
"spans": [s.to_json() for s in span._finished_spans if s is not span],
}
)

@overload # noqa
Expand Down
39 changes: 29 additions & 10 deletions sentry_sdk/integrations/django/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import

import contextlib
import sys
import weakref

Expand Down Expand Up @@ -318,10 +319,12 @@ def format_sql(sql, params):
return sql, rv


@contextlib.contextmanager
def record_sql(sql, params, cursor=None):
# type: (Any, Any, Any) -> None
hub = Hub.current
if hub.get_integration(DjangoIntegration) is None:
yield
return

real_sql = None
Expand Down Expand Up @@ -353,9 +356,33 @@ def record_sql(sql, params, cursor=None):
except Exception:
pass

span = None

if real_sql:
with capture_internal_exceptions():
hub.add_breadcrumb(message=real_sql, category="query")
span = hub.start_span(op="sql.query", description=real_sql)

if span is None:
yield
else:
try:
yield
finally:
span.set_tag("status", sys.exc_info()[1] is None)
span.finish()


@contextlib.contextmanager
def record_many_sql(sql, param_list, cursor):
ctxs = [record_sql(sql, params, cursor).__enter__() for params in param_list]

try:
yield
finally:
einfo = sys.exc_info()
for ctx in ctxs:
ctx.__exit__(*einfo)


def install_sql_hook():
Expand All @@ -373,21 +400,13 @@ def install_sql_hook():
# This won't work on Django versions < 1.6
return

def record_many_sql(sql, param_list, cursor):
for params in param_list:
record_sql(sql, params, cursor)

def execute(self, sql, params=None):
try:
with record_sql(sql, params, self.cursor):
return real_execute(self, sql, params)
finally:
record_sql(sql, params, self.cursor)

def executemany(self, sql, param_list):
try:
with record_many_sql(sql, param_list, self.cursor):
return real_executemany(self, sql, param_list)
finally:
record_many_sql(sql, param_list, self.cursor)

CursorWrapper.execute = execute
CursorWrapper.executemany = executemany
Expand Down
10 changes: 7 additions & 3 deletions sentry_sdk/integrations/stdlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ def putrequest(self, method, url, *args, **kwargs):
if hub.get_integration(StdlibIntegration) is None:
return rv

self._sentrysdk_data_dict = data = {}
self._sentrysdk_span = hub.start_span()

host = self.host
port = self.port
default_port = self.default_port
Expand All @@ -44,6 +41,11 @@ def putrequest(self, method, url, *args, **kwargs):
url,
)

self._sentrysdk_data_dict = data = {}
self._sentrysdk_span = hub.start_span(
op="http", description="%s %s" % (real_url, method)
)

for key, value in hub.iter_trace_propagation_headers():
self.putheader(key, value)

Expand All @@ -66,6 +68,8 @@ def getresponse(self, *args, **kwargs):
span = self._sentrysdk_span
if span is not None:
span.set_tag("status_code", rv.status)
for k, v in data.items():
span.set_data(k, v)
span.finish()

hub.add_breadcrumb(
Expand Down
7 changes: 3 additions & 4 deletions sentry_sdk/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,9 @@ def _drop(event, cause, ty):
event.setdefault("contexts", {}).update(self._contexts)

if self._span is not None:
event.setdefault("contexts", {})["trace"] = {
"trace_id": self._span.trace_id,
"span_id": self._span.span_id,
}
contexts = event.setdefault("contexts", {})
if not contexts.get("trace"):
contexts["trace"] = self._span.get_trace_context()

exc_info = hint.get("exc_info") if hint is not None else None
if exc_info is not None:
Expand Down
61 changes: 37 additions & 24 deletions sentry_sdk/tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,41 @@ class Span(object):
__slots__ = (
"trace_id",
"span_id",
"ref",
"ref_type",
"parent_span_id",
"same_process_as_parent",
"sampled",
"transaction",
"op",
"description",
"start_timestamp",
"timestamp",
"_tags",
"_data",
"_finished_spans",
"start",
"end",
)

def __init__(
self, trace_id, span_id, transaction=None, ref=None, ref_type=None, sampled=None
self,
trace_id,
span_id,
parent_span_id=None,
same_process_as_parent=True,
sampled=None,
transaction=None,
op=None,
description=None,
):
self.trace_id = trace_id
self.span_id = span_id
self.ref = ref
self.ref_type = ref_type
self.parent_span_id = parent_span_id
self.same_process_as_parent = same_process_as_parent
self.sampled = sampled
self.transaction = transaction
self._tags = {}
self._data = {}
self._finished_spans = []
self.start = datetime.now()
self.end = None
self.start_timestamp = datetime.now()
self.timestamp = None

def __repr__(self):
return "<%s(transaction=%r, trace_id=%r, span_id=%r, ref=%r)>" % (
Expand All @@ -58,24 +70,18 @@ def __repr__(self):
)

@classmethod
def start_trace(cls, transaction=None, sampled=None):
return cls(
transaction=transaction,
trace_id=uuid.uuid4().hex,
span_id=uuid.uuid4().hex[16:],
sampled=sampled,
)
def start_trace(cls, **kwargs):
return cls(trace_id=uuid.uuid4().hex, span_id=uuid.uuid4().hex[16:], **kwargs)

def new_span(self, ref_type="child"):
def new_span(self, **kwargs):
if self.trace_id is None:
return Span.start_trace()

rv = Span(
trace_id=self.trace_id,
span_id=uuid.uuid4().hex[16:],
ref=self,
ref_type=ref_type,
sampled=self.sampled,
parent_span_id=self.span_id,
**kwargs
)
rv._finished_spans = self._finished_spans
return rv
Expand Down Expand Up @@ -124,17 +130,24 @@ def to_traceparent(self):
def set_tag(self, key, value):
self._tags[key] = value

def set_data(self, key, value):
self._data[key] = value

def finish(self):
self.end = datetime.now()
self.timestamp = datetime.now()
self._finished_spans.append(self)

def to_json(self):
return {
"trace_id": self.trace_id,
"span_id": self.span_id,
"ref_span_id": self.ref and self.ref.span_id or None,
"parent_span_id": self.parent_span_id,
"transaction": self.transaction,
"tags": self._tags,
"start": self.start,
"end": self.end,
"data": self._data,
"start_timestamp": self.start_timestamp,
"timestamp": self.timestamp,
}

def get_trace_context(self):
return {"trace_id": self.trace_id, "span_id": self.span_id}
7 changes: 4 additions & 3 deletions tests/test_tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ def test_basic(sentry_init, capture_events, sample_rate):
sentry_init(traces_sample_rate=sample_rate)
events = capture_events()

with Hub.current.trace("hi"):
with Hub.current.span():
1 / 0
with Hub.current.trace(transaction="hi"):
with pytest.raises(ZeroDivisionError):
with Hub.current.span():
1 / 0

with Hub.current.span():
pass
Expand Down

0 comments on commit 9cde1cd

Please sign in to comment.