Skip to content

Commit 8b72ee1

Browse files
adding reqeust hooks and documentation
1 parent 7fc376e commit 8b72ee1

File tree

3 files changed

+78
-3
lines changed

3 files changed

+78
-3
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.5.0-0.24b0...HEAD)
99

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

1414
### Changed

instrumentation/opentelemetry-instrumentation-redis/src/opentelemetry/instrumentation/redis/__init__.py

+47-2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,38 @@
3838
client = redis.StrictRedis(host="localhost", port=6379)
3939
client.get("my-key")
4040
41+
The `instrument` method accepts the following keyword args:
42+
43+
tracer_provider (TracerProvider) - an optional tracer provider
44+
request_hook (Callable) - a function with extra user-defined logic to be performed before performing the request
45+
this function signature is:
46+
def request_hook(span: Span, instance: redis.connection.Connection, args, kwargs) -> None
47+
response_hook (Callable) - a function with extra user-defined logic to be performed after performing the request
48+
this function signature is:
49+
def response_hook(span: Span, instance: redis.connection.Connection, response) -> None
50+
51+
for example:
52+
53+
.. code: python
54+
55+
from opentelemetry.instrumentation.redis import RedisInstrumentor
56+
import redis
57+
58+
def request_hook(span, instance, args, kwargs):
59+
if span and span.is_recording():
60+
span.set_attribute("custom_user_attribute_from_request_hook", "some-value")
61+
62+
def response_hook(span, instance, response):
63+
if span and span.is_recording():
64+
span.set_attribute("custom_user_attribute_from_response_hook", "some-value")
65+
66+
# Instrument redis with hooks
67+
RedisInstrumentor().instrument(request_hook=request_hook, response_hook=response_hook)
68+
69+
# This will report a span with the default settings and the custom attributes added from the hooks
70+
client = redis.StrictRedis(host="localhost", port=6379)
71+
client.get("my-key")
72+
4173
API
4274
---
4375
"""
@@ -61,6 +93,11 @@
6193

6294
_DEFAULT_SERVICE = "redis"
6395

96+
_RequestHookT = typing.Optional[
97+
typing.Callable[
98+
[Span, redis.connection.Connection, typing.List, typing.Dict], None
99+
]
100+
]
64101
_ResponseHookT = typing.Optional[
65102
typing.Callable[[Span, redis.connection.Connection, Any], None]
66103
]
@@ -76,7 +113,9 @@ def _set_connection_attributes(span, conn):
76113

77114

78115
def _instrument(
79-
tracer, response_hook: _ResponseHookT = None,
116+
tracer,
117+
request_hook: _RequestHookT = None,
118+
response_hook: _ResponseHookT = None,
80119
):
81120
def _traced_execute_command(func, instance, args, kwargs):
82121
query = _format_command_args(args)
@@ -92,6 +131,8 @@ def _traced_execute_command(func, instance, args, kwargs):
92131
span.set_attribute(SpanAttributes.DB_STATEMENT, query)
93132
_set_connection_attributes(span, instance)
94133
span.set_attribute("db.redis.args_length", len(args))
134+
if callable(request_hook):
135+
request_hook(span, instance, args, kwargs)
95136
response = func(*args, **kwargs)
96137
if callable(response_hook):
97138
response_hook(span, instance, response)
@@ -155,7 +196,11 @@ def _instrument(self, **kwargs):
155196
tracer = trace.get_tracer(
156197
__name__, __version__, tracer_provider=tracer_provider
157198
)
158-
_instrument(tracer, response_hook=kwargs.get("response_hook"))
199+
_instrument(
200+
tracer,
201+
request_hook=kwargs.get("request_hook"),
202+
response_hook=kwargs.get("response_hook"),
203+
)
159204

160205
def _uninstrument(self, **kwargs):
161206
if redis.VERSION < (3, 0, 0):

instrumentation/opentelemetry-instrumentation-redis/tests/test_redis.py

+30
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,33 @@ def response_hook(span, conn, response):
111111
self.assertEqual(
112112
span.attributes.get(response_attribute_name), test_value
113113
)
114+
115+
def test_request_hook(self):
116+
redis_client = redis.Redis()
117+
connection = redis.connection.Connection()
118+
redis_client.connection = connection
119+
120+
custom_attribute_name = "my.request.attribute"
121+
122+
def request_hook(span, conn, args, kwargs):
123+
if span and span.is_recording():
124+
span.set_attribute(custom_attribute_name, args[0])
125+
126+
RedisInstrumentor().uninstrument()
127+
RedisInstrumentor().instrument(
128+
tracer_provider=self.tracer_provider, request_hook=request_hook
129+
)
130+
131+
test_value = "test_value"
132+
133+
with mock.patch.object(connection, "send_command"):
134+
with mock.patch.object(
135+
redis_client, "parse_response", return_value=test_value
136+
):
137+
redis_client.get("key")
138+
139+
spans = self.memory_exporter.get_finished_spans()
140+
self.assertEqual(len(spans), 1)
141+
142+
span = spans[0]
143+
self.assertEqual(span.attributes.get(custom_attribute_name), "GET")

0 commit comments

Comments
 (0)