Skip to content

Commit 0059b27

Browse files
DB and HTTP semantic convention stability migration for Redis instrumentation (#4370)
1 parent 734429e commit 0059b27

8 files changed

Lines changed: 1127 additions & 39 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9393
([#4049](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4049))
9494
- `opentelemetry-instrumentation-sqlalchemy`: implement new semantic convention opt-in migration
9595
([#4110](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4110))
96+
- `opentelemetry-instrumentation`: Add experimental metrics attributes Labeler utility
97+
([#4288](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4288))
98+
- `opentelemetry-instrumentation-redis`: implement new semantic convention opt-in migration
99+
([#4370](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4370))
96100

97101
### Fixed
98102

instrumentation/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
| [opentelemetry-instrumentation-pymssql](./opentelemetry-instrumentation-pymssql) | pymssql >= 2.1.5, < 3 | No | development
3939
| [opentelemetry-instrumentation-pymysql](./opentelemetry-instrumentation-pymysql) | PyMySQL < 2 | No | development
4040
| [opentelemetry-instrumentation-pyramid](./opentelemetry-instrumentation-pyramid) | pyramid >= 1.7 | Yes | migration
41-
| [opentelemetry-instrumentation-redis](./opentelemetry-instrumentation-redis) | redis >= 2.6 | No | development
41+
| [opentelemetry-instrumentation-redis](./opentelemetry-instrumentation-redis) | redis >= 2.6 | No | migration
4242
| [opentelemetry-instrumentation-remoulade](./opentelemetry-instrumentation-remoulade) | remoulade >= 0.50 | No | development
4343
| [opentelemetry-instrumentation-requests](./opentelemetry-instrumentation-requests) | requests ~= 2.0 | Yes | migration
4444
| [opentelemetry-instrumentation-sqlalchemy](./opentelemetry-instrumentation-sqlalchemy) | sqlalchemy >= 1.0.0, < 2.1.0 | Yes | migration

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

Lines changed: 126 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,13 @@ def response_hook(span, instance, response):
141141
from wrapt import wrap_function_wrapper
142142

143143
from opentelemetry import trace
144+
from opentelemetry.instrumentation._semconv import (
145+
_get_schema_url_for_signal_types,
146+
_get_semconv_opt_in_modes,
147+
_OpenTelemetrySemanticConventionStability,
148+
_OpenTelemetryStabilitySignalType,
149+
_set_db_statement,
150+
)
144151
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
145152
from opentelemetry.instrumentation.redis.package import _instruments
146153
from opentelemetry.instrumentation.redis.util import (
@@ -156,9 +163,6 @@ def response_hook(span, instance, response):
156163
is_instrumentation_enabled,
157164
unwrap,
158165
)
159-
from opentelemetry.semconv._incubating.attributes.db_attributes import (
160-
DB_STATEMENT,
161-
)
162166
from opentelemetry.trace import (
163167
StatusCode,
164168
Tracer,
@@ -211,6 +215,19 @@ def _traced_execute_factory(
211215
request_hook: RequestHook | None = None,
212216
response_hook: ResponseHook | None = None,
213217
):
218+
sem_conv_opt_in_modes = _get_semconv_opt_in_modes(
219+
(
220+
_OpenTelemetryStabilitySignalType.DATABASE,
221+
_OpenTelemetryStabilitySignalType.HTTP,
222+
)
223+
)
224+
db_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
225+
_OpenTelemetryStabilitySignalType.DATABASE
226+
]
227+
http_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
228+
_OpenTelemetryStabilitySignalType.HTTP
229+
]
230+
214231
def _traced_execute_command(
215232
func: Callable[..., R],
216233
instance: RedisInstance,
@@ -226,9 +243,20 @@ def _traced_execute_command(
226243
name, kind=trace.SpanKind.CLIENT
227244
) as span:
228245
if span.is_recording():
229-
span.set_attribute(DB_STATEMENT, query)
230-
_set_connection_attributes(span, instance)
231-
span.set_attribute("db.redis.args_length", len(args))
246+
span_attrs = {}
247+
_set_db_statement(span_attrs, query, db_sem_conv_opt_in_mode)
248+
span_attrs["db.redis.args_length"] = len(args)
249+
250+
# Set all DB attributes
251+
for key, value in span_attrs.items():
252+
span.set_attribute(key, value)
253+
254+
_set_connection_attributes(
255+
span,
256+
instance,
257+
db_sem_conv_opt_in_mode,
258+
http_sem_conv_opt_in_mode,
259+
)
232260
if span.name == "redis.create_index":
233261
_add_create_attributes(span, args)
234262
if callable(request_hook):
@@ -249,6 +277,19 @@ def _traced_execute_pipeline_factory(
249277
request_hook: RequestHook | None = None,
250278
response_hook: ResponseHook | None = None,
251279
):
280+
sem_conv_opt_in_modes = _get_semconv_opt_in_modes(
281+
(
282+
_OpenTelemetryStabilitySignalType.DATABASE,
283+
_OpenTelemetryStabilitySignalType.HTTP,
284+
)
285+
)
286+
db_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
287+
_OpenTelemetryStabilitySignalType.DATABASE
288+
]
289+
http_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
290+
_OpenTelemetryStabilitySignalType.HTTP
291+
]
292+
252293
def _traced_execute_pipeline(
253294
func: Callable[..., R],
254295
instance: PipelineInstance,
@@ -268,10 +309,21 @@ def _traced_execute_pipeline(
268309
span_name, kind=trace.SpanKind.CLIENT
269310
) as span:
270311
if span.is_recording():
271-
span.set_attribute(DB_STATEMENT, resource)
272-
_set_connection_attributes(span, instance)
273-
span.set_attribute(
274-
"db.redis.pipeline_length", len(command_stack)
312+
span_attrs = {}
313+
_set_db_statement(
314+
span_attrs, resource, db_sem_conv_opt_in_mode
315+
)
316+
span_attrs["db.redis.pipeline_length"] = len(command_stack)
317+
318+
# Set all DB attributes
319+
for key, value in span_attrs.items():
320+
span.set_attribute(key, value)
321+
322+
_set_connection_attributes(
323+
span,
324+
instance,
325+
db_sem_conv_opt_in_mode,
326+
http_sem_conv_opt_in_mode,
275327
)
276328

277329
response = None
@@ -297,6 +349,19 @@ def _async_traced_execute_factory(
297349
request_hook: RequestHook | None = None,
298350
response_hook: ResponseHook | None = None,
299351
):
352+
sem_conv_opt_in_modes = _get_semconv_opt_in_modes(
353+
(
354+
_OpenTelemetryStabilitySignalType.DATABASE,
355+
_OpenTelemetryStabilitySignalType.HTTP,
356+
)
357+
)
358+
db_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
359+
_OpenTelemetryStabilitySignalType.DATABASE
360+
]
361+
http_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
362+
_OpenTelemetryStabilitySignalType.HTTP
363+
]
364+
300365
async def _async_traced_execute_command(
301366
func: Callable[..., Awaitable[R]],
302367
instance: AsyncRedisInstance,
@@ -313,9 +378,20 @@ async def _async_traced_execute_command(
313378
name, kind=trace.SpanKind.CLIENT
314379
) as span:
315380
if span.is_recording():
316-
span.set_attribute(DB_STATEMENT, query)
317-
_set_connection_attributes(span, instance)
318-
span.set_attribute("db.redis.args_length", len(args))
381+
span_attrs = {}
382+
_set_db_statement(span_attrs, query, db_sem_conv_opt_in_mode)
383+
span_attrs["db.redis.args_length"] = len(args)
384+
385+
# Set all DB attributes
386+
for key, value in span_attrs.items():
387+
span.set_attribute(key, value)
388+
389+
_set_connection_attributes(
390+
span,
391+
instance,
392+
db_sem_conv_opt_in_mode,
393+
http_sem_conv_opt_in_mode,
394+
)
319395
if callable(request_hook):
320396
request_hook(span, instance, args, kwargs)
321397
response = await func(*args, **kwargs)
@@ -331,6 +407,19 @@ def _async_traced_execute_pipeline_factory(
331407
request_hook: RequestHook | None = None,
332408
response_hook: ResponseHook | None = None,
333409
):
410+
sem_conv_opt_in_modes = _get_semconv_opt_in_modes(
411+
(
412+
_OpenTelemetryStabilitySignalType.DATABASE,
413+
_OpenTelemetryStabilitySignalType.HTTP,
414+
)
415+
)
416+
db_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
417+
_OpenTelemetryStabilitySignalType.DATABASE
418+
]
419+
http_sem_conv_opt_in_mode = sem_conv_opt_in_modes[
420+
_OpenTelemetryStabilitySignalType.HTTP
421+
]
422+
334423
async def _async_traced_execute_pipeline(
335424
func: Callable[..., Awaitable[R]],
336425
instance: AsyncPipelineInstance,
@@ -352,10 +441,21 @@ async def _async_traced_execute_pipeline(
352441
span_name, kind=trace.SpanKind.CLIENT
353442
) as span:
354443
if span.is_recording():
355-
span.set_attribute(DB_STATEMENT, resource)
356-
_set_connection_attributes(span, instance)
357-
span.set_attribute(
358-
"db.redis.pipeline_length", len(command_stack)
444+
span_attrs = {}
445+
_set_db_statement(
446+
span_attrs, resource, db_sem_conv_opt_in_mode
447+
)
448+
span_attrs["db.redis.pipeline_length"] = len(command_stack)
449+
450+
# Set all DB attributes
451+
for key, value in span_attrs.items():
452+
span.set_attribute(key, value)
453+
454+
_set_connection_attributes(
455+
span,
456+
instance,
457+
db_sem_conv_opt_in_mode,
458+
http_sem_conv_opt_in_mode,
359459
)
360460

361461
response = None
@@ -529,12 +629,20 @@ def _pipeline_wrapper(func, instance, args, kwargs):
529629
class RedisInstrumentor(BaseInstrumentor):
530630
@staticmethod
531631
def _get_tracer(**kwargs):
632+
# Initialize semantic conventions opt-in if needed
633+
_OpenTelemetrySemanticConventionStability._initialize()
634+
# Redis instrumentation supports both DATABASE and HTTP signal types
635+
signal_types = [
636+
_OpenTelemetryStabilitySignalType.DATABASE,
637+
_OpenTelemetryStabilitySignalType.HTTP,
638+
]
639+
532640
tracer_provider = kwargs.get("tracer_provider")
533641
return get_tracer(
534642
__name__,
535643
__version__,
536644
tracer_provider=tracer_provider,
537-
schema_url="https://opentelemetry.io/schemas/1.11.0",
645+
schema_url=_get_schema_url_for_signal_types(signal_types),
538646
)
539647

540648
def instrument(

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@
33

44

55
_instruments = ("redis >= 2.6",)
6+
7+
_semconv_status = "migration"

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

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99

1010
from typing import TYPE_CHECKING, Any
1111

12-
from opentelemetry.semconv._incubating.attributes.db_attributes import (
13-
DB_REDIS_DATABASE_INDEX,
14-
DB_SYSTEM,
12+
from opentelemetry.instrumentation._semconv import (
13+
_set_db_redis_database_index,
14+
_set_db_system,
15+
_set_http_net_peer_name_client,
16+
_set_http_peer_port_client,
17+
_set_net_transport,
1518
)
16-
from opentelemetry.semconv._incubating.attributes.net_attributes import (
17-
NET_PEER_NAME,
18-
NET_PEER_PORT,
19-
NET_TRANSPORT,
19+
from opentelemetry.semconv.attributes.network_attributes import (
20+
NetworkTransportValues,
2021
)
2122
from opentelemetry.semconv.trace import (
2223
DbSystemValues,
@@ -35,20 +36,44 @@
3536
_FIELD_TYPES = ["NUMERIC", "TEXT", "GEO", "TAG", "VECTOR"]
3637

3738

38-
def _extract_conn_attributes(conn_kwargs):
39+
def _extract_conn_attributes(
40+
conn_kwargs, db_sem_conv_opt_in_mode, http_sem_conv_opt_in_mode
41+
):
3942
"""Transform redis conn info into dict"""
40-
attributes = {
41-
DB_SYSTEM: DbSystemValues.REDIS.value,
42-
}
43+
attributes = {}
44+
_set_db_system(
45+
attributes, DbSystemValues.REDIS.value, db_sem_conv_opt_in_mode
46+
)
47+
4348
db = conn_kwargs.get("db", 0)
44-
attributes[DB_REDIS_DATABASE_INDEX] = db
49+
_set_db_redis_database_index(attributes, db, db_sem_conv_opt_in_mode)
4550
if "path" in conn_kwargs:
46-
attributes[NET_PEER_NAME] = conn_kwargs.get("path", "")
47-
attributes[NET_TRANSPORT] = NetTransportValues.OTHER.value
51+
_set_http_net_peer_name_client(
52+
attributes, conn_kwargs.get("path", ""), http_sem_conv_opt_in_mode
53+
)
54+
_set_net_transport(
55+
attributes,
56+
NetTransportValues.OTHER.value,
57+
NetworkTransportValues.UNIX.value,
58+
http_sem_conv_opt_in_mode,
59+
)
4860
else:
49-
attributes[NET_PEER_NAME] = conn_kwargs.get("host", "localhost")
50-
attributes[NET_PEER_PORT] = conn_kwargs.get("port", 6379)
51-
attributes[NET_TRANSPORT] = NetTransportValues.IP_TCP.value
61+
_set_http_net_peer_name_client(
62+
attributes,
63+
conn_kwargs.get("host", "localhost"),
64+
http_sem_conv_opt_in_mode,
65+
)
66+
_set_http_peer_port_client(
67+
attributes,
68+
conn_kwargs.get("port", 6379),
69+
http_sem_conv_opt_in_mode,
70+
)
71+
_set_net_transport(
72+
attributes,
73+
NetTransportValues.IP_TCP.value,
74+
NetworkTransportValues.TCP.value,
75+
http_sem_conv_opt_in_mode,
76+
)
5277

5378
return attributes
5479

@@ -88,12 +113,17 @@ def _value_or_none(values, n):
88113

89114

90115
def _set_connection_attributes(
91-
span: Span, conn: RedisInstance | AsyncRedisInstance
116+
span: Span,
117+
conn: RedisInstance | AsyncRedisInstance,
118+
db_sem_conv_opt_in_mode,
119+
http_sem_conv_opt_in_mode,
92120
) -> None:
93121
if not span.is_recording() or not hasattr(conn, "connection_pool"):
94122
return
95123
for key, value in _extract_conn_attributes(
96-
conn.connection_pool.connection_kwargs
124+
conn.connection_pool.connection_kwargs,
125+
db_sem_conv_opt_in_mode,
126+
http_sem_conv_opt_in_mode,
97127
).items():
98128
span.set_attribute(key, value)
99129

0 commit comments

Comments
 (0)