Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions manifests/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,7 @@ manifest:
tests/integrations/test_inferred_proxy.py::Test_AWS_API_Gateway_Inferred_Span_Creation_With_Error: v3.15.0
tests/integrations/test_inferred_proxy.py::Test_AWS_API_Gateway_Inferred_Span_Creation_v2: missing_feature
tests/integrations/test_mongo.py::Test_Mongo: missing_feature (Endpoint is not implemented on weblog)
tests/integrations/test_otel_db_semantics.py::Test_PostgresOtelSemantics: missing_feature (DB OTel semantics not implemented; DD_TRACE_OTEL_SEMANTICS_ENABLED covers HTTP only)
tests/integrations/test_otel_drop_in.py::Test_Otel_Drop_In: missing_feature
tests/integrations/test_service_overrides.py::Test_SqlServiceNameSource: v3.40.0
tests/integrations/test_sql.py::Test_Sql: missing_feature (Endpoint is not implemented on weblog)
Expand Down
1 change: 1 addition & 0 deletions manifests/golang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,7 @@ manifest:
net-http-orchestrion: v1.72.1
tests/integrations/test_inferred_proxy.py::Test_AWS_API_Gateway_Inferred_Span_Creation_v2: missing_feature
tests/integrations/test_mongo.py::Test_Mongo: missing_feature (Endpoint is not implemented on weblog)
tests/integrations/test_otel_db_semantics.py::Test_PostgresOtelSemantics: missing_feature (DB OTel semantics not implemented; DD_TRACE_OTEL_SEMANTICS_ENABLED covers HTTP only)
tests/integrations/test_otel_drop_in.py::Test_Otel_Drop_In: missing_feature
tests/integrations/test_service_overrides.py::Test_SqlServiceNameSource: missing_feature
tests/integrations/test_sql.py::Test_Sql: missing_feature (Endpoint is not implemented on weblog)
Expand Down
1 change: 1 addition & 0 deletions manifests/java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3645,6 +3645,7 @@ manifest:
"*": v1.61.0-SNAPSHOT
spring-boot-3-native: irrelevant (GraalVM. Tracing support only)
tests/integrations/test_mongo.py::Test_Mongo: bug (APMAPI-729)
tests/integrations/test_otel_db_semantics.py::Test_PostgresOtelSemantics: missing_feature (DB OTel semantics not implemented; DD_TRACE_OTEL_SEMANTICS_ENABLED covers HTTP only)
tests/integrations/test_otel_drop_in.py::Test_Otel_Drop_In:
- weblog_declaration:
"*": missing_feature
Expand Down
1 change: 1 addition & 0 deletions manifests/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,7 @@ manifest:
tests/integrations/test_mongo.py::Test_Mongo:
- declaration: missing_feature (mongodb PHP extension not available in apache-mod containers)
excluded_weblog: [php-fpm-7.0, php-fpm-7.1, php-fpm-7.2, php-fpm-7.3, php-fpm-7.4, php-fpm-8.0, php-fpm-8.1, php-fpm-8.2, php-fpm-8.5]
tests/integrations/test_otel_db_semantics.py::Test_PostgresOtelSemantics: missing_feature (DB OTel semantics not implemented; DD_TRACE_OTEL_SEMANTICS_ENABLED covers HTTP only)
tests/integrations/test_otel_drop_in.py::Test_Otel_Drop_In:
- declaration: missing_feature (OTel SDK requires PHP >= 8.1)
excluded_weblog: [apache-mod-8.1, apache-mod-8.1-zts, apache-mod-8.2, apache-mod-8.2-zts, php-fpm-8.1, php-fpm-8.2, php-fpm-8.5]
Expand Down
1 change: 1 addition & 0 deletions manifests/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1752,6 +1752,7 @@ manifest:
uwsgi-poc: v4.2.0
uds-flask: v4.2.0
tests/integrations/test_mongo.py::Test_Mongo: missing_feature (Endpoint is not implemented on weblog)
tests/integrations/test_otel_db_semantics.py::Test_PostgresOtelSemantics: missing_feature (DB OTel semantics not implemented; DD_TRACE_OTEL_SEMANTICS_ENABLED covers HTTP only)
tests/integrations/test_otel_drop_in.py::Test_Otel_Drop_In: missing_feature
tests/integrations/test_service_overrides.py::Test_SqlServiceNameSource: irrelevant (Only implemented for Java)
tests/integrations/test_sql.py::Test_Sql: missing_feature (Endpoint is not implemented on weblog)
Expand Down
1 change: 1 addition & 0 deletions manifests/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2042,6 +2042,7 @@ manifest:
rails72: v2.33.0 # TODO: a lower version might be supported
rails52: v2.33.0 # TODO: a lower version might be supported
tests/integrations/test_mongo.py::Test_Mongo: missing_feature (Endpoint is not implemented on weblog)
tests/integrations/test_otel_db_semantics.py::Test_PostgresOtelSemantics: missing_feature (DB OTel semantics not implemented; DD_TRACE_OTEL_SEMANTICS_ENABLED covers HTTP only)
tests/integrations/test_otel_drop_in.py::Test_Otel_Drop_In: missing_feature
tests/integrations/test_service_overrides.py::Test_SqlServiceNameSource:
- weblog_declaration:
Expand Down
1 change: 1 addition & 0 deletions manifests/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ manifest:
tests/integrations/test_dsm.py::Test_DsmKafka::test_dsm_kafka_without_cluster_id: irrelevant
tests/integrations/test_dsm.py::Test_DsmRabbitmq::test_dsm_rabbitmq_dotnet_legacy: irrelevant (legacy dotnet behavior)
tests/integrations/test_mongo.py::Test_Mongo: missing_feature (Endpoint is not implemented on weblog)
tests/integrations/test_otel_db_semantics.py::Test_PostgresOtelSemantics: missing_feature (DB OTel semantics not implemented; DD_TRACE_OTEL_SEMANTICS_ENABLED covers HTTP only)
tests/integrations/test_service_overrides.py::Test_SqlServiceNameSource: irrelevant (Only implemented for Java)
tests/integrations/test_sql.py::Test_Sql: missing_feature (Endpoint is not implemented on weblog)
tests/otel/test_tracing_otlp.py::Test_Otel_Tracing_OTLP: missing_feature
Expand Down
101 changes: 101 additions & 0 deletions tests/integrations/test_otel_db_semantics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Unless explicitly stated otherwise all files in this repository are licensed under the the Apache License Version 2.0.
# This product includes software developed at Datadog (https://www.datadoghq.com/).
# Copyright 2026 Datadog, Inc.

