opentelemetry-instrumentation-tornado 0.46b0__tar.gz → 0.48b0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (13) hide show
  1. {opentelemetry_instrumentation_tornado-0.46b0 → opentelemetry_instrumentation_tornado-0.48b0}/.gitignore +3 -0
  2. {opentelemetry_instrumentation_tornado-0.46b0 → opentelemetry_instrumentation_tornado-0.48b0}/PKG-INFO +5 -4
  3. {opentelemetry_instrumentation_tornado-0.46b0 → opentelemetry_instrumentation_tornado-0.48b0}/pyproject.toml +4 -3
  4. {opentelemetry_instrumentation_tornado-0.46b0 → opentelemetry_instrumentation_tornado-0.48b0}/src/opentelemetry/instrumentation/tornado/__init__.py +1 -1
  5. {opentelemetry_instrumentation_tornado-0.46b0 → opentelemetry_instrumentation_tornado-0.48b0}/src/opentelemetry/instrumentation/tornado/client.py +36 -20
  6. {opentelemetry_instrumentation_tornado-0.46b0 → opentelemetry_instrumentation_tornado-0.48b0}/src/opentelemetry/instrumentation/tornado/version.py +1 -1
  7. {opentelemetry_instrumentation_tornado-0.46b0 → opentelemetry_instrumentation_tornado-0.48b0}/tests/test_instrumentation.py +45 -2
  8. {opentelemetry_instrumentation_tornado-0.46b0 → opentelemetry_instrumentation_tornado-0.48b0}/LICENSE +0 -0
  9. {opentelemetry_instrumentation_tornado-0.46b0 → opentelemetry_instrumentation_tornado-0.48b0}/README.rst +0 -0
  10. {opentelemetry_instrumentation_tornado-0.46b0 → opentelemetry_instrumentation_tornado-0.48b0}/src/opentelemetry/instrumentation/tornado/package.py +0 -0
  11. {opentelemetry_instrumentation_tornado-0.46b0 → opentelemetry_instrumentation_tornado-0.48b0}/tests/__init__.py +0 -0
  12. {opentelemetry_instrumentation_tornado-0.46b0 → opentelemetry_instrumentation_tornado-0.48b0}/tests/test_metrics_instrumentation.py +0 -0
  13. {opentelemetry_instrumentation_tornado-0.46b0 → opentelemetry_instrumentation_tornado-0.48b0}/tests/tornado_test_app.py +0 -0
@@ -58,3 +58,6 @@ _build/
58
58
  # mypy
59
59
  .mypy_cache/
60
60
  target
61
+
62
+ # Benchmark result files
63
+ *-benchmark.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: opentelemetry-instrumentation-tornado
3
- Version: 0.46b0
3
+ Version: 0.48b0
4
4
  Summary: Tornado instrumentation for OpenTelemetry
5
5
  Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-tornado
6
6
  Author-email: OpenTelemetry Authors <cncf-opentelemetry-contributors@lists.cncf.io>
@@ -14,11 +14,12 @@ Classifier: Programming Language :: Python :: 3.8
14
14
  Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
17
18
  Requires-Python: >=3.8
18
19
  Requires-Dist: opentelemetry-api~=1.12
19
- Requires-Dist: opentelemetry-instrumentation==0.46b0
20
- Requires-Dist: opentelemetry-semantic-conventions==0.46b0
21
- Requires-Dist: opentelemetry-util-http==0.46b0
20
+ Requires-Dist: opentelemetry-instrumentation==0.48b0
21
+ Requires-Dist: opentelemetry-semantic-conventions==0.48b0
22
+ Requires-Dist: opentelemetry-util-http==0.48b0
22
23
  Provides-Extra: instruments
23
24
  Requires-Dist: tornado>=5.1.1; extra == 'instruments'
24
25
  Description-Content-Type: text/x-rst
@@ -21,12 +21,13 @@ classifiers = [
21
21
  "Programming Language :: Python :: 3.9",
22
22
  "Programming Language :: Python :: 3.10",
23
23
  "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
24
25
  ]
