diff --git a/.riot/requirements/16ae446.txt b/.riot/requirements/16ae446.txt index eb3298286dc..842b0f325b1 100644 --- a/.riot/requirements/16ae446.txt +++ b/.riot/requirements/16ae446.txt @@ -8,9 +8,9 @@ attrs==25.3.0 azure-core==1.34.0 azure-functions==1.23.0 azure-servicebus==7.14.2 -certifi==2025.4.26 +certifi==2025.6.15 charset-normalizer==3.4.2 -coverage[toml]==7.8.2 +coverage[toml]==7.9.1 exceptiongroup==1.3.0 hypothesis==6.45.0 idna==3.10 @@ -21,14 +21,14 @@ mock==5.2.0 opentracing==2.4.0 packaging==25.0 pluggy==1.6.0 -pygments==2.19.1 -pytest==8.4.0 -pytest-cov==6.1.1 +pygments==2.19.2 +pytest==8.4.1 +pytest-cov==6.2.1 pytest-mock==3.14.1 requests==2.32.4 six==1.17.0 sortedcontainers==2.4.0 tomli==2.2.1 typing-extensions==4.14.0 -urllib3==2.4.0 +urllib3==2.5.0 werkzeug==3.1.3 diff --git a/.riot/requirements/1241599.txt b/.riot/requirements/18d52a7.txt similarity index 77% rename from .riot/requirements/1241599.txt rename to .riot/requirements/18d52a7.txt index 0a779a45325..a2da5eb23b1 100644 --- a/.riot/requirements/1241599.txt +++ b/.riot/requirements/18d52a7.txt @@ -2,15 +2,15 @@ # This file is autogenerated by pip-compile with Python 3.9 # by the following command: # -# pip-compile --allow-unsafe --no-annotate .riot/requirements/1241599.in +# pip-compile --allow-unsafe --no-annotate .riot/requirements/18d52a7.in # attrs==25.3.0 azure-core==1.34.0 -azure-functions==1.20.0 +azure-functions==1.10.1 azure-servicebus==7.14.2 -certifi==2025.4.26 +certifi==2025.6.15 charset-normalizer==3.4.2 -coverage[toml]==7.8.2 +coverage[toml]==7.9.1 exceptiongroup==1.3.0 hypothesis==6.45.0 idna==3.10 @@ -20,13 +20,13 @@ mock==5.2.0 opentracing==2.4.0 packaging==25.0 pluggy==1.6.0 -pygments==2.19.1 -pytest==8.4.0 -pytest-cov==6.1.1 +pygments==2.19.2 +pytest==8.4.1 +pytest-cov==6.2.1 pytest-mock==3.14.1 requests==2.32.4 six==1.17.0 sortedcontainers==2.4.0 tomli==2.2.1 typing-extensions==4.14.0 -urllib3==2.4.0 +urllib3==2.5.0 diff --git a/.riot/requirements/9b11151.txt b/.riot/requirements/5b1bf57.txt similarity index 79% rename from .riot/requirements/9b11151.txt rename to .riot/requirements/5b1bf57.txt index 40246383d19..e80114f1798 100644 --- a/.riot/requirements/9b11151.txt +++ b/.riot/requirements/5b1bf57.txt @@ -2,15 +2,15 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile --allow-unsafe --no-annotate .riot/requirements/9b11151.in +# pip-compile --allow-unsafe --no-annotate .riot/requirements/5b1bf57.in # attrs==25.3.0 azure-core==1.34.0 -azure-functions==1.20.0 +azure-functions==1.10.1 azure-servicebus==7.14.2 -certifi==2025.4.26 +certifi==2025.6.15 charset-normalizer==3.4.2 -coverage[toml]==7.9.0 +coverage[toml]==7.9.1 hypothesis==6.45.0 idna==3.10 iniconfig==2.1.0 @@ -19,12 +19,12 @@ mock==5.2.0 opentracing==2.4.0 packaging==25.0 pluggy==1.6.0 -pygments==2.19.1 -pytest==8.4.0 +pygments==2.19.2 +pytest==8.4.1 pytest-cov==6.2.1 pytest-mock==3.14.1 requests==2.32.4 six==1.17.0 sortedcontainers==2.4.0 typing-extensions==4.14.0 -urllib3==2.4.0 +urllib3==2.5.0 diff --git a/.riot/requirements/5b956b7.txt b/.riot/requirements/5b956b7.txt index 3844449a2a5..31b2d2c6e87 100644 --- a/.riot/requirements/5b956b7.txt +++ b/.riot/requirements/5b956b7.txt @@ -8,9 +8,9 @@ attrs==25.3.0 azure-core==1.34.0 azure-functions==1.23.0 azure-servicebus==7.14.2 -certifi==2025.4.26 +certifi==2025.6.15 charset-normalizer==3.4.2 -coverage[toml]==7.9.0 +coverage[toml]==7.9.1 hypothesis==6.45.0 idna==3.10 iniconfig==2.1.0 @@ -20,13 +20,13 @@ mock==5.2.0 opentracing==2.4.0 packaging==25.0 pluggy==1.6.0 -pygments==2.19.1 -pytest==8.4.0 +pygments==2.19.2 +pytest==8.4.1 pytest-cov==6.2.1 pytest-mock==3.14.1 requests==2.32.4 six==1.17.0 sortedcontainers==2.4.0 typing-extensions==4.14.0 -urllib3==2.4.0 +urllib3==2.5.0 werkzeug==3.1.3 diff --git a/.riot/requirements/6c15ef8.txt b/.riot/requirements/6c15ef8.txt index 140c92528fc..1f24a3d26e8 100644 --- a/.riot/requirements/6c15ef8.txt +++ b/.riot/requirements/6c15ef8.txt @@ -8,7 +8,7 @@ attrs==25.3.0 azure-core==1.33.0 azure-functions==1.23.0 azure-servicebus==7.14.2 -certifi==2025.4.26 +certifi==2025.6.15 charset-normalizer==3.4.2 coverage[toml]==7.6.1 exceptiongroup==1.3.0 diff --git a/.riot/requirements/e7ef4bc.txt b/.riot/requirements/846ba1d.txt similarity index 91% rename from .riot/requirements/e7ef4bc.txt rename to .riot/requirements/846ba1d.txt index 4d317496cf1..8688ae12a75 100644 --- a/.riot/requirements/e7ef4bc.txt +++ b/.riot/requirements/846ba1d.txt @@ -2,13 +2,13 @@ # This file is autogenerated by pip-compile with Python 3.8 # by the following command: # -# pip-compile --allow-unsafe --no-annotate .riot/requirements/e7ef4bc.in +# pip-compile --allow-unsafe --no-annotate .riot/requirements/846ba1d.in # attrs==25.3.0 azure-core==1.33.0 -azure-functions==1.20.0 +azure-functions==1.10.1 azure-servicebus==7.14.2 -certifi==2025.4.26 +certifi==2025.6.15 charset-normalizer==3.4.2 coverage[toml]==7.6.1 exceptiongroup==1.3.0 diff --git a/.riot/requirements/a1026b8.txt b/.riot/requirements/a1026b8.txt index a62e1b24c20..2ed718e7b09 100644 --- a/.riot/requirements/a1026b8.txt +++ b/.riot/requirements/a1026b8.txt @@ -8,9 +8,9 @@ attrs==25.3.0 azure-core==1.34.0 azure-functions==1.23.0 azure-servicebus==7.14.2 -certifi==2025.4.26 +certifi==2025.6.15 charset-normalizer==3.4.2 -coverage[toml]==7.8.2 +coverage[toml]==7.9.1 exceptiongroup==1.3.0 hypothesis==6.45.0 idna==3.10 @@ -21,14 +21,14 @@ mock==5.2.0 opentracing==2.4.0 packaging==25.0 pluggy==1.6.0 -pygments==2.19.1 -pytest==8.4.0 -pytest-cov==6.1.1 +pygments==2.19.2 +pytest==8.4.1 +pytest-cov==6.2.1 pytest-mock==3.14.1 requests==2.32.4 six==1.17.0 sortedcontainers==2.4.0 tomli==2.2.1 typing-extensions==4.14.0 -urllib3==2.4.0 +urllib3==2.5.0 werkzeug==3.1.3 diff --git a/.riot/requirements/1ac22db.txt b/.riot/requirements/ce8d896.txt similarity index 78% rename from .riot/requirements/1ac22db.txt rename to .riot/requirements/ce8d896.txt index 600ff24d1f5..dd8786ce54e 100644 --- a/.riot/requirements/1ac22db.txt +++ b/.riot/requirements/ce8d896.txt @@ -2,15 +2,15 @@ # This file is autogenerated by pip-compile with Python 3.10 # by the following command: # -# pip-compile --allow-unsafe --no-annotate .riot/requirements/1ac22db.in +# pip-compile --allow-unsafe --no-annotate .riot/requirements/ce8d896.in # attrs==25.3.0 azure-core==1.34.0 -azure-functions==1.20.0 +azure-functions==1.10.1 azure-servicebus==7.14.2 -certifi==2025.4.26 +certifi==2025.6.15 charset-normalizer==3.4.2 -coverage[toml]==7.8.2 +coverage[toml]==7.9.1 exceptiongroup==1.3.0 hypothesis==6.45.0 idna==3.10 @@ -20,13 +20,13 @@ mock==5.2.0 opentracing==2.4.0 packaging==25.0 pluggy==1.6.0 -pygments==2.19.1 -pytest==8.4.0 -pytest-cov==6.1.1 +pygments==2.19.2 +pytest==8.4.1 +pytest-cov==6.2.1 pytest-mock==3.14.1 requests==2.32.4 six==1.17.0 sortedcontainers==2.4.0 tomli==2.2.1 typing-extensions==4.14.0 -urllib3==2.4.0 +urllib3==2.5.0 diff --git a/ddtrace/_trace/trace_handlers.py b/ddtrace/_trace/trace_handlers.py index d62ea286ca7..c2dd3f00ba4 100644 --- a/ddtrace/_trace/trace_handlers.py +++ b/ddtrace/_trace/trace_handlers.py @@ -33,6 +33,7 @@ from ddtrace.internal.constants import FLASK_URL_RULE from ddtrace.internal.constants import FLASK_VIEW_ARGS from ddtrace.internal.constants import MESSAGING_DESTINATION_NAME +from ddtrace.internal.constants import MESSAGING_MESSAGE_ID from ddtrace.internal.constants import MESSAGING_OPERATION from ddtrace.internal.constants import MESSAGING_SYSTEM from ddtrace.internal.constants import NETWORK_DESTINATION_NAME @@ -869,6 +870,17 @@ def _on_azure_functions_trigger_span_modifier(ctx, azure_functions_config, funct _set_azure_function_tags(span, azure_functions_config, function_name, trigger, span_kind) +def _on_azure_functions_service_bus_trigger_span_modifier( + ctx, azure_functions_config, function_name, trigger, span_kind, entity_name, message_id +): + span = ctx.span + _set_azure_function_tags(span, azure_functions_config, function_name, trigger, span_kind) + span.set_tag_str(MESSAGING_DESTINATION_NAME, entity_name) + span.set_tag_str(MESSAGING_MESSAGE_ID, message_id) + span.set_tag_str(MESSAGING_OPERATION, "receive") + span.set_tag_str(MESSAGING_SYSTEM, azure_servicebusx.SERVICE) + + def _on_azure_servicebus_send_message_modifier(ctx, azure_servicebus_config, entity_name, fully_qualified_namespace): span = ctx.span span.set_tag_str(COMPONENT, azure_servicebus_config.integration_name) @@ -933,6 +945,7 @@ def listen(): core.on("azure.functions.request_call_modifier", _on_azure_functions_request_span_modifier) core.on("azure.functions.start_response", _on_azure_functions_start_response) core.on("azure.functions.trigger_call_modifier", _on_azure_functions_trigger_span_modifier) + core.on("azure.functions.service_bus_trigger_modifier", _on_azure_functions_service_bus_trigger_span_modifier) core.on("azure.servicebus.send_message_modifier", _on_azure_servicebus_send_message_modifier) # web frameworks general handlers diff --git a/ddtrace/contrib/integration_registry/registry.yaml b/ddtrace/contrib/integration_registry/registry.yaml index cd4bc3e58ea..6c0ee648a10 100644 --- a/ddtrace/contrib/integration_registry/registry.yaml +++ b/ddtrace/contrib/integration_registry/registry.yaml @@ -138,7 +138,7 @@ integrations: - azure-functions tested_versions_by_dependency: azure-functions: - min: 1.20.0 + min: 1.10.1 max: 1.23.0 - integration_name: azure_servicebus diff --git a/ddtrace/contrib/internal/azure_functions/patch.py b/ddtrace/contrib/internal/azure_functions/patch.py index c8fe45dcd7f..d905928a27c 100644 --- a/ddtrace/contrib/internal/azure_functions/patch.py +++ b/ddtrace/contrib/internal/azure_functions/patch.py @@ -29,7 +29,7 @@ def get_version() -> str: def _supported_versions() -> Dict[str, str]: - return {"azure.functions": ">=1.20.0"} + return {"azure.functions": ">=1.10.1"} def patch(): @@ -58,6 +58,7 @@ def _patched_get_functions(wrapped, instance, args, kwargs): continue trigger_type = trigger.get_binding_name() + trigger_details = trigger.get_dict_repr() trigger_arg_name = trigger.name function_name = function.get_function_name() @@ -68,7 +69,7 @@ def _patched_get_functions(wrapped, instance, args, kwargs): elif trigger_type == "timerTrigger": function._func = _wrap_timer_trigger(pin, func, function_name) elif trigger_type == "serviceBusTrigger": - function._func = _wrap_service_bus_trigger(pin, func, function_name, trigger_arg_name) + function._func = _wrap_service_bus_trigger(pin, func, function_name, trigger_arg_name, trigger_details) return functions @@ -90,7 +91,7 @@ def post_dispatch(ctx, res): return wrap_function_with_tracing(func, context_factory, pre_dispatch=pre_dispatch, post_dispatch=post_dispatch) -def _wrap_service_bus_trigger(pin, func, function_name, trigger_arg_name): +def _wrap_service_bus_trigger(pin, func, function_name, trigger_arg_name, trigger_details): trigger_type = "ServiceBus" def context_factory(kwargs): @@ -101,9 +102,19 @@ def context_factory(kwargs): ) def pre_dispatch(ctx, kwargs): + msg = kwargs.get(trigger_arg_name) + entity_name = trigger_details.get("topicName") or trigger_details.get("queueName") return ( - "azure.functions.trigger_call_modifier", - (ctx, config.azure_functions, function_name, trigger_type, SpanKind.CONSUMER), + "azure.functions.service_bus_trigger_modifier", + ( + ctx, + config.azure_functions, + function_name, + trigger_type, + SpanKind.CONSUMER, + entity_name, + msg.message_id, + ), ) return wrap_function_with_tracing(func, context_factory, pre_dispatch=pre_dispatch) diff --git a/ddtrace/internal/constants.py b/ddtrace/internal/constants.py index 0734da80985..4e2a21ffcb5 100644 --- a/ddtrace/internal/constants.py +++ b/ddtrace/internal/constants.py @@ -74,9 +74,10 @@ EXTERNAL_ENV_HEADER_NAME = "Datadog-External-Env" EXTERNAL_ENV_ENVIRONMENT_VARIABLE = "DD_EXTERNAL_ENV" -MESSAGING_SYSTEM = "messaging.system" MESSAGING_DESTINATION_NAME = "messaging.destination.name" +MESSAGING_MESSAGE_ID = "messaging.message_id" MESSAGING_OPERATION = "messaging.operation" +MESSAGING_SYSTEM = "messaging.system" NETWORK_DESTINATION_NAME = "network.destination.name" diff --git a/docs/index.rst b/docs/index.rst index 5531a7a30fa..4d5a656bfa5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -66,7 +66,7 @@ contacting support. +--------------------------------------------------+---------------+----------------+ | :ref:`avro` | \* | Yes | +--------------------------------------------------+---------------+----------------+ -| :ref:`azure_functions` | >= 1.20.0 | Yes | +| :ref:`azure_functions` | >= 1.10.1 | Yes | +--------------------------------------------------+---------------+----------------+ | :ref:`botocore` | \* | Yes | +--------------------------------------------------+---------------+----------------+ diff --git a/releasenotes/notes/feat-azure-functions-service-bus-trigger-span-attributes-6de4b04663074cdb.yaml b/releasenotes/notes/feat-azure-functions-service-bus-trigger-span-attributes-6de4b04663074cdb.yaml new file mode 100644 index 00000000000..9c235505f00 --- /dev/null +++ b/releasenotes/notes/feat-azure-functions-service-bus-trigger-span-attributes-6de4b04663074cdb.yaml @@ -0,0 +1,3 @@ +features: + - | + azure_functions: Add messaging span attributes for service bus triggers diff --git a/riotfile.py b/riotfile.py index 7fa42837584..e47bd17648f 100644 --- a/riotfile.py +++ b/riotfile.py @@ -2923,7 +2923,7 @@ def select_pys(min_version: str = MIN_PYTHON_VERSION, max_version: str = MAX_PYT command="pytest {cmdargs} tests/contrib/azure_functions", pys=select_pys(min_version="3.8", max_version="3.11"), pkgs={ - "azure.functions": ["~=1.20.0", latest], + "azure.functions": ["~=1.10.1", latest], "azure.servicebus": latest, "requests": latest, }, diff --git a/supported_versions_output.json b/supported_versions_output.json index b56570889a6..73b978ce367 100644 --- a/supported_versions_output.json +++ b/supported_versions_output.json @@ -103,7 +103,7 @@ { "dependency": "azure-functions", "integration": "azure_functions", - "minimum_tracer_supported": "1.20.0", + "minimum_tracer_supported": "1.10.1", "max_tracer_supported": "1.23.0", "pinned": "true", "auto-instrumented": true diff --git a/supported_versions_table.csv b/supported_versions_table.csv index 1971874dd5b..60522c25b91 100644 --- a/supported_versions_table.csv +++ b/supported_versions_table.csv @@ -13,7 +13,7 @@ asyncpg,asyncpg,0.22.0,0.30.0,True avro,avro,1.12.0,1.12.0,True datadog-lambda,aws_lambda,6.105.0,6.105.0,True datadog_lambda,aws_lambda,6.105.0,6.105.0,True -azure-functions,azure_functions *,1.20.0,1.23.0,True +azure-functions,azure_functions *,1.10.1,1.23.0,True azure-servicebus,azure_servicebus *,7.14.2,7.14.2,True boto3,botocore *,1.34.49,1.38.26,True botocore,botocore *,1.34.49,1.38.26,True diff --git a/tests/contrib/azure_functions/azure_function_app/function_app.py b/tests/contrib/azure_functions/azure_function_app/function_app.py index 80a518db9be..6979969ecf9 100644 --- a/tests/contrib/azure_functions/azure_function_app/function_app.py +++ b/tests/contrib/azure_functions/azure_function_app/function_app.py @@ -80,14 +80,13 @@ def http_get_child(req: func.HttpRequest) -> func.HttpResponse: @app.route(route="httppostrootservicebus", auth_level=func.AuthLevel.ANONYMOUS, methods=[func.HttpMethod.POST]) def http_post_root_servicebus(req: func.HttpRequest) -> func.HttpResponse: - message = azure_servicebus.ServiceBusMessage("test message") with azure_servicebus.ServiceBusClient.from_connection_string( conn_str=os.getenv("CONNECTION_STRING", "") ) as servicebus_client: with servicebus_client.get_queue_sender(queue_name="queue.1") as queue_sender: - queue_sender.send_messages(message) + queue_sender.send_messages(azure_servicebus.ServiceBusMessage("test message")) with servicebus_client.get_topic_sender(topic_name="topic.1") as topic_sender: - topic_sender.send_messages(message) + topic_sender.send_messages(azure_servicebus.ServiceBusMessage("test message")) return func.HttpResponse("Hello Datadog!") diff --git a/tests/contrib/azure_functions/test_azure_functions_snapshot.py b/tests/contrib/azure_functions/test_azure_functions_snapshot.py index 5800ccca500..5da57bb7c25 100644 --- a/tests/contrib/azure_functions/test_azure_functions_snapshot.py +++ b/tests/contrib/azure_functions/test_azure_functions_snapshot.py @@ -17,6 +17,8 @@ "DD_AZURE_FUNCTIONS_DISTRIBUTED_TRACING": "False", } +SNAPSHOT_IGNORES = ["meta.messaging.message_id"] + @pytest.fixture def azure_functions_client(request): @@ -131,7 +133,7 @@ def test_http_get_distributed_tracing(azure_functions_client: Client) -> None: ids=["enabled", "disabled"], indirect=True, ) -@pytest.mark.snapshot +@pytest.mark.snapshot(ignores=SNAPSHOT_IGNORES) def test_service_bus_distributed_tracing(azure_functions_client: Client) -> None: assert azure_functions_client.post("/api/httppostrootservicebus", headers=DEFAULT_HEADERS).status_code == 200 diff --git a/tests/snapshots/tests.contrib.azure_functions.test_azure_functions_snapshot.test_service_bus_distributed_tracing[disabled].json b/tests/snapshots/tests.contrib.azure_functions.test_azure_functions_snapshot.test_service_bus_distributed_tracing[disabled].json index d4015428eb6..5e5674eb178 100644 --- a/tests/snapshots/tests.contrib.azure_functions.test_azure_functions_snapshot.test_service_bus_distributed_tracing[disabled].json +++ b/tests/snapshots/tests.contrib.azure_functions.test_azure_functions_snapshot.test_service_bus_distributed_tracing[disabled].json @@ -94,6 +94,10 @@ "aas.function.trigger": "ServiceBus", "component": "azure_functions", "language": "python", + "messaging.destination.name": "topic.1", + "messaging.message_id": "aa4762c1-fbbf-4da2-8162-fc48ccbeb010", + "messaging.operation": "receive", + "messaging.system": "servicebus", "runtime-id": "6b992d56c97f41bea2c59c38ff06a8e3", "span.kind": "consumer" }, @@ -122,6 +126,10 @@ "aas.function.trigger": "ServiceBus", "component": "azure_functions", "language": "python", + "messaging.destination.name": "queue.1", + "messaging.message_id": "aa4762c1-fbbf-4da2-8162-fc48ccbeb010", + "messaging.operation": "receive", + "messaging.system": "servicebus", "runtime-id": "6b992d56c97f41bea2c59c38ff06a8e3", "span.kind": "consumer" }, diff --git a/tests/snapshots/tests.contrib.azure_functions.test_azure_functions_snapshot.test_service_bus_distributed_tracing[enabled].json b/tests/snapshots/tests.contrib.azure_functions.test_azure_functions_snapshot.test_service_bus_distributed_tracing[enabled].json index b71418e0ce5..0a9b1c5b562 100644 --- a/tests/snapshots/tests.contrib.azure_functions.test_azure_functions_snapshot.test_service_bus_distributed_tracing[enabled].json +++ b/tests/snapshots/tests.contrib.azure_functions.test_azure_functions_snapshot.test_service_bus_distributed_tracing[enabled].json @@ -67,6 +67,10 @@ "aas.function.name": "servicebusqueue", "aas.function.trigger": "ServiceBus", "component": "azure_functions", + "messaging.destination.name": "queue.1", + "messaging.message_id": "68f07ff4-5e14-486c-aa81-535e7c3cf562", + "messaging.operation": "receive", + "messaging.system": "servicebus", "runtime-id": "3e1d1c4aeb5940e298b66654dc72a8ee", "span.kind": "consumer" }, @@ -114,6 +118,10 @@ "aas.function.name": "servicebustopic", "aas.function.trigger": "ServiceBus", "component": "azure_functions", + "messaging.destination.name": "topic.1", + "messaging.message_id": "68f07ff4-5e14-486c-aa81-535e7c3cf562", + "messaging.operation": "receive", + "messaging.system": "servicebus", "runtime-id": "3e1d1c4aeb5940e298b66654dc72a8ee", "span.kind": "consumer" },