Skip to content

Commit 9def4d5

Browse files
committed
pylibmc: add query metadata
1 parent 4be5cd4 commit 9def4d5

File tree

3 files changed

+74
-29
lines changed

3 files changed

+74
-29
lines changed

ddtrace/contrib/pylibmc/client.py

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
# project
1010
import ddtrace
11-
from ddtrace.ext import AppTypes
11+
from ddtrace.ext import memcached
1212
from ddtrace.ext import net
1313
from .addrs import parse_addresses
1414

@@ -22,7 +22,7 @@ class TracedClient(ObjectProxy):
2222
_service = None
2323
_tracer = None
2424

25-
def __init__(self, client, service="memcached", tracer=None):
25+
def __init__(self, client, service=memcached.SERVICE, tracer=None):
2626
""" Create a traced client that wraps the given memcached client. """
2727
super(TracedClient, self).__init__(client)
2828
self._service = service
@@ -38,8 +38,8 @@ def __init__(self, client, service="memcached", tracer=None):
3838
try:
3939
self._tracer.set_service_info(
4040
service=service,
41-
app="memcached",
42-
app_type=AppTypes.cache)
41+
app=memcached.SERVICE,
42+
app_type=memcached.TYPE)
4343
except Exception:
4444
log.exception("error setting service info")
4545

@@ -49,48 +49,65 @@ def clone(self, *args, **kwargs):
4949
return TracedClient(cloned, tracer=self._tracer, service=self._service)
5050

5151
def get(self, *args, **kwargs):
52-
return self._trace("get", *args, **kwargs)
53-
54-
def get_multi(self, *args, **kwargs):
55-
return self._trace("get_multi", *args, **kwargs)
56-
57-
def set_multi(self, *args, **kwargs):
58-
return self._trace("set_multi", *args, **kwargs)
59-
60-
def delete_multi(self, *args, **kwargs):
61-
self._trace("delete_multi", *args, **kwargs)
52+
return self._trace_cmd("get", *args, **kwargs)
6253

6354
def set(self, *args, **kwargs):
64-
return self._trace("set", *args, **kwargs)
55+
return self._trace_cmd("set", *args, **kwargs)
6556

6657
def delete(self, *args, **kwargs):
67-
return self._trace("delete", *args, **kwargs)
58+
return self._trace_cmd("delete", *args, **kwargs)
6859

6960
def gets(self, *args, **kwargs):
70-
return self._trace("gets", *args, **kwargs)
61+
return self._trace_cmd("gets", *args, **kwargs)
7162

7263
def touch(self, *args, **kwargs):
73-
return self._trace("touch", *args, **kwargs)
64+
return self._trace_cmd("touch", *args, **kwargs)
7465

7566
def cas(self, *args, **kwargs):
76-
return self._trace("cas", *args, **kwargs)
67+
return self._trace_cmd("cas", *args, **kwargs)
7768

7869
def incr(self, *args, **kwargs):
79-
return self._trace("incr", *args, **kwargs)
70+
return self._trace_cmd("incr", *args, **kwargs)
8071

8172
def decr(self, *args, **kwargs):
82-
return self._trace("decr", *args, **kwargs)
73+
return self._trace_cmd("decr", *args, **kwargs)
8374

8475
def append(self, *args, **kwargs):
85-
return self._trace("append", *args, **kwargs)
76+
return self._trace_cmd("append", *args, **kwargs)
8677

8778
def prepend(self, *args, **kwargs):
88-
return self._trace("prepend", *args, **kwargs)
79+
return self._trace_cmd("prepend", *args, **kwargs)
80+
81+
def get_multi(self, *args, **kwargs):
82+
return self._trace_multi_cmd("get_multi", *args, **kwargs)
83+
84+
def set_multi(self, *args, **kwargs):
85+
return self._trace_multi_cmd("set_multi", *args, **kwargs)
8986

90-
def _trace(self, method_name, *args, **kwargs):
91-
""" trace the execution of the method with the given name. """
87+
def delete_multi(self, *args, **kwargs):
88+
return self._trace_multi_cmd("delete_multi", *args, **kwargs)
89+
90+
def _trace_cmd(self, method_name, *args, **kwargs):
91+
""" trace the execution of the method with the given name and will
92+
patch the first arg.
93+
"""
94+
method = getattr(self.__wrapped__, method_name)
95+
with self._span(method_name) as span:
96+
97+
if args:
98+
span.set_tag(memcached.QUERY, "%s %s" % (method_name, args[0]))
99+
100+
return method(*args, **kwargs)
101+
102+
def _trace_multi_cmd(self, method_name, *args, **kwargs):
103+
""" trace the execution of the multi command with the given name. """
92104
method = getattr(self.__wrapped__, method_name)
93-
with self._span(method_name):
105+
with self._span(method_name) as span:
106+
107+
pre = kwargs.get('key_prefix')
108+
if pre:
109+
span.set_tag(memcached.QUERY, "%s %s" % (method_name, pre))
110+
94111
return method(*args, **kwargs)
95112

96113
def _span(self, cmd_name):
@@ -105,8 +122,8 @@ def _span(self, cmd_name):
105122
self._tag_span(span)
106123
except Exception:
107124
log.exception("error tagging span")
108-
finally:
109-
return span
125+
126+
return span
110127

111128
def _tag_span(self, span):
112129
# FIXME[matt] the host selection is buried in c code. we can't tell what it's actually

ddtrace/ext/memcached.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
from ddtrace.ext import AppTypes
3+
4+
SERVICE = "memcached"
5+
TYPE = AppTypes.cache
6+
7+
QUERY = "memcached.query"

tests/contrib/pylibmc/test.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,29 @@ def test_get_set_multi(self):
100100
resources = sorted(s.resource for s in spans)
101101
eq_(expected_resources, resources)
102102

103+
def test_get_set_multi_prefix(self):
104+
client, tracer = _setup()
105+
# test
106+
start = time.time()
107+
client.set_multi({"a":1, "b":2}, key_prefix='foo')
108+
out = client.get_multi(["a", "c"], key_prefix='foo')
109+
eq_(out, {"a":1})
110+
client.delete_multi(["a", "c"], key_prefix='foo')
111+
end = time.time()
112+
# verify
113+
spans = tracer.writer.pop()
114+
for s in spans:
115+
_verify_cache_span(s, start, end)
116+
eq_(s.get_tag("memcached.query"), "%s foo" % s.resource,)
117+
expected_resources = sorted(["get_multi", "set_multi", "delete_multi"])
118+
resources = sorted(s.resource for s in spans)
119+
eq_(expected_resources, resources)
120+
121+
103122
def test_get_set_delete(self):
104123
client, tracer = _setup()
105124
# test
106-
k = "key-foo"
125+
k = u'cafe'
107126
v = "val-foo"
108127
start = time.time()
109128
client.delete(k) # just in case
@@ -117,10 +136,12 @@ def test_get_set_delete(self):
117136
spans = tracer.writer.pop()
118137
for s in spans:
119138
_verify_cache_span(s, start, end)
139+
eq_(s.get_tag("memcached.query"), "%s %s" % (s.resource, k))
120140
expected_resources = sorted(["get", "get", "delete", "set"])
121141
resources = sorted(s.resource for s in spans)
122142
eq_(expected_resources, resources)
123143

144+
124145
def _verify_cache_span(s, start, end):
125146
assert s.start > start
126147
assert s.start + s.duration < end

0 commit comments

Comments
 (0)