25
26
  dependencies = [
26
27
  "opentelemetry-api ~= 1.12",
27
- "opentelemetry-instrumentation == 0.46b0",
28
- "opentelemetry-semantic-conventions == 0.46b0",
29
- "opentelemetry-util-http == 0.46b0",
28
+ "opentelemetry-instrumentation == 0.48b0",
29
+ "opentelemetry-semantic-conventions == 0.48b0",
30
+ "opentelemetry-util-http == 0.48b0",
30
31
  ]
31
32
 
32
33
  [project.optional-dependencies]
@@ -296,7 +296,7 @@ def _create_server_histograms(meter) -> Dict[str, Histogram]:
296
296
  MetricInstruments.HTTP_SERVER_DURATION: meter.create_histogram(
297
297
  name=MetricInstruments.HTTP_SERVER_DURATION,
298
298
  unit="ms",
299
- description="Duration of HTTP client requests.",
299
+ description="Duration of HTTP server requests.",
300
300
  ),
301
301
  MetricInstruments.HTTP_SERVER_REQUEST_SIZE: meter.create_histogram(
302
302
  name=MetricInstruments.HTTP_SERVER_REQUEST_SIZE,
@@ -21,7 +21,7 @@ from opentelemetry import trace
21
21
  from opentelemetry.instrumentation.utils import http_status_to_status_code
22
22
  from opentelemetry.propagate import inject
23
23
  from opentelemetry.semconv.trace import SpanAttributes
24
- from opentelemetry.trace.status import Status
24
+ from opentelemetry.trace.status import Status, StatusCode
25
25
  from opentelemetry.util.http import remove_url_credentials
26
26
 
27
27
 
@@ -105,37 +105,53 @@ def _finish_tracing_callback(
105
105
  request_size_histogram,
106
106
  response_size_histogram,
107
107
  ):
108
+ response = None
108
109
  status_code = None
110
+ status = None
109
111
  description = None
110
- exc = future.exception()
111
-
112
- response = future.result()
113
112
 
114
- if span.is_recording() and exc:
113
+ exc = future.exception()
114
+ if exc:
115
+ description = f"{type(exc).__qualname__}: {exc}"
115
116
  if isinstance(exc, HTTPError):
117
+ response = exc.response
116
118
  status_code = exc.code
117
- description = f"{type(exc).__name__}: {exc}"
119
+ status = Status(
120
+ status_code=http_status_to_status_code(status_code),
121
+ description=description,
122
+ )
123
+ else:
124
+ status = Status(
125
+ status_code=StatusCode.ERROR,
126
+ description=description,
127
+ )
128
+ span.record_exception(exc)
118
129
  else:
130
+ response = future.result()
119
131
  status_code = response.code
132
+ status = Status(
133
+ status_code=http_status_to_status_code(status_code),
134
+ description=description,
135
+ )
120
136
 
121
137
  if status_code is not None:
122
138
  span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code)
123
- span.set_status(
124
- Status(
125
- status_code=http_status_to_status_code(status_code),
126
- description=description,
127
- )
128
- )
139
+ span.set_status(status)
129
140
 
130
- metric_attributes = _create_metric_attributes(response)
131
- request_size = int(response.request.headers.get("Content-Length", 0))
132
- response_size = int(response.headers.get("Content-Length", 0))
141
+ if response is not None:
142
+ metric_attributes = _create_metric_attributes(response)
143
+ request_size = int(response.request.headers.get("Content-Length", 0))
144
+ response_size = int(response.headers.get("Content-Length", 0))
133
145
 
134
- duration_histogram.record(
135
- response.request_time, attributes=metric_attributes
136
- )
137
- request_size_histogram.record(request_size, attributes=metric_attributes)
138
- response_size_histogram.record(response_size, attributes=metric_attributes)
146
+ duration_histogram.record(
147
+ response.request_time, attributes=metric_attributes
148
+ )
149
+ request_size_histogram.record(
150
+ request_size, attributes=metric_attributes
151
+ )
152
+ response_size_histogram.record(
153
+ response_size, attributes=metric_attributes
154
+ )
139
155
 
