Skip to content

Commit 11f1ed3

Browse files
author
OlegZv
committed
Update documentation with the client method
1 parent 9432bda commit 11f1ed3

File tree

5 files changed

+158
-55
lines changed

5 files changed

+158
-55
lines changed

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
"https://opentelemetry-python.readthedocs.io/en/latest/",
123123
None,
124124
),
125+
"redis": ("https://redis-py.readthedocs.io/en/latest/", None),
125126
}
126127

127128
# http://www.sphinx-doc.org/en/master/config.html#confval-nitpicky
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
OpenTelemetry Redis Instrumentation
2-
===================================
1+
.. include:: ../../../instrumentation/opentelemetry-instrumentation-redis/README.rst
2+
:end-before: References
3+
4+
Usage
5+
-----
36

47
.. automodule:: opentelemetry.instrumentation.redis
58
:members:
69
:undoc-members:
7-
:show-inheritance:
10+
:show-inheritance:

instrumentation/opentelemetry-instrumentation-httpx/README.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@ Instrumenting single clients
4343
****************************
4444

4545
If you only want to instrument requests for specific client instances, you can
46-
use the `instrument_client` method.
46+
use the `instrument_client`_ method.
4747

4848

49+
.. _instrument_client: #opentelemetry.instrumentation.httpx.HTTPXClientInstrumentor.instrument_client
50+
4951
.. code-block:: python
5052
5153
import httpx

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

Lines changed: 142 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,14 @@
1515
"""
1616
Instrument `redis`_ to report Redis queries.
1717
18-
There are two options for instrumenting code. The first option is to use the
19-
``opentelemetry-instrument`` executable which will automatically
20-
instrument your Redis client. The second is to programmatically enable
21-
instrumentation via the following code:
22-
2318
.. _redis: https://pypi.org/project/redis/
2419
25-
Usage
26-
-----
20+
21+
Instrument All Clients
22+
----------------------
23+
24+
The easiest way to instrument all redis client instances is by
25+
``RedisInstrumentor().instrument()``:
2726
2827
.. code:: python
2928
@@ -38,7 +37,7 @@
3837
client = redis.StrictRedis(host="localhost", port=6379)
3938
client.get("my-key")
4039
41-
Async Redis clients (i.e. redis.asyncio.Redis) are also instrumented in the same way:
40+
Async Redis clients (i.e. ``redis.asyncio.Redis``) are also instrumented in the same way:
4241
4342
.. code:: python
4443
@@ -54,19 +53,44 @@ async def redis_get():
5453
client = redis.asyncio.Redis(host="localhost", port=6379)
5554
await client.get("my-key")
5655
57-
The `instrument` method accepts the following keyword args:
56+
.. note::
57+
Calling the ``instrument`` method will instrument the client classes, so any client
58+
created after the ``instrument`` call will be instrumented. To instrument only a
59+
single client, use :func:`RedisInstrumentor.instrument_client` method.
5860
59-
tracer_provider (TracerProvider) - an optional tracer provider
61+
Instrument Single Client
62+
------------------------
6063
61-
request_hook (Callable) - a function with extra user-defined logic to be performed before performing the request
62-
this function signature is: def request_hook(span: Span, instance: redis.connection.Connection, args, kwargs) -> None
64+
The :func:`RedisInstrumentor.instrument_client` can instrument a connection instance. This is useful when there are multiple clients with a different redis database index.
65+
Or, you might have a different connection pool used for an application function you
66+
don't want instrumented.
6367
64-
response_hook (Callable) - a function with extra user-defined logic to be performed after performing the request
65-
this function signature is: def response_hook(span: Span, instance: redis.connection.Connection, response) -> None
68+
.. code:: python
6669
67-
for example:
70+
from opentelemetry.instrumentation.redis import RedisInstrumentor
71+
import redis
72+
73+
instrumented_client = redis.Redis()
74+
not_instrumented_client = redis.Redis()
75+
76+
# Instrument redis
77+
RedisInstrumentor.instrument_client(client=instrumented_client)
78+
79+
# This will report a span with the default settings
80+
instrumented_client.get("my-key")
6881
69-
.. code: python
82+
# This will not have a span
83+
not_instrumented_client.get("my-key")
84+
85+
.. warning::
86+
All client instances created after calling ``RedisInstrumentor().instrument`` will
87+
be instrumented. To avoid instrumenting all clients, use
88+
:func:`RedisInstrumentor.instrument_client` .
89+
90+
Request/Response Hooks
91+
----------------------
92+
93+
.. code:: python
7094
7195
from opentelemetry.instrumentation.redis import RedisInstrumentor
7296
import redis
@@ -86,7 +110,6 @@ def response_hook(span, instance, response):
86110
client = redis.StrictRedis(host="localhost", port=6379)
87111
client.get("my-key")
88112
89-
90113
API
91114
---
92115
"""
@@ -110,16 +133,16 @@ def response_hook(span, instance, response):
110133
from opentelemetry.instrumentation.redis.version import __version__
111134
from opentelemetry.instrumentation.utils import unwrap
112135
from opentelemetry.semconv.trace import SpanAttributes
113-
from opentelemetry.trace import Span, StatusCode
136+
from opentelemetry.trace import Span, StatusCode, TracerProvider, get_tracer
114137