"""Validate that database (postgres) SQL spans honor the OpenTelemetry database semantic
conventions when ``DD_TRACE_OTEL_SEMANTICS_ENABLED=true`` (the OTEL_SEMANTICS_DB scenario).

This is the database counterpart to tests/test_otel_http_semantics.py. When the flag is on, SQL
spans should emit the OpenTelemetry DB attribute names instead of the Datadog ones (the Datadog
names are replaced, not added alongside).

NOTE: no tracer implements DB OTel semantics yet — the flag currently covers HTTP only — so these
tests are gated ``missing_feature`` everywhere. They encode the target contract and activate
per-tracer as database support lands.

Spec: https://opentelemetry.io/docs/specs/semconv/db/database-spans/
"""

from utils import context, features, scenarios

from .utils import BaseDbIntegrationsTestClass


@features.semantic_core_validations
@scenarios.otel_semantics_db
class Test_PostgresOtelSemantics(BaseDbIntegrationsTestClass):
"""Postgres SQL spans emit OpenTelemetry database semantic-convention attribute names."""

db_service = "postgresql"

def _setup_queries(self):
# the inherited _setup issues the /db queries once (idempotent, shared across the class)
self._setup()

# one setup per test method, all aliased to the shared query setup (mirrors BaseDbIntegrationsTestClass)
setup_db_system_name = _setup_queries
setup_db_namespace = _setup_queries
setup_db_operation_name = _setup_queries
setup_db_query_text = _setup_queries
setup_db_collection_name = _setup_queries
setup_server_address = _setup_queries
setup_server_port = _setup_queries

def _tracer_span_metas(self, excluded_operations: tuple[str, ...] = ("select_error",)):
for db_operation, request in self.get_requests(excluded_operations=excluded_operations):
yield db_operation, self.get_span_from_tracer(request).meta

def test_db_system_name(self):
"""``db.type`` / ``db.system`` become ``db.system.name`` (the stable spec name) = postgresql."""
for db_operation, meta in self._tracer_span_metas():
assert meta.get("db.system.name") == "postgresql", f"failing for {db_operation}"
assert "db.system" not in meta, "experimental db.system must be absent (stable name is db.system.name)"
assert "db.type" not in meta, "legacy db.type must be absent in OTel mode"

