Add pymemcache instrumentation#772
Conversation
majorgreys
left a comment
There was a problem hiding this comment.
LGTM on a first pass!
| """ | ||
| Usage | ||
| ----- | ||
| The OpenTelemetry ``jinja2`` integration traces templates loading, compilation |
There was a problem hiding this comment.
👍 good catch , updated
ext/opentelemetry-ext-pymemcache/src/opentelemetry/ext/pymemcache/__init__.py
Show resolved
Hide resolved
| _CMD, kind=SpanKind.INTERNAL, attributes={} | ||
| ) as span: | ||
| try: | ||
| span.set_attribute("service", tracer.instrumentation_info.name) |
There was a problem hiding this comment.
The service name is handled by the Resource in OpenTelemetry so we can skip setting this attribute.
There was a problem hiding this comment.
ah gotcha, makes sense, i've removed this entirely then.
cnnradams
left a comment
There was a problem hiding this comment.
LGTM! Have a few minor comments, but other than that looks great!
| from opentelemetry.ext.pymemcache import PymemcacheInstrumentor | ||
| trace.set_tracer_provider(TracerProvider()) | ||
| from pymemcache.client.base import Client | ||
| client = Client(('localhost', 11211)) |
There was a problem hiding this comment.
Should there be a PymemcacheInstrumentor().instrument() before creating the client?
There was a problem hiding this comment.
ah good catch, ty, updated
| See `BaseInstrumentor` | ||
| """ | ||
|
|
||
| def _instrument(self, **kwargs): |
There was a problem hiding this comment.
A lot of other similar instrumentors also expose an instrument_client(client) function that would instrument methods on a single instantiated Client instead of every Client in every file that is created. Is that possible here, and if so do you think it provides enough value to add?
There was a problem hiding this comment.
I think that sounds possible, I think some of the dd-trace-py instrumentations expose something to that effect, and it could have some value, but I don't think it's trivial to add. Is that convention established anywhere in opentelemetry-python at the moment?
There was a problem hiding this comment.
good question, I don't think it's written down anywhere, I just see it a lot (all db instrumentations,most webserver instrumentations). There's probably no need to add it in this PR unless there was actually a use case where you would want to instrument one client but not others
There was a problem hiding this comment.
no strong convention there, but see flask for an example:https://github.com/open-telemetry/opentelemetry-python/blob/master/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py#L194
One thing is that "app" is probably a misnomer in the context of memcache, so the convention is loose at best.
There was a problem hiding this comment.
There is no convention for any other method outside _instrument and _uninstrument. Since these instrumentations deal with different third party libraries it is ok to add more methods as needed (like it was done for the Flask instrumentation) to better adapt the instrumentation to the characteristics of the third party libraries.
| instance.server | ||
| ) | ||
|
|
||
| except Exception: # pylint: disable=broad-except |
There was a problem hiding this comment.
What would cause an exception here?
There was a problem hiding this comment.
just was being overly defensive, removed
| elif isinstance(arg, bytes): | ||
| keys = arg.decode() | ||
| elif isinstance(arg, list) and len(arg) >= 1: | ||
| if isinstance(arg[0], str): |
There was a problem hiding this comment.
are we guaranteed that every argument in the argument list is a string if the first one is? not familiar with pymemcache, so maybe it is guaranteed
There was a problem hiding this comment.
Not a pymemcache expert but yes I believe that's the case, first arg is either:
- keys, a list or dict of key strings, like here:
https://github.com/pinterest/pymemcache/blob/f02ddf73a28c09256589b8afbb3ee50f1171cac7/pymemcache/client/base.py#L503 - or a single key, a string:
https://github.com/pinterest/pymemcache/blob/f02ddf73a28c09256589b8afbb3ee50f1171cac7/pymemcache/client/base.py#L324
Also, just to clarify, we're only grabbing the keys of the query, not the full command
|
|
||
| _set_connection_attributes(span, instance) | ||
| except Exception: # pylint: disable=broad-except | ||
| pass |
There was a problem hiding this comment.
👍 added a warning log
| return address_attributes | ||
|
|
||
|
|
||
| def _get_query_string(args): |
There was a problem hiding this comment.
If this function only ever deals with the first element of the list, may be it should take only a single element as argument instead of a list?
There was a problem hiding this comment.
Also, this function is only used in one place in your code, better to move it there to avoid having to jump to some other file to read your code.
There was a problem hiding this comment.
@owais agreed, i've updated this function (and moved it to the same file as the function where it's being called) to simplify things a bit
| # pull out the first arg which will contain any key | ||
| arg = args[0] | ||
|
|
||
| # if we get a dict, convert to list of keys |
There was a problem hiding this comment.
Nit: these comments are a bit unnecessary
There was a problem hiding this comment.
fair enough, removed
| def test_set_success(self): | ||
| client = self.make_client([b"STORED\r\n"]) | ||
| result = client.set(b"key", b"value", noreply=False) | ||
| assert result is True |
There was a problem hiding this comment.
| assert result is True | |
| self.assertTrue(result) |
ocelotl
left a comment
There was a problem hiding this comment.
Just some minor comments
toumorokoshi
left a comment
There was a problem hiding this comment.
not 100% how to handle changelog and versioning, as well as setup.cfg and setup.py files. Took some educated guesses and basically just borrowed the structure of other integrations as a guide, let me know if I've botched anything here
Looks good! Not much to it. See https://github.com/open-telemetry/opentelemetry-python/blob/master/RELEASING.md for our whole process which includes bumping versions.
this opentelemetry-ext-pymemcache implementation differs a bit from dd-trace-py, in that dd-trace-py replaces the entire Client with a wrapped client, whereas opentelemetry-ext-pymemcache is only wrapping specific methods on the original Client. The dd-trace-py approach relies on Pin, which isn't a construct available in opentelemetry-python afaik. There were a few gotchas with this approach (like ensuring methods aren't patched multiple times) but overall it behaves the same.
Do you have any docs / links to "Pin"? Maybe it's a concept we should add in.
Tried to write the tests as closely as I could to the originals in dd-trace-py but was not super sure how to test some of config/client setting stuff, so left that out for now.
Do you have links? curious about these as well.
Thanks! overall LGTM.
| return keys | ||
|
|
||
|
|
||
| def _unwrap(obj, attr): |
There was a problem hiding this comment.
this is a now a utility in opentelemetry-instrumentation. it would be great to refactor to use that method.
There was a problem hiding this comment.
ah nice, updated to use the util method
| See `BaseInstrumentor` | ||
| """ | ||
|
|
||
| def _instrument(self, **kwargs): |
There was a problem hiding this comment.
no strong convention there, but see flask for an example:https://github.com/open-telemetry/opentelemetry-python/blob/master/ext/opentelemetry-ext-flask/src/opentelemetry/ext/flask/__init__.py#L194
One thing is that "app" is probably a misnomer in the context of memcache, so the convention is loose at best.
ocelotl
left a comment
There was a problem hiding this comment.
Approving, I commented about a couple minor changes that could be addressed 👍
| from opentelemetry.ext.pymemcache.version import __version__ | ||
| from opentelemetry.instrumentation.instrumentor import BaseInstrumentor | ||
| from opentelemetry.trace import SpanKind, get_tracer | ||
| from opentelemetry.trace.status import Status, StatusCanonicalCode |
There was a problem hiding this comment.
good catch, updated (removed)
ext/opentelemetry-ext-pymemcache/src/opentelemetry/ext/pymemcache/__init__.py
Show resolved
Hide resolved
| See `BaseInstrumentor` | ||
| """ | ||
|
|
||
| def _instrument(self, **kwargs): |
There was a problem hiding this comment.
There is no convention for any other method outside _instrument and _uninstrument. Since these instrumentations deal with different third party libraries it is ok to add more methods as needed (like it was done for the Flask instrumentation) to better adapt the instrumentation to the characteristics of the third party libraries.
|
@ericmustin do you have time to look at Diego's comments today? We're good to merge after maybe a final look by you. |
ext/opentelemetry-ext-pymemcache/src/opentelemetry/ext/pymemcache/__init__.py
Show resolved
Hide resolved
| return wrapped(*args, **kwargs) | ||
|
|
||
|
|
||
| def _get_query_string(arg): |
There was a problem hiding this comment.
I'm wondering if some of these methods defined here could go into util.py and leave only the wrapping, instrumenting code in __init__.py.
There was a problem hiding this comment.
apologies, i missed this comment. yea i mean, i don't have strong opinions here at all, this was actually originally in util and it was suggested to move it here 😅 #772 (comment)
Happy to move _get_query_string back if that's preferred
There was a problem hiding this comment.
@ocelotl has a point that it's only being used once. Maybe just move all the funcitons into this file and get rid of the utils.py?
There was a problem hiding this comment.
Yup, I think that probably makes sense, updated (merged util.py helpers into __init__.py and removed util.py ).
lzchen
left a comment
There was a problem hiding this comment.
LGTM. Some non-blocking comments.
|
@toumorokoshi apologies, i'm afk rn (i'm in CEST timezone 🇫🇷 ) so likely cannot grab these before EOD US 🇺🇸 . let me circle back to these first thing in the morning, everyone's comments seem very reasonable i should be able to address them pretty quickly. thanks for all the the thoughtful reviews everyone! |
no worries! sorry there's no timeline here. Just @ me when things look good and I'll merge. |
|
@toumorokoshi I think I've addressed the relevant feedback here from @ocelotl , @lzchen , @cnnradams and others. Thanks again all for the great feedback! I'm still a little bit confused on what I should include in the Addressing the other feedback points:
Lmk if there's anything else needed here or I've missed something, happy to help make sure this gets brought across the finish line. |
|
@ericmustin |
|
@ericmustin sorry, but this needs to be updated to the new version (0.10.dev.0) since that was released yesterday. can you either update the branch yourself, or change the PR configuration so we can make changes to your branch? |
|
@toumorokoshi Updated to |
|
Great, thanks a lot! Sorry for all the back and forth. |

summary
👋 This PR adds pymemcache instrumentation and tests, addressing: #766 . Still a few open questions I have, but at a place where I think this can be reviewed. By and large I tried to port things from dd-trace-py's pymemcache instumentation
Open Questions / Todos:
not 100% how to handle changelog and versioning, as well as
setup.cfgandsetup.pyfiles. Took some educated guesses and basically just borrowed the structure of other integrations as a guide, let me know if I've botched anything hereTried to attach licenses everywhere I could but lmk if I've missed anything.
this opentelemetry-ext-pymemcache implementation differs a bit from dd-trace-py, in that dd-trace-py replaces the entire
Clientwith a wrapped client, whereas opentelemetry-ext-pymemcache is only wrapping specific methods on the originalClient. The dd-trace-py approach relies onPin, which isn't a construct available in opentelemetry-python afaik. There were a few gotchas with this approach (like ensuring methods aren't patched multiple times) but overall it behaves the same.Tried to write the tests as closely as I could to the originals in dd-trace-py but was not super sure how to test some of config/client setting stuff, so left that out for now.
Example Usage:
Thanks for taking a look, happy to help however i can and address any feedback.