Skip to content

Commit

Permalink
adding reqeust hooks and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
ItayGibel-helios committed Sep 14, 2021
1 parent 7fc376e commit 8b72ee1
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 3 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.5.0-0.24b0...HEAD)

### Added
- `opentelemetry-instrumentation-redis` added response_hook callback passed as an argument to the instrument method.
- `opentelemetry-instrumentation-redis` added request_hook and response_hook callbacks passed as arguments to the instrument method.
([#669](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/669))

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,38 @@
client = redis.StrictRedis(host="localhost", port=6379)
client.get("my-key")
The `instrument` method accepts the following keyword args:
tracer_provider (TracerProvider) - an optional tracer provider
request_hook (Callable) - a function with extra user-defined logic to be performed before performing the request
this function signature is:
def request_hook(span: Span, instance: redis.connection.Connection, args, kwargs) -> None
response_hook (Callable) - a function with extra user-defined logic to be performed after performing the request
this function signature is:
def response_hook(span: Span, instance: redis.connection.Connection, response) -> None
for example:
.. code: python
from opentelemetry.instrumentation.redis import RedisInstrumentor
import redis
def request_hook(span, instance, args, kwargs):
if span and span.is_recording():
span.set_attribute("custom_user_attribute_from_request_hook", "some-value")
def response_hook(span, instance, response):
if span and span.is_recording():
span.set_attribute("custom_user_attribute_from_response_hook", "some-value")
# Instrument redis with hooks
RedisInstrumentor().instrument(request_hook=request_hook, response_hook=response_hook)
# This will report a span with the default settings and the custom attributes added from the hooks
client = redis.StrictRedis(host="localhost", port=6379)
client.get("my-key")
API
---
"""
Expand All @@ -61,6 +93,11 @@

_DEFAULT_SERVICE = "redis"

_RequestHookT = typing.Optional[
typing.Callable[
[Span, redis.connection.Connection, typing.List, typing.Dict], None
]
]
_ResponseHookT = typing.Optional[
typing.Callable[[Span, redis.connection.Connection, Any], None]
]
Expand All @@ -76,7 +113,9 @@ def _set_connection_attributes(span, conn):


def _instrument(
tracer, response_hook: _ResponseHookT = None,
tracer,
request_hook: _RequestHookT = None,
response_hook: _ResponseHookT = None,
):
def _traced_execute_command(func, instance, args, kwargs):
query = _format_command_args(args)
Expand All @@ -92,6 +131,8 @@ def _traced_execute_command(func, instance, args, kwargs):
span.set_attribute(SpanAttributes.DB_STATEMENT, query)
_set_connection_attributes(span, instance)
span.set_attribute("db.redis.args_length", len(args))
if callable(request_hook):
request_hook(span, instance, args, kwargs)
response = func(*args, **kwargs)
if callable(response_hook):
response_hook(span, instance, response)
Expand Down Expand Up @@ -155,7 +196,11 @@ def _instrument(self, **kwargs):
tracer = trace.get_tracer(
__name__, __version__, tracer_provider=tracer_provider
)
_instrument(tracer, response_hook=kwargs.get("response_hook"))
_instrument(
tracer,
request_hook=kwargs.get("request_hook"),
response_hook=kwargs.get("response_hook"),
)

def _uninstrument(self, **kwargs):
if redis.VERSION < (3, 0, 0):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,33 @@ def response_hook(span, conn, response):
self.assertEqual(
span.attributes.get(response_attribute_name), test_value
)

def test_request_hook(self):
redis_client = redis.Redis()
connection = redis.connection.Connection()
redis_client.connection = connection

custom_attribute_name = "my.request.attribute"

def request_hook(span, conn, args, kwargs):
if span and span.is_recording():
span.set_attribute(custom_attribute_name, args[0])

RedisInstrumentor().uninstrument()
RedisInstrumentor().instrument(
tracer_provider=self.tracer_provider, request_hook=request_hook
)

test_value = "test_value"

with mock.patch.object(connection, "send_command"):
with mock.patch.object(
redis_client, "parse_response", return_value=test_value
):
redis_client.get("key")

spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 1)

span = spans[0]
self.assertEqual(span.attributes.get(custom_attribute_name), "GET")

0 comments on commit 8b72ee1

Please sign in to comment.