def test_db_namespace(self):
"""``db.name`` / ``db.instance`` become ``db.namespace`` (the database name)."""
db_container = context.get_container_by_dd_integration_name(self.db_service)
for db_operation, meta in self._tracer_span_metas():
assert meta.get("db.namespace") == db_container.db_instance, f"failing for {db_operation}"
assert "db.name" not in meta, "legacy db.name must be absent in OTel mode"
assert "db.instance" not in meta, "legacy db.instance must be absent in OTel mode"

def test_db_operation_name(self):
"""``db.operation`` becomes ``db.operation.name`` (the SQL operation, e.g. select/insert)."""
for db_operation, meta in self._tracer_span_metas(excluded_operations=("select_error", "procedure")):
assert db_operation in meta.get("db.operation.name", "").lower(), f"failing for {db_operation}"
assert "db.operation" not in meta, "legacy db.operation must be absent in OTel mode"

def test_db_query_text(self):
"""``db.statement`` becomes ``db.query.text``."""
for db_operation, meta in self._tracer_span_metas(excluded_operations=("select_error", "procedure")):
assert db_operation in meta.get("db.query.text", "").lower(), f"failing for {db_operation}"
assert "db.statement" not in meta, "legacy db.statement must be absent in OTel mode"

def test_db_collection_name(self):
"""``db.sql.table`` becomes ``db.collection.name`` (the primary table)."""
for db_operation, meta in self._tracer_span_metas(excluded_operations=("select_error", "procedure")):
assert meta.get("db.collection.name"), f"db.collection.name expected, failing for {db_operation}"
assert "db.sql.table" not in meta, "legacy db.sql.table must be absent in OTel mode"

def test_server_address(self):
"""``out.host`` becomes ``server.address``."""
for db_operation, meta in self._tracer_span_metas():
assert meta.get("server.address"), f"server.address expected, failing for {db_operation}"
assert "out.host" not in meta, "legacy out.host must be absent in OTel mode"

def test_server_port(self):
"""``out.port`` / ``network.destination.port`` become ``server.port``.

The postgres port is non-default, so per the spec server.port is expected. Numeric values
may live in ``metrics`` rather than ``meta`` depending on the tracer, so check both.
"""
for db_operation, request in self.get_requests(excluded_operations=("select_error",)):
span = self.get_span_from_tracer(request)
meta, metrics = span.meta, span.metrics
port = meta.get("server.port", metrics.get("server.port"))
assert port is not None, f"server.port expected (non-default port), failing for {db_operation}"
_ = int(port)
for legacy in ("out.port", "network.destination.port"):
assert legacy not in meta, f"legacy {legacy} must be absent in meta in OTel mode ({db_operation})"
assert legacy not in metrics, f"legacy {legacy} must be absent in metrics in OTel mode ({db_operation})"
3 changes: 3 additions & 0 deletions utils/_context/_scenarios/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
CrossedTracingLibraryScenario,
DbmDynamicServiceScenario,
IntegrationsScenario,
OtelSemanticsDbScenario,
AWSIntegrationsScenario,
)
from .open_telemetry import OpenTelemetryScenario
Expand Down Expand Up @@ -255,6 +256,8 @@ class _Scenarios:
scenario_groups=[scenario_groups.open_telemetry],
)

otel_semantics_db = OtelSemanticsDbScenario()

# Telemetry scenarios
telemetry_dependency_loaded_test_for_dependency_collection_disabled = EndToEndScenario(
"TELEMETRY_DEPENDENCY_LOADED_TEST_FOR_DEPENDENCY_COLLECTION_DISABLED",
Expand Down
18 changes: 18 additions & 0 deletions utils/_context/_scenarios/integrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,24 @@ def __init__(self) -> None:
)


class OtelSemanticsDbScenario(EndToEndScenario):
def __init__(self) -> None:
super().__init__(
"OTEL_SEMANTICS_DB",
weblog_env={
"DD_TRACE_OTEL_SEMANTICS_ENABLED": "true",
"DD_TRACE_OTEL_ENABLED": "1",
"DD_DBM_PROPAGATION_MODE": "full",
},
other_weblog_containers=(PostgresContainer,),
doc=(
"Like OTEL_SEMANTICS but for database (postgres) spans: validates that SQL spans emit "
"OpenTelemetry database semantic-convention attributes when DD_TRACE_OTEL_SEMANTICS_ENABLED=true"
),
scenario_groups=[scenario_groups.open_telemetry, scenario_groups.integrations],
)


class AWSIntegrationsScenario(EndToEndScenario):
unique_id: str = ""

Expand Down
Loading