Skip to content
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

fix(tornado): ensure reading future.result() won't throw an exception in client.py _finish_tracing_callback #2563

Merged
merged 29 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
38ebaf6
fix(tornado): ensure reading future.result() won't throw an exception…
jgibo May 28, 2024
6220697
add test cases
jgibo May 29, 2024
245c557
improve test 'test_http_client_failed_response'
jgibo May 29, 2024
a13049a
uncomment dev only change
jgibo May 29, 2024
516de12
fix test
jgibo May 29, 2024
cbb3e4d
lint fix
jgibo May 29, 2024
9aebb65
update changelog
jgibo May 29, 2024
ca6cfc9
remove debugging prints
jgibo May 29, 2024
4280382
Revert "lint fix"
jgibo May 29, 2024
a55a22e
lint fix tornado dir only
jgibo May 29, 2024
dfd4198
remove unused import
jgibo May 29, 2024
18fbf80
Remove 'general exception' catch clause
jgibo May 29, 2024
b60cfc8
Merge branch 'main' into issue_2555
jgibo May 31, 2024
4d9613e
Merge branch 'open-telemetry:main' into issue_2555
jgibo Jun 10, 2024
8256c46
Merge branch 'main' into issue_2555
jgibo Jun 10, 2024
632452a
extract response from exception and record its info into span
jgibo Jun 26, 2024
d9ed843
improvement, make sure we record a non HTTPError as well
jgibo Jun 26, 2024
a1a8caa
update changelog
jgibo Jun 26, 2024
24c75ad
Merge branch 'main' into issue_2555
jgibo Jun 26, 2024
8f1a52f
get status_code from exception instead of the optional exception resp…
jgibo Jul 9, 2024
d3e0d25
Merge branch 'main' into issue_2555, fix changelog conflict
jgibo Jul 10, 2024
90eb089
Merge branch 'main' into issue_2555, fix changelog conflict
jgibo Aug 2, 2024
d96e92c
Update CHANGELOG.md
jgibo Aug 5, 2024
4937ee7
Merge branch 'main' into issue_2555, fix changelog conflict
jgibo Aug 5, 2024
e1963f0
Merge branch 'main' into issue_2555
jgibo Aug 7, 2024
20b2846
Merge branch 'main' into issue_2555, fix changelog conflict
jgibo Aug 13, 2024
252feba
record exception when exc is not a HTTPError
jgibo Aug 13, 2024
5a841a7
code review suggestion - use __qualname__
jgibo Aug 14, 2024
9b37b88
Merge branch 'main' into issue_2555
jgibo Aug 19, 2024
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `opentelemetry-instrumentation-requests` Fix wrong time unit for duration histogram
([#2553](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2553))
- `opentelemetry-util-http` Preserve brackets around literal IPv6 hosts ([#2552](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2552))
- `opentelemetry-instrumentation-tornado` Fix not handling a http client exception
xrmx marked this conversation as resolved.
Show resolved Hide resolved
([#2563](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2563))

## Version 1.24.0/0.45b0 (2024-03-28)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,14 @@ def _finish_tracing_callback(
description = None
exc = future.exception()

response = future.result()

if span.is_recording() and exc:
if isinstance(exc, HTTPError):
status_code = exc.code
description = f"{type(exc).__name__}: {exc}"
else:

response = None
if not exc:
jgibo marked this conversation as resolved.
Show resolved Hide resolved
response = future.result()
status_code = response.code

if status_code is not None:
Expand All @@ -127,15 +128,20 @@ def _finish_tracing_callback(
)
)

metric_attributes = _create_metric_attributes(response)
request_size = int(response.request.headers.get("Content-Length", 0))
response_size = int(response.headers.get("Content-Length", 0))
if response is not None:
metric_attributes = _create_metric_attributes(response)
request_size = int(response.request.headers.get("Content-Length", 0))
response_size = int(response.headers.get("Content-Length", 0))

duration_histogram.record(
response.request_time, attributes=metric_attributes
)
request_size_histogram.record(request_size, attributes=metric_attributes)
response_size_histogram.record(response_size, attributes=metric_attributes)
duration_histogram.record(
response.request_time, attributes=metric_attributes
)
request_size_histogram.record(
request_size, attributes=metric_attributes
)
response_size_histogram.record(
response_size, attributes=metric_attributes
)

if response_hook:
response_hook(span, future)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@


from unittest.mock import Mock, patch
from urllib.error import HTTPError

from http_server_mock import HttpServerMock
from tornado.httpclient import HTTPClientError
from tornado.testing import AsyncHTTPTestCase

from opentelemetry import trace
Expand Down Expand Up @@ -602,6 +604,50 @@ def client_response_hook(span, response):
self.memory_exporter.clear()


class TestTornadoHTTPClientInstrumentation(TornadoTest, WsgiTestBase):
def test_http_client_success_response(self):
response = self.fetch("/")
self.assertEqual(response.code, 201)

spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 3)
manual, server, client = self.sorted_spans(spans)
self.assertEqual(manual.name, "manual")
self.assertEqual(server.name, "GET /")
self.assertEqual(client.name, "GET")
self.memory_exporter.clear()

def test_http_client_failed_response(self):
# when an exception isn't thrown
response = self.fetch("/some-404")
self.assertEqual(response.code, 404)

spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 2)
server, client = self.sorted_spans(spans)
self.assertEqual(server.name, "GET /some-404")
self.assertEqual(client.name, "GET")
self.memory_exporter.clear()
print("server span", server)
xrmx marked this conversation as resolved.
Show resolved Hide resolved

# when an exception is thrown
jgibo marked this conversation as resolved.
Show resolved Hide resolved
try:
response = self.fetch("/some-404", raise_error=True)
self.assertEqual(response.code, 404)
except HTTPClientError:
pass
except Exception as e:
print("Exception type", type(e))
xrmx marked this conversation as resolved.
Show resolved Hide resolved
self.fail(f"Unexpected exception: {e}")

spans = self.memory_exporter.get_finished_spans()
self.assertEqual(len(spans), 2)
server, client = self.sorted_spans(spans)
self.assertEqual(server.name, "GET /some-404")
self.assertEqual(client.name, "GET")
self.memory_exporter.clear()


class TestTornadoUninstrument(TornadoTest):
def test_uninstrument(self):
response = self.fetch("/")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Optional
from os import environ
from typing import Optional

from opentelemetry.resource.detector.azure._utils import (
_get_azure_resource_uri,
_is_on_functions,
)
from opentelemetry.sdk.resources import Resource, ResourceDetector
from opentelemetry.semconv.resource import (
CloudPlatformValues,
CloudProviderValues,
ResourceAttributes,
)
from opentelemetry.resource.detector.azure._utils import _get_azure_resource_uri

from ._constants import (
_APP_SERVICE_ATTRIBUTE_ENV_VARS,
_WEBSITE_SITE_NAME,
)

from opentelemetry.resource.detector.azure._utils import _is_on_functions
from ._constants import _APP_SERVICE_ATTRIBUTE_ENV_VARS, _WEBSITE_SITE_NAME


class AzureAppServiceResourceDetector(ResourceDetector):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

from os import environ, getpid

from opentelemetry.resource.detector.azure._utils import (
_get_azure_resource_uri,
_is_on_functions,
)
from opentelemetry.sdk.resources import Resource, ResourceDetector
from opentelemetry.semconv.resource import (
CloudPlatformValues,
Expand All @@ -26,10 +30,6 @@
_REGION_NAME,
_WEBSITE_SITE_NAME,
)
from opentelemetry.resource.detector.azure._utils import (
_get_azure_resource_uri,
_is_on_functions,
)


class AzureFunctionsResourceDetector(ResourceDetector):
Expand Down Expand Up @@ -65,4 +65,3 @@ def detect(self) -> Resource:
attributes[key] = value

return Resource(attributes)

Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def test_on_app_service(self):
self.assertEqual(
attributes["azure.app.service.stamp"], TEST_WEBSITE_HOME_STAMPNAME
)

@patch.dict(
"os.environ",
{
Expand Down
8 changes: 6 additions & 2 deletions util/opentelemetry-util-http/tests/test_remove_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ def test_remove_credentials(self):
def test_remove_credentials_ipv4_literal(self):
url = "http://someuser:[email protected]:8080/test/path?query=value"
cleaned_url = remove_url_credentials(url)
self.assertEqual(cleaned_url, "http://127.0.0.1:8080/test/path?query=value")
self.assertEqual(
cleaned_url, "http://127.0.0.1:8080/test/path?query=value"
)

def test_remove_credentials_ipv6_literal(self):
url = "http://someuser:somepass@[::1]:8080/test/path?query=value"
cleaned_url = remove_url_credentials(url)
self.assertEqual(cleaned_url, "http://[::1]:8080/test/path?query=value")
self.assertEqual(
cleaned_url, "http://[::1]:8080/test/path?query=value"
)
Loading