From f283f82e00d0f658012aeb1d0df18cfd4faca506 Mon Sep 17 00:00:00 2001 From: Varshitha Bachu Date: Wed, 2 Apr 2025 22:29:42 -0700 Subject: [PATCH 01/10] initial commit --- .../models/DurableOrchestrationClient.py | 9 +++++++-- azure/durable_functions/models/utils/http_utils.py | 8 ++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/azure/durable_functions/models/DurableOrchestrationClient.py b/azure/durable_functions/models/DurableOrchestrationClient.py index 1be5a28e..a3f3ff8f 100644 --- a/azure/durable_functions/models/DurableOrchestrationClient.py +++ b/azure/durable_functions/models/DurableOrchestrationClient.py @@ -47,7 +47,9 @@ def __init__(self, context: str): async def start_new(self, orchestration_function_name: str, instance_id: Optional[str] = None, - client_input: Optional[Any] = None) -> str: + client_input: Optional[Any] = None, + trace_parent: str = None, + trace_state: str = None) -> str: """Start a new instance of the specified orchestrator function. If an orchestration instance with the specified ID already exists, the @@ -72,7 +74,10 @@ async def start_new(self, instance_id=instance_id, orchestration_function_name=orchestration_function_name) response: List[Any] = await self._post_async_request( - request_url, self._get_json_input(client_input)) + request_url, + self._get_json_input(client_input), + trace_parent, + trace_state) status_code: int = response[0] if status_code <= 202 and response[1]: diff --git a/azure/durable_functions/models/utils/http_utils.py b/azure/durable_functions/models/utils/http_utils.py index e45cef68..45ed465d 100644 --- a/azure/durable_functions/models/utils/http_utils.py +++ b/azure/durable_functions/models/utils/http_utils.py @@ -3,7 +3,7 @@ import aiohttp -async def post_async_request(url: str, data: Any = None) -> List[Union[int, Any]]: +async def post_async_request(url: str, data: Any = None, trace_parent: str = None, trace_state: str = None) -> List[Union[int, Any]]: """Post request with the data provided to the url provided. Parameters @@ -20,7 +20,11 @@ async def post_async_request(url: str, data: Any = None) -> List[Union[int, Any] """ async with aiohttp.ClientSession() as session: async with session.post(url, - json=data) as response: + json=data, + headers={ + "traceparent": trace_parent, + "tracestate": trace_state + }) as response: # We disable aiohttp's input type validation # as the server may respond with alternative # data encodings. This is potentially unsafe. From 77857fea6d99b1071f3d6bad70c6543d4e659f35 Mon Sep 17 00:00:00 2001 From: Varshitha Bachu Date: Tue, 8 Apr 2025 13:32:00 -0700 Subject: [PATCH 02/10] updated comments and made parameters Optional --- .../models/DurableOrchestrationClient.py | 8 ++++++-- .../durable_functions/models/utils/http_utils.py | 16 ++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/azure/durable_functions/models/DurableOrchestrationClient.py b/azure/durable_functions/models/DurableOrchestrationClient.py index a3f3ff8f..5842aa24 100644 --- a/azure/durable_functions/models/DurableOrchestrationClient.py +++ b/azure/durable_functions/models/DurableOrchestrationClient.py @@ -48,8 +48,8 @@ async def start_new(self, orchestration_function_name: str, instance_id: Optional[str] = None, client_input: Optional[Any] = None, - trace_parent: str = None, - trace_state: str = None) -> str: + trace_parent: Optional[str] = None, + trace_state: Optional[str] = None) -> str: """Start a new instance of the specified orchestrator function. If an orchestration instance with the specified ID already exists, the @@ -64,6 +64,10 @@ async def start_new(self, the Durable Functions extension will generate a random GUID (recommended). client_input : Optional[Any] JSON-serializable input value for the orchestrator function. + trace_parent : str + The traceparent header to send with the request. + trace_state : str + The tracestate header to send with the request. Returns ------- diff --git a/azure/durable_functions/models/utils/http_utils.py b/azure/durable_functions/models/utils/http_utils.py index 45ed465d..b2a40e91 100644 --- a/azure/durable_functions/models/utils/http_utils.py +++ b/azure/durable_functions/models/utils/http_utils.py @@ -12,6 +12,10 @@ async def post_async_request(url: str, data: Any = None, trace_parent: str = Non url to make the post to data: Any object to post + trace_parent: str + traceparent header to send with the request + trace_state: str + tracestate header to send with the request Returns ------- @@ -19,12 +23,12 @@ async def post_async_request(url: str, data: Any = None, trace_parent: str = Non Tuple with the Response status code and the data returned from the request """ async with aiohttp.ClientSession() as session: - async with session.post(url, - json=data, - headers={ - "traceparent": trace_parent, - "tracestate": trace_state - }) as response: + headers = {} + if trace_parent: + headers["traceparent"] = trace_parent + if trace_state: + headers["tracestate"] = trace_state + async with session.post(url, json=data, headers=headers) as response: # We disable aiohttp's input type validation # as the server may respond with alternative # data encodings. This is potentially unsafe. From 85ca91e7f5e2f07f1e547994e55fddc5334025d3 Mon Sep 17 00:00:00 2001 From: Varshitha Bachu Date: Tue, 8 Apr 2025 14:26:59 -0700 Subject: [PATCH 03/10] fixed lint warning --- azure/durable_functions/models/utils/http_utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/azure/durable_functions/models/utils/http_utils.py b/azure/durable_functions/models/utils/http_utils.py index b2a40e91..eaa3a07d 100644 --- a/azure/durable_functions/models/utils/http_utils.py +++ b/azure/durable_functions/models/utils/http_utils.py @@ -3,7 +3,10 @@ import aiohttp -async def post_async_request(url: str, data: Any = None, trace_parent: str = None, trace_state: str = None) -> List[Union[int, Any]]: +async def post_async_request(url: str, + data: Any = None, + trace_parent: str = None, + trace_state: str = None) -> List[Union[int, Any]]: """Post request with the data provided to the url provided. Parameters From a08493ac93f74350504e59b980472813bea22541 Mon Sep 17 00:00:00 2001 From: Varshitha Bachu Date: Wed, 9 Apr 2025 12:17:20 -0700 Subject: [PATCH 04/10] Rename trace headers in HTTP utils --- azure/durable_functions/models/utils/http_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure/durable_functions/models/utils/http_utils.py b/azure/durable_functions/models/utils/http_utils.py index eaa3a07d..247a8d7d 100644 --- a/azure/durable_functions/models/utils/http_utils.py +++ b/azure/durable_functions/models/utils/http_utils.py @@ -28,9 +28,9 @@ async def post_async_request(url: str, async with aiohttp.ClientSession() as session: headers = {} if trace_parent: - headers["traceparent"] = trace_parent + headers["x-client-traceparent"] = trace_parent if trace_state: - headers["tracestate"] = trace_state + headers["x-client-tracestate"] = trace_state async with session.post(url, json=data, headers=headers) as response: # We disable aiohttp's input type validation # as the server may respond with alternative From 209d4fa96c83be2507f3d129d11318f6162911b9 Mon Sep 17 00:00:00 2001 From: Varshitha Bachu Date: Wed, 23 Apr 2025 11:24:55 -0700 Subject: [PATCH 05/10] removed manually passing in traceparent and tracestate for start_new() --- .../models/DurableOrchestrationClient.py | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/azure/durable_functions/models/DurableOrchestrationClient.py b/azure/durable_functions/models/DurableOrchestrationClient.py index 5842aa24..4775f3b8 100644 --- a/azure/durable_functions/models/DurableOrchestrationClient.py +++ b/azure/durable_functions/models/DurableOrchestrationClient.py @@ -4,6 +4,7 @@ from time import time from asyncio import sleep from urllib.parse import urlparse, quote +from opentelemetry import trace import azure.functions as func @@ -47,9 +48,7 @@ def __init__(self, context: str): async def start_new(self, orchestration_function_name: str, instance_id: Optional[str] = None, - client_input: Optional[Any] = None, - trace_parent: Optional[str] = None, - trace_state: Optional[str] = None) -> str: + client_input: Optional[Any] = None) -> str: """Start a new instance of the specified orchestrator function. If an orchestration instance with the specified ID already exists, the @@ -64,10 +63,6 @@ async def start_new(self, the Durable Functions extension will generate a random GUID (recommended). client_input : Optional[Any] JSON-serializable input value for the orchestrator function. - trace_parent : str - The traceparent header to send with the request. - trace_state : str - The tracestate header to send with the request. Returns ------- @@ -77,6 +72,18 @@ async def start_new(self, request_url = self._get_start_new_url( instance_id=instance_id, orchestration_function_name=orchestration_function_name) + # Get the current span + current_span = trace.get_current_span() + span_context = current_span.get_span_context() + + # Get the traceparent and tracestate from the span context + trace_id = format(span_context.trace_id, '032x') + span_id = format(span_context.span_id, '016x') + trace_flags = format(span_context.trace_flags, '02x') + trace_parent = f"00-{trace_id}-{span_id}-{trace_flags}" + + trace_state = span_context.trace_state + response: List[Any] = await self._post_async_request( request_url, self._get_json_input(client_input), From 93f37071ad6e246be183840315a5faef546c4e10 Mon Sep 17 00:00:00 2001 From: Varshitha Bachu Date: Wed, 23 Apr 2025 13:08:43 -0700 Subject: [PATCH 06/10] update header names to traceparent and tracestate --- azure/durable_functions/models/utils/http_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure/durable_functions/models/utils/http_utils.py b/azure/durable_functions/models/utils/http_utils.py index 247a8d7d..eaa3a07d 100644 --- a/azure/durable_functions/models/utils/http_utils.py +++ b/azure/durable_functions/models/utils/http_utils.py @@ -28,9 +28,9 @@ async def post_async_request(url: str, async with aiohttp.ClientSession() as session: headers = {} if trace_parent: - headers["x-client-traceparent"] = trace_parent + headers["traceparent"] = trace_parent if trace_state: - headers["x-client-tracestate"] = trace_state + headers["tracestate"] = trace_state async with session.post(url, json=data, headers=headers) as response: # We disable aiohttp's input type validation # as the server may respond with alternative From d8244d9f4be6258e73698cef8966b31b395af99d Mon Sep 17 00:00:00 2001 From: Varshitha Bachu Date: Fri, 25 Apr 2025 16:11:12 -0700 Subject: [PATCH 07/10] fixed lint warning --- azure/durable_functions/models/DurableOrchestrationClient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure/durable_functions/models/DurableOrchestrationClient.py b/azure/durable_functions/models/DurableOrchestrationClient.py index 4775f3b8..0495dbb6 100644 --- a/azure/durable_functions/models/DurableOrchestrationClient.py +++ b/azure/durable_functions/models/DurableOrchestrationClient.py @@ -83,7 +83,7 @@ async def start_new(self, trace_parent = f"00-{trace_id}-{span_id}-{trace_flags}" trace_state = span_context.trace_state - + response: List[Any] = await self._post_async_request( request_url, self._get_json_input(client_input), From e1140d90105d471070e8fb94274bf7a78999d401 Mon Sep 17 00:00:00 2001 From: Varshitha Bachu Date: Fri, 25 Apr 2025 16:25:03 -0700 Subject: [PATCH 08/10] addressed PR feedback --- azure/durable_functions/models/DurableOrchestrationClient.py | 2 ++ requirements.txt | 1 + 2 files changed, 3 insertions(+) diff --git a/azure/durable_functions/models/DurableOrchestrationClient.py b/azure/durable_functions/models/DurableOrchestrationClient.py index 0495dbb6..56edf99b 100644 --- a/azure/durable_functions/models/DurableOrchestrationClient.py +++ b/azure/durable_functions/models/DurableOrchestrationClient.py @@ -77,6 +77,8 @@ async def start_new(self, span_context = current_span.get_span_context() # Get the traceparent and tracestate from the span context + # Follows the W3C Trace Context specification for traceparent + # https://www.w3.org/TR/trace-context/#traceparent-header trace_id = format(span_context.trace_id, '032x') span_id = format(span_context.span_id, '016x') trace_flags = format(span_context.trace_flags, '02x') diff --git a/requirements.txt b/requirements.txt index 69900faf..acba43a7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,3 +11,4 @@ furl==2.1.0 pytest-asyncio==0.20.2 autopep8 types-python-dateutil +opentelemetry-api \ No newline at end of file From 3129bbf419dcbdad444fb43eb49182588479d4dd Mon Sep 17 00:00:00 2001 From: Varshitha Bachu Date: Fri, 25 Apr 2025 17:10:07 -0700 Subject: [PATCH 09/10] fixing warning --- azure/durable_functions/models/DurableOrchestrationClient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure/durable_functions/models/DurableOrchestrationClient.py b/azure/durable_functions/models/DurableOrchestrationClient.py index 56edf99b..bd812be9 100644 --- a/azure/durable_functions/models/DurableOrchestrationClient.py +++ b/azure/durable_functions/models/DurableOrchestrationClient.py @@ -83,7 +83,7 @@ async def start_new(self, span_id = format(span_context.span_id, '016x') trace_flags = format(span_context.trace_flags, '02x') trace_parent = f"00-{trace_id}-{span_id}-{trace_flags}" - + trace_state = span_context.trace_state response: List[Any] = await self._post_async_request( From c1a43107b8a3e5a25478fb425b603e9c29d531b5 Mon Sep 17 00:00:00 2001 From: Varshitha Bachu Date: Tue, 29 Apr 2025 16:29:33 -0700 Subject: [PATCH 10/10] updated post() in tests to add trace_parent and trace_state --- tests/models/test_DurableOrchestrationClient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/models/test_DurableOrchestrationClient.py b/tests/models/test_DurableOrchestrationClient.py index 028975ed..6660a2e2 100644 --- a/tests/models/test_DurableOrchestrationClient.py +++ b/tests/models/test_DurableOrchestrationClient.py @@ -67,7 +67,7 @@ async def delete(self, url: str): assert url == self._expected_url return self._response - async def post(self, url: str, data: Any = None): + async def post(self, url: str, data: Any = None, trace_parent: str = None, trace_state: str = None): assert url == self._expected_url return self._response