Skip to content

Fix for FASTAPI unable to record AppService URL (Issue #3654) #3670

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

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
> Use [this search for a list of all CHANGELOG.md files in this repo](https://github.com/search?q=repo%3Aopen-telemetry%2Fopentelemetry-python-contrib+path%3A**%2FCHANGELOG.md&type=code).

## Unreleased
- `opentelemetry-instrumentation-asgi` Fixed issue where FastAPI reports IP instead of URL.
([#3670](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3670))

## Version 1.36.0/0.57b0 (2025-07-29)

### Fixed

- `opentelemetry-instrumentation`: Fix dependency conflict detection when instrumented packages are not installed by moving check back to before instrumentors are loaded. Add "instruments-any" feature for instrumentations that target multiple packages.
([#3610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3610))
- infra(ci): Fix git pull failures in core contrib test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -445,15 +445,28 @@ def get_host_port_url_tuple(scope):
"""Returns (host, port, full_url) tuple."""
server = scope.get("server") or ["0.0.0.0", 80]
port = server[1]

host_header = asgi_getter.get(scope, "host")
if host_header:
host_value = host_header[0]
# Ensure host_value is a string, not bytes
if isinstance(host_value, bytes):
host_value = host_value.decode("utf-8")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use the already created _decode_header_item helper?


url_host = host_value

else:
url_host = server[0] + (":" + str(port) if str(port) != "80" else "")
server_host = server[0] + (":" + str(port) if str(port) != "80" else "")

# using the scope path is enough, see:
# - https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope (see: root_path and path)
# - https://asgi.readthedocs.io/en/latest/specs/www.html#wsgi-compatibility (see: PATH_INFO)
# PATH_INFO can be derived by stripping root_path from path
# -> that means that the path should contain the root_path already, so prefixing it again is not necessary
# - https://wsgi.readthedocs.io/en/latest/definitions.html#envvar-PATH_INFO
full_path = scope.get("path", "")
http_url = scope.get("scheme", "http") + "://" + server_host + full_path
http_url = scope.get("scheme", "http") + "://" + url_host + full_path
return server_host, port, http_url


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,10 @@ async def test_host_header(self):

def update_expected_server(expected):
expected[3]["attributes"].update(
{SpanAttributes.HTTP_SERVER_NAME: hostname.decode("utf8")}
{
SpanAttributes.HTTP_SERVER_NAME: hostname.decode("utf8"),
SpanAttributes.HTTP_URL: f"http://{hostname.decode('utf8')}/",
}
)
return expected

Expand All @@ -780,7 +783,10 @@ async def test_host_header_both_semconv(self):

def update_expected_server(expected):
expected[3]["attributes"].update(
{SpanAttributes.HTTP_SERVER_NAME: hostname.decode("utf8")}
{
SpanAttributes.HTTP_SERVER_NAME: hostname.decode("utf8"),
SpanAttributes.HTTP_URL: f"http://{hostname.decode('utf8')}/",
}
)
return expected

Expand Down Expand Up @@ -1677,7 +1683,7 @@ def test_request_attributes(self):
SpanAttributes.HTTP_METHOD: "GET",
SpanAttributes.HTTP_HOST: "127.0.0.1",
SpanAttributes.HTTP_TARGET: "/",
SpanAttributes.HTTP_URL: "http://127.0.0.1/?foo=bar",
SpanAttributes.HTTP_URL: "http://test/?foo=bar",
SpanAttributes.NET_HOST_PORT: 80,
SpanAttributes.HTTP_SCHEME: "http",
SpanAttributes.HTTP_SERVER_NAME: "test",
Expand Down Expand Up @@ -1730,7 +1736,7 @@ def test_request_attributes_both_semconv(self):
SpanAttributes.HTTP_METHOD: "GET",
SpanAttributes.HTTP_HOST: "127.0.0.1",
SpanAttributes.HTTP_TARGET: "/",
SpanAttributes.HTTP_URL: "http://127.0.0.1/?foo=bar",
SpanAttributes.HTTP_URL: "http://test/?foo=bar",
SpanAttributes.NET_HOST_PORT: 80,
SpanAttributes.HTTP_SCHEME: "http",
SpanAttributes.HTTP_SERVER_NAME: "test",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ async def test_templated_route_get(self):
self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], "GET")
self.assertEqual(
span.attributes[SpanAttributes.HTTP_URL],
"http://127.0.0.1/route/2020/template/",
"http://testserver/route/2020/template/",
)
self.assertEqual(
span.attributes[SpanAttributes.HTTP_ROUTE],
Expand Down Expand Up @@ -219,7 +219,7 @@ async def test_templated_route_get_both_semconv(self):
self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], "GET")
self.assertEqual(
span.attributes[SpanAttributes.HTTP_URL],
"http://127.0.0.1/route/2020/template/",
"http://testserver/route/2020/template/",
)
self.assertEqual(span.attributes[SpanAttributes.HTTP_SCHEME], "http")
self.assertEqual(span.attributes[SpanAttributes.HTTP_STATUS_CODE], 200)
Expand Down Expand Up @@ -248,7 +248,7 @@ async def test_traced_get(self):
self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], "GET")
self.assertEqual(
span.attributes[SpanAttributes.HTTP_URL],
"http://127.0.0.1/traced/",
"http://testserver/traced/",
)
self.assertEqual(
span.attributes[SpanAttributes.HTTP_ROUTE], "^traced/"
Expand Down Expand Up @@ -289,7 +289,7 @@ async def test_traced_get_both_semconv(self):
self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], "GET")
self.assertEqual(
span.attributes[SpanAttributes.HTTP_URL],
"http://127.0.0.1/traced/",
"http://testserver/traced/",
)
self.assertEqual(span.attributes[SpanAttributes.HTTP_SCHEME], "http")
self.assertEqual(span.attributes[SpanAttributes.HTTP_STATUS_CODE], 200)
Expand Down Expand Up @@ -328,7 +328,7 @@ async def test_traced_post(self):
self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], "POST")
self.assertEqual(
span.attributes[SpanAttributes.HTTP_URL],
"http://127.0.0.1/traced/",
"http://testserver/traced/",
)
self.assertEqual(
span.attributes[SpanAttributes.HTTP_ROUTE], "^traced/"
Expand Down Expand Up @@ -369,7 +369,7 @@ async def test_traced_post_both_semconv(self):
self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], "POST")
self.assertEqual(
span.attributes[SpanAttributes.HTTP_URL],
"http://127.0.0.1/traced/",
"http://testserver/traced/",
)
self.assertEqual(span.attributes[SpanAttributes.HTTP_SCHEME], "http")
self.assertEqual(span.attributes[SpanAttributes.HTTP_STATUS_CODE], 200)
Expand All @@ -396,7 +396,7 @@ async def test_error(self):
self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], "GET")
self.assertEqual(
span.attributes[SpanAttributes.HTTP_URL],
"http://127.0.0.1/error/",
"http://testserver/error/",
)
self.assertEqual(span.attributes[SpanAttributes.HTTP_ROUTE], "^error/")
self.assertEqual(span.attributes[SpanAttributes.HTTP_SCHEME], "http")
Expand Down Expand Up @@ -450,7 +450,7 @@ async def test_error_both_semconv(self):
self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], "GET")
self.assertEqual(
span.attributes[SpanAttributes.HTTP_URL],
"http://127.0.0.1/error/",
"http://testserver/error/",
)
self.assertEqual(span.attributes[SpanAttributes.HTTP_ROUTE], "^error/")
self.assertEqual(span.attributes[SpanAttributes.HTTP_SCHEME], "http")
Expand Down
Loading