Skip to content

Commit

Permalink
[Core] Add new attribute to resent HTTP request spans
Browse files Browse the repository at this point in the history
`DistributedTracingPolicy` will now set an attribute, `http.request.resend_count`,
on HTTP spans for resent requests to indicate the resend attempt count.

Signed-off-by: Paul Van Eck <[email protected]>
  • Loading branch information
pvaneck committed Apr 4, 2024
1 parent acfb396 commit 4060902
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 3 deletions.
4 changes: 3 additions & 1 deletion sdk/core/azure-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

### Features Added

- Tracing: `DistributedTracingPolicy` will now set an attribute, `http.request.resend_count`, on HTTP spans for resent requests to indicate the resend attempt number. #35069

### Breaking Changes

### Bugs Fixed

### Other Changes

- HTTP tracing spans will now include an `error.type` attribute if an error status code is returned. #34619
- Tracing: HTTP tracing spans will now include an `error.type` attribute if an error status code is returned. #34619

## 1.30.1 (2024-02-29)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class DistributedTracingPolicy(SansIOHTTPPolicy[HTTPRequestType, HTTPResponseTyp
TRACING_CONTEXT = "TRACING_CONTEXT"
_REQUEST_ID = "x-ms-client-request-id"
_RESPONSE_ID = "x-ms-request-id"
_HTTP_RESEND_COUNT = "http.request.resend_count"

def __init__(self, **kwargs: Any):
self._network_span_namer = kwargs.get("network_span_namer", _default_network_span_namer)
Expand Down Expand Up @@ -125,6 +126,8 @@ def end_span(
http_request: Union[HttpRequest, LegacyHttpRequest] = request.http_request
if span is not None:
span.set_http_attributes(http_request, response=response)
if request.context.options.get("retry_count"):
span.add_attribute(self._HTTP_RESEND_COUNT, request.context.options["retry_count"])
request_id = http_request.headers.get(self._REQUEST_ID)
if request_id is not None:
span.add_attribute(self._REQUEST_ID, request_id)
Expand Down
1 change: 1 addition & 0 deletions sdk/core/azure-core/azure/core/pipeline/policies/_retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ def send(self, request: PipelineRequest[HTTPRequestType]) -> PipelineResponse[HT
)
try:
self._configure_timeout(request, absolute_timeout, is_response_error)
request.context.options["retry_count"] = len(retry_settings["history"])
response = self.next.send(request)
if self.is_retry(retry_settings, response):
retry_active = self.increment(retry_settings, response=response)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ async def send(
)
try:
self._configure_timeout(request, absolute_timeout, is_response_error)
request.context.options["retry_count"] = len(retry_settings["history"])
response = await self.next.send(request)
if self.is_retry(retry_settings, response):
retry_active = self.increment(retry_settings, response=response)
Expand Down
58 changes: 58 additions & 0 deletions sdk/core/azure-core/tests/async_tests/test_tracing_policy_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# ------------------------------------
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------
"""Tests for the distributed tracing policy in an async pipeline."""

import pytest


from azure.core.pipeline import AsyncPipeline
from azure.core.pipeline.policies import AsyncRetryPolicy, DistributedTracingPolicy
from azure.core.pipeline.transport import (
HttpResponse,
AsyncHttpTransport,
)
from azure.core.settings import settings

from tracing_common import FakeSpan
from utils import HTTP_REQUESTS


@pytest.mark.asyncio
@pytest.mark.parametrize("http_request", HTTP_REQUESTS)
async def test_span_retry_attributes(http_request):
class MockTransport(AsyncHttpTransport):
def __init__(self):
self._count = 0

async def __aexit__(self, exc_type, exc_val, exc_tb):
pass

async def close(self):
pass

async def open(self):
pass

async def send(self, request, **kwargs):
self._count += 1
response = HttpResponse(request, None)
response.status_code = 429
return response

http_request = http_request("GET", "http://localhost/")
retry_policy = AsyncRetryPolicy(retry_total=2)
distributed_tracing_policy = DistributedTracingPolicy()
transport = MockTransport()

settings.tracing_implementation.set_value(FakeSpan)
with FakeSpan(name="parent") as root_span:
pipeline = AsyncPipeline(transport, [retry_policy, distributed_tracing_policy])
await pipeline.run(http_request)

assert transport._count == 3
assert len(root_span.children) == 3
assert root_span.children[0].attributes.get("http.request.resend_count") is None
assert root_span.children[1].attributes.get("http.request.resend_count") == 1
assert root_span.children[2].attributes.get("http.request.resend_count") == 2
43 changes: 41 additions & 2 deletions sdk/core/azure-core/tests/test_tracing_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
"""Tests for the distributed tracing policy."""
import logging

from azure.core.pipeline import PipelineResponse, PipelineRequest, PipelineContext
from azure.core.pipeline.policies import DistributedTracingPolicy, UserAgentPolicy
from azure.core.pipeline import Pipeline, PipelineResponse, PipelineRequest, PipelineContext
from azure.core.pipeline.policies import DistributedTracingPolicy, UserAgentPolicy, RetryPolicy
from azure.core.pipeline.transport import HttpTransport
from azure.core.settings import settings
from tracing_common import FakeSpan
import time
Expand Down Expand Up @@ -210,6 +211,44 @@ def test_distributed_tracing_policy_with_user_agent(http_request, http_response)
assert network_span.status == "Transport trouble"


@pytest.mark.parametrize("http_request,http_response", request_and_responses_product(HTTP_RESPONSES))
def test_span_retry_attributes(http_request, http_response):
class MockTransport(HttpTransport):
def __init__(self):
self._count = 0

def __exit__(self, exc_type, exc_val, exc_tb):
pass

def close(self):
pass

def open(self):
pass

def send(self, request, **kwargs):
self._count += 1
response = create_http_response(http_response, request, None)
response.status_code = 429
return response

settings.tracing_implementation.set_value(FakeSpan)

http_request = http_request("GET", "http://localhost/")
retry_policy = RetryPolicy(retry_total=2)
distributed_tracing_policy = DistributedTracingPolicy()
transport = MockTransport()

with FakeSpan(name="parent") as root_span:
pipeline = Pipeline(transport, [retry_policy, distributed_tracing_policy])
pipeline.run(http_request)
assert transport._count == 3
assert len(root_span.children) == 3
assert root_span.children[0].attributes.get("http.request.resend_count") is None
assert root_span.children[1].attributes.get("http.request.resend_count") == 1
assert root_span.children[2].attributes.get("http.request.resend_count") == 2


@pytest.mark.parametrize("http_request,http_response", request_and_responses_product(HTTP_RESPONSES))
def test_span_namer(http_request, http_response):
settings.tracing_implementation.set_value(FakeSpan)
Expand Down

0 comments on commit 4060902

Please sign in to comment.