Skip to content
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#4049](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4049))
- `opentelemetry-instrumentation-sqlalchemy`: implement new semantic convention opt-in migration
([#4110](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4110))
- `opentelemetry-instrumentation-redis`: implement new semantic convention opt-in migration
([#4370](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4370))

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion instrumentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
| [opentelemetry-instrumentation-pymssql](./opentelemetry-instrumentation-pymssql) | pymssql >= 2.1.5, < 3 | No | development
| [opentelemetry-instrumentation-pymysql](./opentelemetry-instrumentation-pymysql) | PyMySQL < 2 | No | development
| [opentelemetry-instrumentation-pyramid](./opentelemetry-instrumentation-pyramid) | pyramid >= 1.7 | Yes | migration
| [opentelemetry-instrumentation-redis](./opentelemetry-instrumentation-redis) | redis >= 2.6 | No | development
| [opentelemetry-instrumentation-redis](./opentelemetry-instrumentation-redis) | redis >= 2.6 | No | migration
| [opentelemetry-instrumentation-remoulade](./opentelemetry-instrumentation-remoulade) | remoulade >= 0.50 | No | development
| [opentelemetry-instrumentation-requests](./opentelemetry-instrumentation-requests) | requests ~= 2.0 | Yes | migration
| [opentelemetry-instrumentation-sqlalchemy](./opentelemetry-instrumentation-sqlalchemy) | sqlalchemy >= 1.0.0, < 2.1.0 | Yes | migration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ def response_hook(span, instance, response):
from wrapt import wrap_function_wrapper

from opentelemetry import trace
from opentelemetry.instrumentation._semconv import (
_get_schema_url_for_signal_types,
_OpenTelemetrySemanticConventionStability,
_OpenTelemetryStabilitySignalType,
_set_db_statement,
)
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.instrumentation.redis.package import _instruments
from opentelemetry.instrumentation.redis.util import (
Expand All @@ -167,9 +173,6 @@ def response_hook(span, instance, response):
is_instrumentation_enabled,
unwrap,
)
from opentelemetry.semconv._incubating.attributes.db_attributes import (
DB_STATEMENT,
)
from opentelemetry.trace import (
StatusCode,
Tracer,
Expand Down Expand Up @@ -222,6 +225,15 @@ def _traced_execute_factory(
request_hook: RequestHook | None = None,
response_hook: ResponseHook | None = None,
):
# Get semconv opt-in modes for database and HTTP signal types
_OpenTelemetrySemanticConventionStability._initialize()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are duplicated across all four factories, should we add a helper?

db_sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.DATABASE
)
http_sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.HTTP
)

def _traced_execute_command(
func: Callable[..., R],
instance: RedisInstance,
Expand All @@ -237,9 +249,20 @@ def _traced_execute_command(
name, kind=trace.SpanKind.CLIENT
) as span:
if span.is_recording():
span.set_attribute(DB_STATEMENT, query)
_set_connection_attributes(span, instance)
span.set_attribute("db.redis.args_length", len(args))
span_attrs = {}
_set_db_statement(span_attrs, query, db_sem_conv_opt_in_mode)
span_attrs["db.redis.args_length"] = len(args)

# Set all DB attributes
for key, value in span_attrs.items():
span.set_attribute(key, value)

_set_connection_attributes(
span,
instance,
db_sem_conv_opt_in_mode,
http_sem_conv_opt_in_mode,
)
if span.name == "redis.create_index":
_add_create_attributes(span, args)
if callable(request_hook):
Expand All @@ -260,6 +283,15 @@ def _traced_execute_pipeline_factory(
request_hook: RequestHook | None = None,
response_hook: ResponseHook | None = None,
):
# Get semconv opt-in modes for database and HTTP signal types
_OpenTelemetrySemanticConventionStability._initialize()
db_sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.DATABASE
)
http_sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.HTTP
)

def _traced_execute_pipeline(
func: Callable[..., R],
instance: PipelineInstance,
Expand All @@ -279,10 +311,21 @@ def _traced_execute_pipeline(
span_name, kind=trace.SpanKind.CLIENT
) as span:
if span.is_recording():
span.set_attribute(DB_STATEMENT, resource)
_set_connection_attributes(span, instance)
span.set_attribute(
"db.redis.pipeline_length", len(command_stack)
span_attrs = {}
_set_db_statement(
span_attrs, resource, db_sem_conv_opt_in_mode
)
span_attrs["db.redis.pipeline_length"] = len(command_stack)

# Set all DB attributes
for key, value in span_attrs.items():
span.set_attribute(key, value)

_set_connection_attributes(
span,
instance,
db_sem_conv_opt_in_mode,
http_sem_conv_opt_in_mode,
)

response = None
Expand All @@ -308,6 +351,15 @@ def _async_traced_execute_factory(
request_hook: RequestHook | None = None,
response_hook: ResponseHook | None = None,
):
# Get semconv opt-in modes for database and HTTP signal types
_OpenTelemetrySemanticConventionStability._initialize()
db_sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.DATABASE
)
http_sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.HTTP
)

async def _async_traced_execute_command(
func: Callable[..., Awaitable[R]],
instance: AsyncRedisInstance,
Expand All @@ -324,9 +376,20 @@ async def _async_traced_execute_command(
name, kind=trace.SpanKind.CLIENT
) as span:
if span.is_recording():
span.set_attribute(DB_STATEMENT, query)
_set_connection_attributes(span, instance)
span.set_attribute("db.redis.args_length", len(args))
span_attrs = {}
_set_db_statement(span_attrs, query, db_sem_conv_opt_in_mode)
span_attrs["db.redis.args_length"] = len(args)

# Set all DB attributes
for key, value in span_attrs.items():
span.set_attribute(key, value)

_set_connection_attributes(
span,
instance,
db_sem_conv_opt_in_mode,
http_sem_conv_opt_in_mode,
)
if callable(request_hook):
request_hook(span, instance, args, kwargs)
response = await func(*args, **kwargs)
Expand All @@ -342,6 +405,15 @@ def _async_traced_execute_pipeline_factory(
request_hook: RequestHook | None = None,
response_hook: ResponseHook | None = None,
):
# Get semconv opt-in modes for database and HTTP signal types
_OpenTelemetrySemanticConventionStability._initialize()
db_sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.DATABASE
)
http_sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.HTTP
)

async def _async_traced_execute_pipeline(
func: Callable[..., Awaitable[R]],
instance: AsyncPipelineInstance,
Expand All @@ -363,10 +435,21 @@ async def _async_traced_execute_pipeline(
span_name, kind=trace.SpanKind.CLIENT
) as span:
if span.is_recording():
span.set_attribute(DB_STATEMENT, resource)
_set_connection_attributes(span, instance)
span.set_attribute(
"db.redis.pipeline_length", len(command_stack)
span_attrs = {}
_set_db_statement(
span_attrs, resource, db_sem_conv_opt_in_mode
)
span_attrs["db.redis.pipeline_length"] = len(command_stack)

# Set all DB attributes
for key, value in span_attrs.items():
span.set_attribute(key, value)

_set_connection_attributes(
span,
instance,
db_sem_conv_opt_in_mode,
http_sem_conv_opt_in_mode,
)

response = None
Expand Down Expand Up @@ -540,12 +623,20 @@ def _pipeline_wrapper(func, instance, args, kwargs):
class RedisInstrumentor(BaseInstrumentor):
@staticmethod
def _get_tracer(**kwargs):
# Initialize semantic conventions opt-in if needed
_OpenTelemetrySemanticConventionStability._initialize()
# Redis instrumentation supports both DATABASE and HTTP signal types
signal_types = [
_OpenTelemetryStabilitySignalType.DATABASE,
_OpenTelemetryStabilitySignalType.HTTP,
]

tracer_provider = kwargs.get("tracer_provider")
return get_tracer(
__name__,
__version__,
tracer_provider=tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
schema_url=_get_schema_url_for_signal_types(signal_types),
)

def instrument(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@


_instruments = ("redis >= 2.6",)

_semconv_status = "migration"
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@

from typing import TYPE_CHECKING, Any

from opentelemetry.instrumentation._semconv import (
_set_db_system,
_set_http_net_peer_name_client,
_set_http_peer_port_client,
)
from opentelemetry.semconv._incubating.attributes.db_attributes import (
DB_REDIS_DATABASE_INDEX,
DB_SYSTEM,
)
from opentelemetry.semconv._incubating.attributes.net_attributes import (
NET_PEER_NAME,
NET_PEER_PORT,
NET_TRANSPORT,
)
from opentelemetry.semconv.trace import (
Expand All @@ -46,19 +48,33 @@
_FIELD_TYPES = ["NUMERIC", "TEXT", "GEO", "TAG", "VECTOR"]


def _extract_conn_attributes(conn_kwargs):
def _extract_conn_attributes(
conn_kwargs, db_sem_conv_opt_in_mode, http_sem_conv_opt_in_mode
):
"""Transform redis conn info into dict"""
attributes = {
DB_SYSTEM: DbSystemValues.REDIS.value,
}
attributes = {}
_set_db_system(
attributes, DbSystemValues.REDIS.value, db_sem_conv_opt_in_mode
)

db = conn_kwargs.get("db", 0)
attributes[DB_REDIS_DATABASE_INDEX] = db
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think db.redis.database_index became db.namespace - do we have a set helper that can use old/new/dual write for these?

if "path" in conn_kwargs:
attributes[NET_PEER_NAME] = conn_kwargs.get("path", "")
_set_http_net_peer_name_client(
attributes, conn_kwargs.get("path", ""), http_sem_conv_opt_in_mode
)
attributes[NET_TRANSPORT] = NetTransportValues.OTHER.value
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think net.transport has been deprecated - should we drop this in stable mode?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah. maybe just set if _report_old()

else:
attributes[NET_PEER_NAME] = conn_kwargs.get("host", "localhost")
attributes[NET_PEER_PORT] = conn_kwargs.get("port", 6379)
_set_http_net_peer_name_client(
attributes,
conn_kwargs.get("host", "localhost"),
http_sem_conv_opt_in_mode,
)
_set_http_peer_port_client(
attributes,
conn_kwargs.get("port", 6379),
http_sem_conv_opt_in_mode,
)
attributes[NET_TRANSPORT] = NetTransportValues.IP_TCP.value
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.


return attributes
Expand Down Expand Up @@ -99,12 +115,17 @@ def _value_or_none(values, n):


def _set_connection_attributes(
span: Span, conn: RedisInstance | AsyncRedisInstance
span: Span,
conn: RedisInstance | AsyncRedisInstance,
db_sem_conv_opt_in_mode,
http_sem_conv_opt_in_mode,
) -> None:
if not span.is_recording() or not hasattr(conn, "connection_pool"):
return
for key, value in _extract_conn_attributes(
conn.connection_pool.connection_kwargs
conn.connection_pool.connection_kwargs,
db_sem_conv_opt_in_mode,
http_sem_conv_opt_in_mode,
).items():
span.set_attribute(key, value)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ packaging==24.0
pluggy==1.6.0
py-cpuinfo==9.0.0
pytest==7.4.4
pytest-asyncio==0.23.5
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 1c7e3cc so Python 3.9 tests pass, else RuntimeError: There is no current event loop in thread 'MainThread'.

redis==5.0.1
tomli==2.0.1
typing_extensions==4.12.2
Expand Down
Loading
Loading