Skip to content

Commit 0918232

Browse files
committed
Update documentation with the client method
1 parent be3ca10 commit 0918232

File tree

5 files changed

+165
-60
lines changed

5 files changed

+165
-60
lines changed

docs/conf.py

+1
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

docs/instrumentation/redis/redis.rst

+6-3
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

+3-1
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

+149-50
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.
60+
61+
Instrument Single Client
62+
------------------------
5863
59-
tracer_provider (TracerProvider) - an optional tracer provider
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.
6067
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
68+
.. code:: python
69+
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")
6381
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
82+
# This will not have a span
83+
not_instrumented_client.get("my-key")
6684
67-
for example:
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` .
6889
69-
.. code: python
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
"""
@@ -111,7 +134,13 @@ def response_hook(span, instance, response):
111134
from opentelemetry.instrumentation.redis.version import __version__
112135
from opentelemetry.instrumentation.utils import unwrap
113136
from opentelemetry.semconv.trace import SpanAttributes
114-
from opentelemetry.trace import Span, StatusCode, Tracer
137+
from opentelemetry.trace import (
138+
Span,
139+
StatusCode,
140+
Tracer,
141+
TracerProvider,
142+
get_tracer,
143+
)
115144

116145
if TYPE_CHECKING:
117146
from typing import Awaitable, TypeVar
@@ -122,10 +151,10 @@ def response_hook(span, instance, response):
122151
import redis.cluster
123152
import redis.connection
124153

125-
_RequestHookT = Callable[
154+
RequestHook = Callable[
126155
[Span, redis.connection.Connection, list[Any], dict[str, Any]], None
127156
]
128-
_ResponseHookT = Callable[[Span, redis.connection.Connection, Any], None]
157+
ResponseHook = Callable[[Span, redis.connection.Connection, Any], None]
129158

130159
AsyncPipelineInstance = TypeVar(
131160
"AsyncPipelineInstance",
@@ -148,6 +177,7 @@ def response_hook(span, instance, response):
148177

149178
_DEFAULT_SERVICE = "redis"
150179
_logger = logging.getLogger(__name__)
180+
assert hasattr(redis, "VERSION")
151181

152182
_REDIS_ASYNCIO_VERSION = (4, 2, 0)
153183
_REDIS_CLUSTER_VERSION = (4, 1, 0)
@@ -281,9 +311,9 @@ def _build_span_meta_data_for_pipeline(
281311

282312

283313
def _traced_execute_factory(
284-
tracer,
285-
request_hook: _RequestHookT = None,
286-
response_hook: _ResponseHookT = None,
314+
tracer: Tracer,
315+
request_hook: RequestHook | None = None,
316+
response_hook: ResponseHook | None = None,
287317
):
288318
def _traced_execute_command(
289319
func: Callable[..., R],
@@ -316,9 +346,9 @@ def _traced_execute_command(
316346

317347

318348
def _traced_execute_pipeline_factory(
319-
tracer,
320-
request_hook: _RequestHookT = None,
321-
response_hook: _ResponseHookT = None,
349+
tracer: Tracer,
350+
request_hook: RequestHook | None = None,
351+
response_hook: ResponseHook | None = None,
322352
):
323353
def _traced_execute_pipeline(
324354
func: Callable[..., R],
@@ -361,9 +391,9 @@ def _traced_execute_pipeline(
361391

362392

363393
def _async_traced_execute_factory(
364-
tracer,
365-
request_hook: _RequestHookT = None,
366-
response_hook: _ResponseHookT = None,
394+
tracer: Tracer,
395+
request_hook: RequestHook | None = None,
396+
response_hook: ResponseHook | None = None,
367397
):
368398
async def _async_traced_execute_command(
369399
func: Callable[..., Awaitable[R]],
@@ -392,9 +422,9 @@ async def _async_traced_execute_command(
392422

393423

394424
def _async_traced_execute_pipeline_factory(
395-
tracer,
396-
request_hook: _RequestHookT = None,
397-
response_hook: _ResponseHookT = None,
425+
tracer: Tracer,
426+
request_hook: RequestHook | None = None,
427+
response_hook: ResponseHook | None = None,
398428
):
399429
async def _async_traced_execute_pipeline(
400430
func: Callable[..., Awaitable[R]],
@@ -441,8 +471,8 @@ async def _async_traced_execute_pipeline(
441471
# pylint: disable=R0915
442472
def _instrument(
443473
tracer: Tracer,
444-
request_hook: _RequestHookT | None = None,
445-
response_hook: _ResponseHookT | None = None,
474+
request_hook: RequestHook | None = None,
475+
response_hook: ResponseHook | None = None,
446476
):
447477
_traced_execute_command = _traced_execute_factory(
448478
tracer, request_hook, response_hook
@@ -513,11 +543,11 @@ def _instrument(
513543
)
514544

515545

516-
def _instrument_connection(
546+
def _instrument_client(
517547
client,
518-
tracer,
519-
request_hook: _RequestHookT = None,
520-
response_hook: _ResponseHookT = None,
548+
tracer: Tracer,
549+
request_hook: RequestHook | None = None,
550+
response_hook: ResponseHook | None = None,
521551
):
522552
# first, handle async clients and cluster clients
523553
_async_traced_execute = _async_traced_execute_factory(
@@ -589,23 +619,48 @@ def _pipeline_wrapper(func, instance, args, kwargs):
589619

590620

591621
class RedisInstrumentor(BaseInstrumentor):
592-
"""An instrumentor for Redis.
593-
594-
See `BaseInstrumentor`
595-
"""
596-
597622
@staticmethod
598623
def _get_tracer(**kwargs):
599624
tracer_provider = kwargs.get("tracer_provider")
600-
return trace.get_tracer(
625+
return get_tracer(
601626
__name__,
602627
__version__,
603628
tracer_provider=tracer_provider,
604629
schema_url="https://opentelemetry.io/schemas/1.11.0",
605630
)
606631

607-
def instrumentation_dependencies(self) -> Collection[str]:
608-
return _instruments
632+
def instrument(
633+
self,
634+
tracer_provider: TracerProvider | None = None,
635+
request_hook: RequestHook | None = None,
636+
response_hook: ResponseHook | None = None,
637+
**kwargs,
638+
):
639+
"""Instruments all Redis/StrictRedis/RedisCluster and async client instances.
640+
641+
Args:
642+
tracer_provider: A TracerProvider, defaults to global.
643+
request_hook:
644+
a function with extra user-defined logic to run before performing the request.
645+
646+
The ``args`` is a tuple, where items are
647+
command arguments. For example ``client.set("mykey", "value", ex=5)`` would
648+
have ``args`` as ``('SET', 'mykey', 'value', 'EX', 5)``.
649+
650+
The ``kwargs`` represents occasional ``options`` passed by redis. For example,
651+
if you use ``client.set("mykey", "value", get=True)``, the ``kwargs`` would be
652+
``{'get': True}``.
653+
response_hook:
654+
a function with extra user-defined logic to run after the request is complete.
655+
656+
The ``args`` represents the response.
657+
"""
658+
super().instrument(
659+
tracer_provider=tracer_provider,
660+
request_hook=request_hook,
661+
response_hook=response_hook,
662+
**kwargs,
663+
)
609664

610665
def _instrument(self, **kwargs: Any):
611666
"""Instruments the redis module
@@ -652,13 +707,42 @@ def _uninstrument(self, **kwargs: Any):
652707
unwrap(redis.asyncio.cluster.ClusterPipeline, "execute")
653708

654709
@staticmethod
655-
def instrument_connection(
656-
client, tracer_provider: None, request_hook=None, response_hook=None
710+
def instrument_client(
711+
client: redis.StrictRedis
712+
| redis.Redis
713+
| redis.asyncio.Redis
714+
| redis.cluster.RedisCluster
715+
| redis.asyncio.cluster.RedisCluster,
716+
tracer_provider: TracerProvider | None = None,
717+
request_hook: RequestHook | None = None,
718+
response_hook: ResponseHook | None = None,
657719
):
720+
"""Instrument the provided Redis Client. The client can be sync or async.
721+
Cluster client is also supported.
722+
723+
Args:
724+
client: The redis client.
725+
tracer_provider: A TracerProvider, defaults to global.
726+
request_hook: a function with extra user-defined logic to run before
727+
performing the request.
728+
729+
The ``args`` is a tuple, where items are
730+
command arguments. For example ``client.set("mykey", "value", ex=5)`` would
731+
have ``args`` as ``('SET', 'mykey', 'value', 'EX', 5)``.
732+
733+
The ``kwargs`` represents occasional ``options`` passed by redis. For example,
734+
if you use ``client.set("mykey", "value", get=True)``, the ``kwargs`` would be
735+
``{'get': True}``.
736+
737+
response_hook: a function with extra user-defined logic to run after
738+
the request is complete.
739+
740+
The ``args`` represents the response.
741+
"""
658742
if not hasattr(client, INSTRUMENTATION_ATTR):
659743
setattr(client, INSTRUMENTATION_ATTR, False)
660744
if not getattr(client, INSTRUMENTATION_ATTR):
661-
_instrument_connection(
745+
_instrument_client(
662746
client,
663747
RedisInstrumentor._get_tracer(tracer_provider=tracer_provider),
664748
request_hook=request_hook,
@@ -671,7 +755,18 @@ def instrument_connection(
671755
)
672756

673757
@staticmethod
674-
def uninstrument_connection(client):
758+
def uninstrument_client(
759+
client: redis.StrictRedis
760+
| redis.Redis
761+
| redis.asyncio.Redis
762+
| redis.cluster.RedisCluster
763+
| redis.asyncio.cluster.RedisCluster,
764+
):
765+
"""Disables instrumentation for the given client instance
766+
767+
Args:
768+
client: The redis client
769+
"""
675770
if getattr(client, INSTRUMENTATION_ATTR):
676771
# for all clients we need to unwrap execute_command and pipeline functions
677772
unwrap(client, "execute_command")
@@ -685,3 +780,7 @@ def uninstrument_connection(client):
685780
"Attempting to un-instrument Redis connection that wasn't instrumented"
686781
)
687782
return
783+
784+
def instrumentation_dependencies(self) -> Collection[str]:
785+
"""Return a list of python packages with versions that the will be instrumented."""
786+
return _instruments

0 commit comments

Comments
 (0)