From 431540a56e9632c87ff1de687ade357374370cfc Mon Sep 17 00:00:00 2001 From: HardMax71 Date: Fri, 25 Sep 2026 16:30:27 +0200 Subject: [PATCH] fix(http): set url.uri_template only when the request has a template A RequestInformation whose URL is set directly (PageIterator, LargeFileUploadTask) has no url_template, and the None went into the span attributes: opentelemetry-sdk up to 1.44 logged a warning for it twice per request, 1.45 exports url.uri_template: None. Fixes #751. --- .../httpx/kiota_http/httpx_request_adapter.py | 4 ++- .../httpx/tests/test_httpx_request_adapter.py | 29 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/http/httpx/kiota_http/httpx_request_adapter.py b/packages/http/httpx/kiota_http/httpx_request_adapter.py index 8fa85187..f6c60b84 100644 --- a/packages/http/httpx/kiota_http/httpx_request_adapter.py +++ b/packages/http/httpx/kiota_http/httpx_request_adapter.py @@ -677,9 +677,11 @@ def get_request_from_request_information( HTTP_REQUEST_METHOD: method.value, SERVER_ADDRESS: url.hostname, URL_SCHEME: url.scheme, - "url.uri_template": request_info.url_template, } + # A request whose URL was set directly (PageIterator, LargeFileUploadTask) has no template + if request_info.url_template is not None: + otel_attributes["url.uri_template"] = request_info.url_template if url.port is not None: otel_attributes["http.port"] = str(url.port) diff --git a/packages/http/httpx/tests/test_httpx_request_adapter.py b/packages/http/httpx/tests/test_httpx_request_adapter.py index 5b508d2e..955119d4 100644 --- a/packages/http/httpx/tests/test_httpx_request_adapter.py +++ b/packages/http/httpx/tests/test_httpx_request_adapter.py @@ -1,4 +1,5 @@ import asyncio +import logging from unittest.mock import AsyncMock, Mock, call, patch from urllib.parse import unquote @@ -7,12 +8,14 @@ from kiota_abstractions.api_error import APIError from kiota_abstractions.method import Method from kiota_abstractions.native_response_handler import NativeResponseHandler +from kiota_abstractions.request_information import RequestInformation from kiota_abstractions.serialization import ( ParseNodeFactoryRegistry, SerializationWriterFactoryRegistry, ) from opentelemetry import trace +import kiota_http.httpx_request_adapter from kiota_http.httpx_request_adapter import HttpxRequestAdapter from kiota_http.middleware import REQUEST_OPTIONS_KEY from kiota_http.middleware.options import ResponseHandlerOption @@ -129,6 +132,32 @@ def test_get_request_from_request_information(request_adapter, request_info, moc assert req.extensions[REQUEST_OPTIONS_KEY] +def test_raw_url_request_has_no_uri_template_attribute(request_adapter, request_info, span_exporter, caplog): + request_info.http_method = Method.GET + request_info.url = "https://graph.microsoft.com/v1.0/me/messages?$skiptoken=abc" + span = kiota_http.httpx_request_adapter.tracer.start_span("parent") + + with caplog.at_level(logging.WARNING, logger="opentelemetry"): + request_adapter.get_request_from_request_information(request_info, span, span) + span.end() + + assert not [r for r in caplog.records if "url.uri_template" in r.getMessage()] + assert all("url.uri_template" not in s.attributes for s in span_exporter.get_finished_spans()) + + +def test_templated_request_keeps_its_uri_template_attribute(request_adapter, span_exporter): + request_info = RequestInformation(Method.GET, "{+baseurl}/me/messages") + request_info.path_parameters = {"baseurl": BASE_URL} + span = kiota_http.httpx_request_adapter.tracer.start_span("parent") + + request_adapter.get_request_from_request_information(request_info, span, span) + span.end() + + assert {s.attributes.get("url.uri_template") for s in span_exporter.get_finished_spans()} == { + "{+baseurl}/me/messages" + } + + def test_get_response_handler(request_adapter, request_info): response_handler_option = ResponseHandlerOption(response_handler=NativeResponseHandler())