Skip to content

feat(azure_functions): add messaging attributes to azure function service bus consumer spans #13810

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
12 changes: 6 additions & 6 deletions .riot/requirements/16ae446.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
10 changes: 5 additions & 5 deletions .riot/requirements/5b956b7.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
2 changes: 1 addition & 1 deletion .riot/requirements/6c15ef8.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions .riot/requirements/a1026b8.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
13 changes: 13 additions & 0 deletions ddtrace/_trace/trace_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion ddtrace/contrib/integration_registry/registry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 16 additions & 5 deletions ddtrace/contrib/internal/azure_functions/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down Expand Up @@ -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()
Expand All @@ -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

Expand All @@ -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):
Expand All @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion ddtrace/internal/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
+--------------------------------------------------+---------------+----------------+
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
features:
- |
azure_functions: Add messaging span attributes for service bus triggers
2 changes: 1 addition & 1 deletion riotfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down
2 changes: 1 addition & 1 deletion supported_versions_output.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion supported_versions_table.csv
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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!")


Expand Down
Loading
Loading