140
156
  if response_hook:
141
157
  response_hook(span, future)
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- __version__ = "0.46b0"
15
+ __version__ = "0.48b0"
@@ -16,6 +16,7 @@
16
16
  from unittest.mock import Mock, patch
17
17
 
18
18
  from http_server_mock import HttpServerMock
19
+ from tornado.httpclient import HTTPClientError
19
20
  from tornado.testing import AsyncHTTPTestCase
20
21
 
21
22
  from opentelemetry import trace
@@ -32,7 +33,7 @@ from opentelemetry.instrumentation.tornado import (
32
33
  from opentelemetry.semconv.trace import SpanAttributes
33
34
  from opentelemetry.test.test_base import TestBase
34
35
  from opentelemetry.test.wsgitestutil import WsgiTestBase
35
- from opentelemetry.trace import SpanKind
36
+ from opentelemetry.trace import SpanKind, StatusCode
36
37
  from opentelemetry.util.http import (
37
38
  OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST,
38
39
  OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE,
@@ -493,7 +494,6 @@ class TestTornadoInstrumentation(TornadoTest, WsgiTestBase):
493
494
  self.assertEqual(len(spans), 3)
494
495
  self.assertTraceResponseHeaderMatchesSpan(response.headers, spans[1])
495
496
 
496
- self.memory_exporter.clear()
497
497
  set_global_response_propagator(orig)
498
498
 
499
499
  def test_credential_removal(self):
@@ -602,6 +602,49 @@ class TornadoHookTest(TornadoTest):
602
602
  self.memory_exporter.clear()
603
603
 
604
604
 
605
+ class TestTornadoHTTPClientInstrumentation(TornadoTest, WsgiTestBase):
606
+ def test_http_client_success_response(self):
607
+ response = self.fetch("/")
608
+ self.assertEqual(response.code, 201)
609
+
610
+ spans = self.memory_exporter.get_finished_spans()
611
+ self.assertEqual(len(spans), 3)
612
+ manual, server, client = self.sorted_spans(spans)
613
+ self.assertEqual(manual.name, "manual")
614
+ self.assertEqual(server.name, "GET /")
615
+ self.assertEqual(client.name, "GET")
616
+ self.assertEqual(client.status.status_code, StatusCode.UNSET)
617
+ self.memory_exporter.clear()
618
+
619
+ def test_http_client_failed_response(self):
620
+ # when an exception isn't thrown
621
+ response = self.fetch("/some-404")
622
+ self.assertEqual(response.code, 404)
623
+
624
+ spans = self.memory_exporter.get_finished_spans()
625
+ self.assertEqual(len(spans), 2)
626
+ server, client = self.sorted_spans(spans)
627
+ self.assertEqual(server.name, "GET /some-404")
628
+ self.assertEqual(client.name, "GET")
629
+ self.assertEqual(client.status.status_code, StatusCode.ERROR)
630
+ self.memory_exporter.clear()
631
+
632
+ # when an exception is thrown
633
+ try:
634
+ response = self.fetch("/some-404", raise_error=True)
635
+ self.assertEqual(response.code, 404)
636
+ except HTTPClientError:
637
+ pass # expected exception - continue
638
+
639
+ spans = self.memory_exporter.get_finished_spans()
640
+ self.assertEqual(len(spans), 2)
641
+ server, client = self.sorted_spans(spans)
642
+ self.assertEqual(server.name, "GET /some-404")
643
+ self.assertEqual(client.name, "GET")
644
+ self.assertEqual(client.status.status_code, StatusCode.ERROR)
645
+ self.memory_exporter.clear()
646
+
647
+
605
648
  class TestTornadoUninstrument(TornadoTest):
606
649
  def test_uninstrument(self):
607
650
  response = self.fetch("/")