115138
_DEFAULT_SERVICE = "redis"
116139

117-
_RequestHookT = typing.Optional[
140+
RequestHook = typing.Optional[
118141
typing.Callable[
119-
[Span, redis.connection.Connection, typing.List, typing.Dict], None
142+
[Span, redis.connection.Connection, typing.Tuple, typing.Dict], None
120143
]
121144
]
122-
_ResponseHookT = typing.Optional[
145+
ResponseHook = typing.Optional[
123146
typing.Callable[[Span, redis.connection.Connection, Any], None]
124147
]
125148
_logger = logging.getLogger(__name__)
@@ -252,8 +275,8 @@ def _build_span_meta_data_for_pipeline(instance):
252275

253276
def _traced_execute_factory(
254277
tracer,
255-
request_hook: _RequestHookT = None,
256-
response_hook: _ResponseHookT = None,
278+
request_hook: RequestHook = None,
279+
response_hook: ResponseHook = None,
257280
):
258281
def _traced_execute_command(func, instance, args, kwargs):
259282
query = _format_command_args(args)
@@ -282,8 +305,8 @@ def _traced_execute_command(func, instance, args, kwargs):
282305

283306
def _traced_execute_pipeline_factory(
284307
tracer,
285-
request_hook: _RequestHookT = None,
286-
response_hook: _ResponseHookT = None,
308+
request_hook: RequestHook = None,
309+
response_hook: ResponseHook = None,
287310
):
288311
def _traced_execute_pipeline(func, instance, args, kwargs):
289312
(
@@ -322,8 +345,8 @@ def _traced_execute_pipeline(func, instance, args, kwargs):
322345

323346
def _async_traced_execute_factory(
324347
tracer,
325-
request_hook: _RequestHookT = None,
326-
response_hook: _ResponseHookT = None,
348+
request_hook: RequestHook = None,
349+
response_hook: ResponseHook = None,
327350
):
328351
async def _async_traced_execute_command(func, instance, args, kwargs):
329352
query = _format_command_args(args)
@@ -348,8 +371,8 @@ async def _async_traced_execute_command(func, instance, args, kwargs):
348371

349372
def _async_traced_execute_pipeline_factory(
350373
tracer,
351-
request_hook: _RequestHookT = None,
352-
response_hook: _ResponseHookT = None,
374+
request_hook: RequestHook = None,
375+
response_hook: ResponseHook = None,
353376
):
354377
async def _async_traced_execute_pipeline(func, instance, args, kwargs):
355378
(
@@ -391,8 +414,8 @@ async def _async_traced_execute_pipeline(func, instance, args, kwargs):
391414
# pylint: disable=R0915
392415
def _instrument(
393416
tracer,
394-
request_hook: _RequestHookT = None,
395-
response_hook: _ResponseHookT = None,
417+
request_hook: RequestHook = None,
418+
response_hook: ResponseHook = None,
396419
):
397420
_traced_execute_command = _traced_execute_factory(
398421
tracer, request_hook, response_hook
@@ -463,11 +486,11 @@ def _instrument(
463486
)
464487

465488

466-
def _instrument_connection(
489+
def _instrument_client(
467490
client,
468491
tracer,
469-
request_hook: _RequestHookT = None,
470-
response_hook: _ResponseHookT = None,
492+
request_hook: RequestHook = None,
493+
response_hook: ResponseHook = None,
471494
):
472495
# first, handle async clients and cluster clients
473496
_async_traced_execute = _async_traced_execute_factory(
@@ -539,22 +562,48 @@ def _pipeline_wrapper(func, instance, args, kwargs):
539562

540563

541564
class RedisInstrumentor(BaseInstrumentor):
542-
"""An instrumentor for Redis
543-
See `BaseInstrumentor`
544-
"""
545-
546565
@staticmethod
547566
def _get_tracer(**kwargs):
548567
tracer_provider = kwargs.get("tracer_provider")
549-
return trace.get_tracer(
568+
return get_tracer(
550569
__name__,
551570
__version__,
552571
tracer_provider=tracer_provider,
553572
schema_url="https://opentelemetry.io/schemas/1.11.0",
554573
)
555574

556-
def instrumentation_dependencies(self) -> Collection[str]:
557-
return _instruments
575+
def instrument(
576+
self,
577+
tracer_provider: typing.Optional[TracerProvider] = None,
578+
request_hook: RequestHook = None,
579+
response_hook: ResponseHook = None,
580+
**kwargs,
581+
):
582+
"""Instruments all Redis/StrictRedis/RedisCluster and async client instances.
583+
584+
Args:
585+
tracer_provider: A TracerProvider, defaults to global.
586+
request_hook:
587+
a function with extra user-defined logic to run before performing the request.
588+
589+
The ``args`` is a tuple, where items are
590+
command arguments. For example ``client.set("mykey", "value", ex=5)`` would
591+
have ``args`` as ``('SET', 'mykey', 'value', 'EX', 5)``.
592+
593+
The ``kwargs`` represents occasional ``options`` passed by redis. For example,
594+
if you use ``client.set("mykey", "value", get=True)``, the ``kwargs`` would be
595+
``{'get': True}``.
596+
response_hook:
597+
a function with extra user-defined logic to run after the request is complete.
598+
599+
The ``args`` represents the response.
600+
"""
601+
super().instrument(
602+
tracer_provider=tracer_provider,
603+
request_hook=request_hook,
604+
response_hook=response_hook,
605+
**kwargs,
606+
)
558607

559608
def _instrument(self, **kwargs):
560609
"""Instruments the redis module
@@ -601,13 +650,44 @@ def _uninstrument(self, **kwargs):
601650
unwrap(redis.asyncio.cluster.ClusterPipeline, "execute")
602651

603652
@staticmethod
604-
def instrument_connection(
605-
client, tracer_provider: None, request_hook=None, response_hook=None
653+
def instrument_client(
654+
client: typing.Union[
655+
redis.StrictRedis,
656+
redis.Redis,
657+
redis.asyncio.Redis,
658+
redis.cluster.RedisCluster,
659+
redis.asyncio.cluster.RedisCluster,
660+
],
661+
tracer_provider: typing.Optional[TracerProvider] = None,
662+
request_hook: RequestHook = None,
663+
response_hook: ResponseHook = None,
606664
):
665+
"""Instrument the provided Redis Client. The client can be sync or async.
666+
Cluster client is also supported.
667+
668+
Args:
669+
client: The redis client.
670+
tracer_provider: A TracerProvider, defaults to global.
671+
request_hook: a function with extra user-defined logic to run before
672+
performing the request.
673+
674+
The ``args`` is a tuple, where items are
675+
command arguments. For example ``client.set("mykey", "value", ex=5)`` would
676+
have ``args`` as ``('SET', 'mykey', 'value', 'EX', 5)``.
677+
678+
The ``kwargs`` represents occasional ``options`` passed by redis. For example,
679+
if you use ``client.set("mykey", "value", get=True)``, the ``kwargs`` would be
680+
``{'get': True}``.
681+
682+
response_hook: a function with extra user-defined logic to run after
683+
the request is complete.
684+
685+
The ``args`` represents the response.
686+
"""
607687
if not hasattr(client, INSTRUMENTATION_ATTR):
608688
setattr(client, INSTRUMENTATION_ATTR, False)
609689
if not getattr(client, INSTRUMENTATION_ATTR):
610-
_instrument_connection(
690+
_instrument_client(
611691
client,
612692
RedisInstrumentor._get_tracer(tracer_provider=tracer_provider),
613693
request_hook=request_hook,
@@ -620,7 +700,20 @@ def instrument_connection(
620700
)
621701

622702
@staticmethod
623-
def uninstrument_connection(client):
703+
def uninstrument_client(
704+
client: typing.Union[
705+
redis.StrictRedis,
706+
redis.Redis,
707+
redis.asyncio.Redis,
708+
redis.cluster.RedisCluster,
709+
redis.asyncio.cluster.RedisCluster,
710+
],
711+
):
712+
"""Disables instrumentation for the given client instance
713+
714+
Args:
715+
client: The redis client
716+
"""
624717
if getattr(client, INSTRUMENTATION_ATTR):
625718
# for all clients we need to unwrap execute_command and pipeline functions
626719
unwrap(client, "execute_command")
@@ -634,3 +727,7 @@ def uninstrument_connection(client):
634727
"Attempting to un-instrument Redis connection that wasn't instrumented"
635728
)
636729
return
730+
731+
def instrumentation_dependencies(self) -> Collection[str]:
732+
"""Return a list of python packages with versions that the will be instrumented."""
733+
return _instruments

0 commit comments

Comments
 (0)