Skip to content

Commit 3292584

Browse files
feat(azure_functions): add messaging attributes to azure function service bus consumer spans (#13810)
This PR adds messaging attributes to spans generated by Azure Function Service Bus Triggers. It also updates the minimum supported Azure Function version to `1.10.1` since that's when the `FunctionApp` class that is used to patch a user's functions was introduced. ### Instrumentation details: - Span attributes added to Azure Function Service Bus Triggers * `messaging.destination.name: <queue/topic name>` * `messaging.message_id: <message id>` * `messaging.operation: receive` * `messaging.system: servicebus` - https://datadoghq.atlassian.net/browse/SVLS-7020 - https://github.com/Azure/azure-functions-python-library/releases/tag/1.10.1 ## Checklist - [x] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)
1 parent d9a1237 commit 3292584

21 files changed

+104
-59
lines changed

.riot/requirements/16ae446.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ attrs==25.3.0
88
azure-core==1.34.0
99
azure-functions==1.23.0
1010
azure-servicebus==7.14.2
11-
certifi==2025.4.26
11+
certifi==2025.6.15
1212
charset-normalizer==3.4.2
13-
coverage[toml]==7.8.2
13+
coverage[toml]==7.9.1
1414
exceptiongroup==1.3.0
1515
hypothesis==6.45.0
1616
idna==3.10
@@ -21,14 +21,14 @@ mock==5.2.0
2121
opentracing==2.4.0
2222
packaging==25.0
2323
pluggy==1.6.0
24-
pygments==2.19.1
25-
pytest==8.4.0
26-
pytest-cov==6.1.1
24+
pygments==2.19.2
25+
pytest==8.4.1
26+
pytest-cov==6.2.1
2727
pytest-mock==3.14.1
2828
requests==2.32.4
2929
six==1.17.0
3030
sortedcontainers==2.4.0
3131
tomli==2.2.1
3232
typing-extensions==4.14.0
33-
urllib3==2.4.0
33+
urllib3==2.5.0
3434
werkzeug==3.1.3

.riot/requirements/1241599.txt renamed to .riot/requirements/18d52a7.txt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
# This file is autogenerated by pip-compile with Python 3.9
33
# by the following command:
44
#
5-
# pip-compile --allow-unsafe --no-annotate .riot/requirements/1241599.in
5+
# pip-compile --allow-unsafe --no-annotate .riot/requirements/18d52a7.in
66
#
77
attrs==25.3.0
88
azure-core==1.34.0
9-
azure-functions==1.20.0
9+
azure-functions==1.10.1
1010
azure-servicebus==7.14.2
11-
certifi==2025.4.26
11+
certifi==2025.6.15
1212
charset-normalizer==3.4.2
13-
coverage[toml]==7.8.2
13+
coverage[toml]==7.9.1
1414
exceptiongroup==1.3.0
1515
hypothesis==6.45.0
1616
idna==3.10
@@ -20,13 +20,13 @@ mock==5.2.0
2020
opentracing==2.4.0
2121
packaging==25.0
2222
pluggy==1.6.0
23-
pygments==2.19.1
24-
pytest==8.4.0
25-
pytest-cov==6.1.1
23+
pygments==2.19.2
24+
pytest==8.4.1
25+
pytest-cov==6.2.1
2626
pytest-mock==3.14.1
2727
requests==2.32.4
2828
six==1.17.0
2929
sortedcontainers==2.4.0
3030
tomli==2.2.1
3131
typing-extensions==4.14.0
32-
urllib3==2.4.0
32+
urllib3==2.5.0

.riot/requirements/9b11151.txt renamed to .riot/requirements/5b1bf57.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
# This file is autogenerated by pip-compile with Python 3.11
33
# by the following command:
44
#
5-
# pip-compile --allow-unsafe --no-annotate .riot/requirements/9b11151.in
5+
# pip-compile --allow-unsafe --no-annotate .riot/requirements/5b1bf57.in
66
#
77
attrs==25.3.0
88
azure-core==1.34.0
9-
azure-functions==1.20.0
9+
azure-functions==1.10.1
1010
azure-servicebus==7.14.2
11-
certifi==2025.4.26
11+
certifi==2025.6.15
1212
charset-normalizer==3.4.2
13-
coverage[toml]==7.9.0
13+
coverage[toml]==7.9.1
1414
hypothesis==6.45.0
1515
idna==3.10
1616
iniconfig==2.1.0
@@ -19,12 +19,12 @@ mock==5.2.0
1919
opentracing==2.4.0
2020
packaging==25.0
2121
pluggy==1.6.0
22-
pygments==2.19.1
23-
pytest==8.4.0
22+
pygments==2.19.2
23+
pytest==8.4.1
2424
pytest-cov==6.2.1
2525
pytest-mock==3.14.1
2626
requests==2.32.4
2727
six==1.17.0
2828
sortedcontainers==2.4.0
2929
typing-extensions==4.14.0
30-
urllib3==2.4.0
30+
urllib3==2.5.0

.riot/requirements/5b956b7.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ attrs==25.3.0
88
azure-core==1.34.0
99
azure-functions==1.23.0
1010
azure-servicebus==7.14.2
11-
certifi==2025.4.26
11+
certifi==2025.6.15
1212
charset-normalizer==3.4.2
13-
coverage[toml]==7.9.0
13+
coverage[toml]==7.9.1
1414
hypothesis==6.45.0
1515
idna==3.10
1616
iniconfig==2.1.0
@@ -20,13 +20,13 @@ mock==5.2.0
2020
opentracing==2.4.0
2121
packaging==25.0
2222
pluggy==1.6.0
23-
pygments==2.19.1
24-
pytest==8.4.0
23+
pygments==2.19.2
24+
pytest==8.4.1
2525
pytest-cov==6.2.1
2626
pytest-mock==3.14.1
2727
requests==2.32.4
2828
six==1.17.0
2929
sortedcontainers==2.4.0
3030
typing-extensions==4.14.0
31-
urllib3==2.4.0
31+
urllib3==2.5.0
3232
werkzeug==3.1.3

.riot/requirements/6c15ef8.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ attrs==25.3.0
88
azure-core==1.33.0
99
azure-functions==1.23.0
1010
azure-servicebus==7.14.2
11-
certifi==2025.4.26
11+
certifi==2025.6.15
1212
charset-normalizer==3.4.2
1313
coverage[toml]==7.6.1
1414
exceptiongroup==1.3.0

.riot/requirements/e7ef4bc.txt renamed to .riot/requirements/846ba1d.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
# This file is autogenerated by pip-compile with Python 3.8
33
# by the following command:
44
#
5-
# pip-compile --allow-unsafe --no-annotate .riot/requirements/e7ef4bc.in
5+
# pip-compile --allow-unsafe --no-annotate .riot/requirements/846ba1d.in
66
#
77
attrs==25.3.0
88
azure-core==1.33.0
9-
azure-functions==1.20.0
9+
azure-functions==1.10.1
1010
azure-servicebus==7.14.2
11-
certifi==2025.4.26
11+
certifi==2025.6.15
1212
charset-normalizer==3.4.2
1313
coverage[toml]==7.6.1
1414
exceptiongroup==1.3.0

.riot/requirements/a1026b8.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ attrs==25.3.0
88
azure-core==1.34.0
99
azure-functions==1.23.0
1010
azure-servicebus==7.14.2
11-
certifi==2025.4.26
11+
certifi==2025.6.15
1212
charset-normalizer==3.4.2
13-
coverage[toml]==7.8.2
13+
coverage[toml]==7.9.1
1414
exceptiongroup==1.3.0
1515
hypothesis==6.45.0
1616
idna==3.10
@@ -21,14 +21,14 @@ mock==5.2.0
2121
opentracing==2.4.0
2222
packaging==25.0
2323
pluggy==1.6.0
24-
pygments==2.19.1
25-
pytest==8.4.0
26-
pytest-cov==6.1.1
24+
pygments==2.19.2
25+
pytest==8.4.1
26+
pytest-cov==6.2.1
2727
pytest-mock==3.14.1
2828
requests==2.32.4
2929
six==1.17.0
3030
sortedcontainers==2.4.0
3131
tomli==2.2.1
3232
typing-extensions==4.14.0
33-
urllib3==2.4.0
33+
urllib3==2.5.0
3434
werkzeug==3.1.3

.riot/requirements/1ac22db.txt renamed to .riot/requirements/ce8d896.txt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
# This file is autogenerated by pip-compile with Python 3.10
33
# by the following command:
44
#
5-
# pip-compile --allow-unsafe --no-annotate .riot/requirements/1ac22db.in
5+
# pip-compile --allow-unsafe --no-annotate .riot/requirements/ce8d896.in
66
#
77
attrs==25.3.0
88
azure-core==1.34.0
9-
azure-functions==1.20.0
9+
azure-functions==1.10.1
1010
azure-servicebus==7.14.2
11-
certifi==2025.4.26
11+
certifi==2025.6.15
1212
charset-normalizer==3.4.2
13-
coverage[toml]==7.8.2
13+
coverage[toml]==7.9.1
1414
exceptiongroup==1.3.0
1515
hypothesis==6.45.0
1616
idna==3.10
@@ -20,13 +20,13 @@ mock==5.2.0
2020
opentracing==2.4.0
2121
packaging==25.0
2222
pluggy==1.6.0
23-
pygments==2.19.1
24-
pytest==8.4.0
25-
pytest-cov==6.1.1
23+
pygments==2.19.2
24+
pytest==8.4.1
25+
pytest-cov==6.2.1
2626
pytest-mock==3.14.1
2727
requests==2.32.4
2828
six==1.17.0
2929
sortedcontainers==2.4.0
3030
tomli==2.2.1
3131
typing-extensions==4.14.0
32-
urllib3==2.4.0
32+
urllib3==2.5.0

ddtrace/_trace/trace_handlers.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from ddtrace.internal.constants import FLASK_URL_RULE
3434
from ddtrace.internal.constants import FLASK_VIEW_ARGS
3535
from ddtrace.internal.constants import MESSAGING_DESTINATION_NAME
36+
from ddtrace.internal.constants import MESSAGING_MESSAGE_ID
3637
from ddtrace.internal.constants import MESSAGING_OPERATION
3738
from ddtrace.internal.constants import MESSAGING_SYSTEM
3839
from ddtrace.internal.constants import NETWORK_DESTINATION_NAME
@@ -869,6 +870,17 @@ def _on_azure_functions_trigger_span_modifier(ctx, azure_functions_config, funct
869870
_set_azure_function_tags(span, azure_functions_config, function_name, trigger, span_kind)
870871

871872

873+
def _on_azure_functions_service_bus_trigger_span_modifier(
874+
ctx, azure_functions_config, function_name, trigger, span_kind, entity_name, message_id
875+
):
876+
span = ctx.span
877+
_set_azure_function_tags(span, azure_functions_config, function_name, trigger, span_kind)
878+
span.set_tag_str(MESSAGING_DESTINATION_NAME, entity_name)
879+
span.set_tag_str(MESSAGING_MESSAGE_ID, message_id)
880+
span.set_tag_str(MESSAGING_OPERATION, "receive")
881+
span.set_tag_str(MESSAGING_SYSTEM, azure_servicebusx.SERVICE)
882+
883+
872884
def _on_azure_servicebus_send_message_modifier(ctx, azure_servicebus_config, entity_name, fully_qualified_namespace):
873885
span = ctx.span
874886
span.set_tag_str(COMPONENT, azure_servicebus_config.integration_name)
@@ -933,6 +945,7 @@ def listen():
933945
core.on("azure.functions.request_call_modifier", _on_azure_functions_request_span_modifier)
934946
core.on("azure.functions.start_response", _on_azure_functions_start_response)
935947
core.on("azure.functions.trigger_call_modifier", _on_azure_functions_trigger_span_modifier)
948+
core.on("azure.functions.service_bus_trigger_modifier", _on_azure_functions_service_bus_trigger_span_modifier)
936949
core.on("azure.servicebus.send_message_modifier", _on_azure_servicebus_send_message_modifier)
937950

938951
# web frameworks general handlers

ddtrace/contrib/integration_registry/registry.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ integrations:
138138
- azure-functions
139139
tested_versions_by_dependency:
140140
azure-functions:
141-
min: 1.20.0
141+
min: 1.10.1
142142
max: 1.23.0
143143

144144
- integration_name: azure_servicebus

ddtrace/contrib/internal/azure_functions/patch.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def get_version() -> str:
2929

3030

3131
def _supported_versions() -> Dict[str, str]:
32-
return {"azure.functions": ">=1.20.0"}
32+
return {"azure.functions": ">=1.10.1"}
3333

3434

3535
def patch():
@@ -58,6 +58,7 @@ def _patched_get_functions(wrapped, instance, args, kwargs):
5858
continue
5959

6060
trigger_type = trigger.get_binding_name()
61+
trigger_details = trigger.get_dict_repr()
6162
trigger_arg_name = trigger.name
6263

6364
function_name = function.get_function_name()
@@ -68,7 +69,7 @@ def _patched_get_functions(wrapped, instance, args, kwargs):
6869
elif trigger_type == "timerTrigger":
6970
function._func = _wrap_timer_trigger(pin, func, function_name)
7071
elif trigger_type == "serviceBusTrigger":
71-
function._func = _wrap_service_bus_trigger(pin, func, function_name, trigger_arg_name)
72+
function._func = _wrap_service_bus_trigger(pin, func, function_name, trigger_arg_name, trigger_details)
7273

7374
return functions
7475

@@ -90,7 +91,7 @@ def post_dispatch(ctx, res):
9091
return wrap_function_with_tracing(func, context_factory, pre_dispatch=pre_dispatch, post_dispatch=post_dispatch)
9192

9293

93-
def _wrap_service_bus_trigger(pin, func, function_name, trigger_arg_name):
94+
def _wrap_service_bus_trigger(pin, func, function_name, trigger_arg_name, trigger_details):
9495
trigger_type = "ServiceBus"
9596

9697
def context_factory(kwargs):
@@ -101,9 +102,19 @@ def context_factory(kwargs):
101102
)
102103

103104
def pre_dispatch(ctx, kwargs):
105+
msg = kwargs.get(trigger_arg_name)
106+
entity_name = trigger_details.get("topicName") or trigger_details.get("queueName")
104107
return (
105-
"azure.functions.trigger_call_modifier",
106-
(ctx, config.azure_functions, function_name, trigger_type, SpanKind.CONSUMER),
108+
"azure.functions.service_bus_trigger_modifier",
109+
(
110+
ctx,
111+
config.azure_functions,
112+
function_name,
113+
trigger_type,
114+
SpanKind.CONSUMER,
115+
entity_name,
116+
msg.message_id,
117+
),
107118
)
108119

109120
return wrap_function_with_tracing(func, context_factory, pre_dispatch=pre_dispatch)

ddtrace/internal/constants.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,10 @@
7474
EXTERNAL_ENV_HEADER_NAME = "Datadog-External-Env"
7575
EXTERNAL_ENV_ENVIRONMENT_VARIABLE = "DD_EXTERNAL_ENV"
7676

77-
MESSAGING_SYSTEM = "messaging.system"
7877
MESSAGING_DESTINATION_NAME = "messaging.destination.name"
78+
MESSAGING_MESSAGE_ID = "messaging.message_id"
7979
MESSAGING_OPERATION = "messaging.operation"
80+
MESSAGING_SYSTEM = "messaging.system"
8081

8182
NETWORK_DESTINATION_NAME = "network.destination.name"
8283

docs/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ contacting support.
6666
+--------------------------------------------------+---------------+----------------+
6767
| :ref:`avro` | \* | Yes |
6868
+--------------------------------------------------+---------------+----------------+
69-
| :ref:`azure_functions` | >= 1.20.0 | Yes |
69+
| :ref:`azure_functions` | >= 1.10.1 | Yes |
7070
+--------------------------------------------------+---------------+----------------+
7171
| :ref:`botocore` | \* | Yes |
7272
+--------------------------------------------------+---------------+----------------+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
features:
2+
- |
3+
azure_functions: Add messaging span attributes for service bus triggers

riotfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2923,7 +2923,7 @@ def select_pys(min_version: str = MIN_PYTHON_VERSION, max_version: str = MAX_PYT
29232923
command="pytest {cmdargs} tests/contrib/azure_functions",
29242924
pys=select_pys(min_version="3.8", max_version="3.11"),
29252925
pkgs={
2926-
"azure.functions": ["~=1.20.0", latest],
2926+
"azure.functions": ["~=1.10.1", latest],
29272927
"azure.servicebus": latest,
29282928
"requests": latest,
29292929
},

supported_versions_output.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@
103103
{
104104
"dependency": "azure-functions",
105105
"integration": "azure_functions",
106-
"minimum_tracer_supported": "1.20.0",
106+
"minimum_tracer_supported": "1.10.1",
107107
"max_tracer_supported": "1.23.0",
108108
"pinned": "true",
109109
"auto-instrumented": true

supported_versions_table.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ asyncpg,asyncpg,0.22.0,0.30.0,True
1313
avro,avro,1.12.0,1.12.0,True
1414
datadog-lambda,aws_lambda,6.105.0,6.105.0,True
1515
datadog_lambda,aws_lambda,6.105.0,6.105.0,True
16-
azure-functions,azure_functions *,1.20.0,1.23.0,True
16+
azure-functions,azure_functions *,1.10.1,1.23.0,True
1717
azure-servicebus,azure_servicebus *,7.14.2,7.14.2,True
1818
boto3,botocore *,1.34.49,1.38.26,True
1919
botocore,botocore *,1.34.49,1.38.26,True

tests/contrib/azure_functions/azure_function_app/function_app.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,13 @@ def http_get_child(req: func.HttpRequest) -> func.HttpResponse:
8080

8181
@app.route(route="httppostrootservicebus", auth_level=func.AuthLevel.ANONYMOUS, methods=[func.HttpMethod.POST])
8282
def http_post_root_servicebus(req: func.HttpRequest) -> func.HttpResponse:
83-
message = azure_servicebus.ServiceBusMessage("test message")
8483
with azure_servicebus.ServiceBusClient.from_connection_string(
8584
conn_str=os.getenv("CONNECTION_STRING", "")
8685
) as servicebus_client:
8786
with servicebus_client.get_queue_sender(queue_name="queue.1") as queue_sender:
88-
queue_sender.send_messages(message)
87+
queue_sender.send_messages(azure_servicebus.ServiceBusMessage("test message"))
8988
with servicebus_client.get_topic_sender(topic_name="topic.1") as topic_sender:
90-
topic_sender.send_messages(message)
89+
topic_sender.send_messages(azure_servicebus.ServiceBusMessage("test message"))
9190
return func.HttpResponse("Hello Datadog!")
9291

9392

0 commit comments

Comments
 (0)