Change metrics test to work with base_test.py (#1688)

This commit is contained in:
Shalev Roda
2023-02-25 00:05:45 +02:00
committed by GitHub
parent a50caf26bc
commit a7bd56354b
5 changed files with 358 additions and 393 deletions

View File

@ -6,7 +6,7 @@ on:
- 'release/*' - 'release/*'
pull_request: pull_request:
env: env:
CORE_REPO_SHA: d0bb12b34b0c487198c935001636b6163485a50f CORE_REPO_SHA: 2d1f0b9f5fce62549d1338882f37b91b95881c75
jobs: jobs:
build: build:

View File

@ -15,97 +15,23 @@
from timeit import default_timer from timeit import default_timer
from tornado.testing import AsyncHTTPTestCase
from opentelemetry import trace
from opentelemetry.instrumentation.tornado import TornadoInstrumentor from opentelemetry.instrumentation.tornado import TornadoInstrumentor
from opentelemetry.sdk.metrics.export import ( from opentelemetry.sdk.metrics.export import HistogramDataPoint
HistogramDataPoint,
NumberDataPoint, from .test_instrumentation import ( # pylint: disable=no-name-in-module,import-error
TornadoTest,
) )
from opentelemetry.test.test_base import TestBase
from .tornado_test_app import make_app
class TornadoTest(AsyncHTTPTestCase, TestBase): class TestTornadoMetricsInstrumentation(TornadoTest):
# pylint:disable=no-self-use # Return Sequence with one histogram
def get_app(self): def create_histogram_data_points(self, sum_data_point, attributes):
tracer = trace.get_tracer(__name__) return [
app = make_app(tracer) self.create_histogram_data_point(
return app sum_data_point, 1, sum_data_point, sum_data_point, attributes
def get_sorted_metrics(self):
resource_metrics = (
self.memory_metrics_reader.get_metrics_data().resource_metrics
)
for metrics in resource_metrics:
for scope_metrics in metrics.scope_metrics:
all_metrics = list(scope_metrics.metrics)
return self.sorted_metrics(all_metrics)
@staticmethod
def sorted_metrics(metrics):
"""
Sorts metrics by metric name.
"""
return sorted(
metrics,
key=lambda m: m.name,
)
def assert_metric_expected(
self, metric, expected_value, expected_attributes
):
data_point = next(iter(metric.data.data_points))
if isinstance(data_point, HistogramDataPoint):
self.assertEqual(
data_point.sum,
expected_value,
)
elif isinstance(data_point, NumberDataPoint):
self.assertEqual(
data_point.value,
expected_value,
) )
]
self.assertDictEqual(
expected_attributes,
dict(data_point.attributes),
)
def assert_duration_metric_expected(
self, metric, duration_estimated, expected_attributes
):
data_point = next(iter(metric.data.data_points))
self.assertAlmostEqual(
data_point.sum,
duration_estimated,
delta=200,
)
self.assertDictEqual(
expected_attributes,
dict(data_point.attributes),
)
def setUp(self):
super().setUp()
TornadoInstrumentor().instrument(
server_request_hook=getattr(self, "server_request_hook", None),
client_request_hook=getattr(self, "client_request_hook", None),
client_response_hook=getattr(self, "client_response_hook", None),
meter_provider=self.meter_provider,
)
def tearDown(self):
TornadoInstrumentor().uninstrument()
super().tearDown()
class TestTornadoInstrumentor(TornadoTest):
def test_basic_metrics(self): def test_basic_metrics(self):
start_time = default_timer() start_time = default_timer()
response = self.fetch("/") response = self.fetch("/")
@ -132,42 +58,51 @@ class TestTornadoInstrumentor(TornadoTest):
) )
self.assert_metric_expected( self.assert_metric_expected(
server_active_request, server_active_request,
0, [
{ self.create_number_data_point(
"http.method": "GET", 0,
"http.flavor": "HTTP/1.1", attributes={
"http.scheme": "http", "http.method": "GET",
"http.target": "/", "http.flavor": "HTTP/1.1",
"http.host": response.request.headers["host"], "http.scheme": "http",
}, "http.target": "/",
"http.host": response.request.headers["host"],
},
),
],
) )
self.assertEqual(server_duration.name, "http.server.duration") self.assertEqual(server_duration.name, "http.server.duration")
self.assert_duration_metric_expected( self.assert_metric_expected(
server_duration, server_duration,
client_duration_estimated, self.create_histogram_data_points(
{ client_duration_estimated,
"http.status_code": response.code, attributes={
"http.method": "GET", "http.method": "GET",
"http.flavor": "HTTP/1.1", "http.flavor": "HTTP/1.1",
"http.scheme": "http", "http.scheme": "http",
"http.target": "/", "http.target": "/",
"http.host": response.request.headers["host"], "http.host": response.request.headers["host"],
}, "http.status_code": response.code,
},
),
est_value_delta=200,
) )
self.assertEqual(server_request_size.name, "http.server.request.size") self.assertEqual(server_request_size.name, "http.server.request.size")
self.assert_metric_expected( self.assert_metric_expected(
server_request_size, server_request_size,
0, self.create_histogram_data_points(
{ 0,
"http.status_code": 200, attributes={
"http.method": "GET", "http.status_code": 200,
"http.flavor": "HTTP/1.1", "http.method": "GET",
"http.scheme": "http", "http.flavor": "HTTP/1.1",
"http.target": "/", "http.scheme": "http",
"http.host": response.request.headers["host"], "http.target": "/",
}, "http.host": response.request.headers["host"],
},
),
) )
self.assertEqual( self.assertEqual(
@ -175,37 +110,44 @@ class TestTornadoInstrumentor(TornadoTest):
) )
self.assert_metric_expected( self.assert_metric_expected(
server_response_size, server_response_size,
len(response.body), self.create_histogram_data_points(
{ len(response.body),
"http.status_code": response.code, attributes={
"http.method": "GET", "http.status_code": response.code,
"http.flavor": "HTTP/1.1", "http.method": "GET",
"http.scheme": "http", "http.flavor": "HTTP/1.1",
"http.target": "/", "http.scheme": "http",
"http.host": response.request.headers["host"], "http.target": "/",
}, "http.host": response.request.headers["host"],
},
),
) )
self.assertEqual(client_duration.name, "http.client.duration") self.assertEqual(client_duration.name, "http.client.duration")
self.assert_duration_metric_expected( self.assert_metric_expected(
client_duration, client_duration,
client_duration_estimated, self.create_histogram_data_points(
{ client_duration_estimated,
"http.status_code": response.code, attributes={
"http.method": "GET", "http.status_code": response.code,
"http.url": response.effective_url, "http.method": "GET",
}, "http.url": response.effective_url,
},
),
est_value_delta=200,
) )
self.assertEqual(client_request_size.name, "http.client.request.size") self.assertEqual(client_request_size.name, "http.client.request.size")
self.assert_metric_expected( self.assert_metric_expected(
client_request_size, client_request_size,
0, self.create_histogram_data_points(
{ 0,
"http.status_code": response.code, attributes={
"http.method": "GET", "http.status_code": response.code,
"http.url": response.effective_url, "http.method": "GET",
}, "http.url": response.effective_url,
},
),
) )
self.assertEqual( self.assertEqual(
@ -213,12 +155,14 @@ class TestTornadoInstrumentor(TornadoTest):
) )
self.assert_metric_expected( self.assert_metric_expected(
client_response_size, client_response_size,
len(response.body), self.create_histogram_data_points(
{ len(response.body),
"http.status_code": response.code, attributes={
"http.method": "GET", "http.status_code": response.code,
"http.url": response.effective_url, "http.method": "GET",
}, "http.url": response.effective_url,
},
),
) )
def test_metric_uninstrument(self): def test_metric_uninstrument(self):
@ -226,10 +170,10 @@ class TestTornadoInstrumentor(TornadoTest):
TornadoInstrumentor().uninstrument() TornadoInstrumentor().uninstrument()
self.fetch("/") self.fetch("/")
metrics_list = self.memory_metrics_reader.get_metrics_data() metrics = self.get_sorted_metrics()
for resource_metric in metrics_list.resource_metrics: self.assertEqual(len(metrics), 7)
for scope_metric in resource_metric.scope_metrics:
for metric in scope_metric.metrics: for metric in metrics:
for point in list(metric.data.data_points): for point in list(metric.data.data_points):
if isinstance(point, HistogramDataPoint): if isinstance(point, HistogramDataPoint):
self.assertEqual(point.count, 1) self.assertEqual(point.count, 1)

View File

@ -14,7 +14,6 @@
from timeit import default_timer from timeit import default_timer
from typing import Optional, Union
from urllib import request from urllib import request
from urllib.parse import urlencode from urllib.parse import urlencode
@ -23,16 +22,11 @@ import httpretty
from opentelemetry.instrumentation.urllib import ( # pylint: disable=no-name-in-module,import-error from opentelemetry.instrumentation.urllib import ( # pylint: disable=no-name-in-module,import-error
URLLibInstrumentor, URLLibInstrumentor,
) )
from opentelemetry.sdk.metrics._internal.point import Metric
from opentelemetry.sdk.metrics.export import (
HistogramDataPoint,
NumberDataPoint,
)
from opentelemetry.semconv.metrics import MetricInstruments from opentelemetry.semconv.metrics import MetricInstruments
from opentelemetry.test.test_base import TestBase from opentelemetry.test.test_base import TestBase
class TestRequestsIntegration(TestBase): class TestUrllibMetricsInstrumentation(TestBase):
URL = "http://httpbin.org/status/200" URL = "http://httpbin.org/status/200"
URL_POST = "http://httpbin.org/post" URL_POST = "http://httpbin.org/post"
@ -50,63 +44,13 @@ class TestRequestsIntegration(TestBase):
URLLibInstrumentor().uninstrument() URLLibInstrumentor().uninstrument()
httpretty.disable() httpretty.disable()
def get_sorted_metrics(self): # Return Sequence with one histogram
resource_metrics = ( def create_histogram_data_points(self, sum_data_point, attributes):
self.memory_metrics_reader.get_metrics_data().resource_metrics return [
) self.create_histogram_data_point(
sum_data_point, 1, sum_data_point, sum_data_point, attributes
all_metrics = []
for metrics in resource_metrics:
for scope_metrics in metrics.scope_metrics:
all_metrics.extend(scope_metrics.metrics)
return self.sorted_metrics(all_metrics)
@staticmethod
def sorted_metrics(metrics):
"""
Sorts metrics by metric name.
"""
return sorted(
metrics,
key=lambda m: m.name,
)
def assert_metric_expected(
self,
metric: Metric,
expected_value: Union[int, float],
expected_attributes: dict,
est_delta: Optional[float] = None,
):
data_point = next(iter(metric.data.data_points))
if isinstance(data_point, HistogramDataPoint):
self.assertEqual(
data_point.count,
1,
) )
if est_delta is None: ]
self.assertEqual(
data_point.sum,
expected_value,
)
else:
self.assertAlmostEqual(
data_point.sum,
expected_value,
delta=est_delta,
)
elif isinstance(data_point, NumberDataPoint):
self.assertEqual(
data_point.value,
expected_value,
)
self.assertDictEqual(
expected_attributes,
dict(data_point.attributes),
)
def test_basic_metric(self): def test_basic_metric(self):
start_time = default_timer() start_time = default_timer()
@ -127,31 +71,33 @@ class TestRequestsIntegration(TestBase):
) )
self.assert_metric_expected( self.assert_metric_expected(
client_duration, client_duration,
client_duration_estimated, self.create_histogram_data_points(
{ client_duration_estimated,
"http.status_code": str(result.code), attributes={
"http.method": "GET", "http.status_code": str(result.code),
"http.url": str(result.url), "http.method": "GET",
"http.flavor": "1.1", "http.url": str(result.url),
}, "http.flavor": "1.1",
est_delta=200, },
),
est_value_delta=200,
) )
# net.peer.name
self.assertEqual( self.assertEqual(
client_request_size.name, client_request_size.name,
MetricInstruments.HTTP_CLIENT_REQUEST_SIZE, MetricInstruments.HTTP_CLIENT_REQUEST_SIZE,
) )
self.assert_metric_expected( self.assert_metric_expected(
client_request_size, client_request_size,
0, self.create_histogram_data_points(
{ 0,
"http.status_code": str(result.code), attributes={
"http.method": "GET", "http.status_code": str(result.code),
"http.url": str(result.url), "http.method": "GET",
"http.flavor": "1.1", "http.url": str(result.url),
}, "http.flavor": "1.1",
},
),
) )
self.assertEqual( self.assertEqual(
@ -160,13 +106,15 @@ class TestRequestsIntegration(TestBase):
) )
self.assert_metric_expected( self.assert_metric_expected(
client_response_size, client_response_size,
result.length, self.create_histogram_data_points(
{ result.length,
"http.status_code": str(result.code), attributes={
"http.method": "GET", "http.status_code": str(result.code),
"http.url": str(result.url), "http.method": "GET",
"http.flavor": "1.1", "http.url": str(result.url),
}, "http.flavor": "1.1",
},
),
) )
def test_basic_metric_request_not_empty(self): def test_basic_metric_request_not_empty(self):
@ -191,14 +139,16 @@ class TestRequestsIntegration(TestBase):
) )
self.assert_metric_expected( self.assert_metric_expected(
client_duration, client_duration,
client_duration_estimated, self.create_histogram_data_points(
{ client_duration_estimated,
"http.status_code": str(result.code), attributes={
"http.method": "POST", "http.status_code": str(result.code),
"http.url": str(result.url), "http.method": "POST",
"http.flavor": "1.1", "http.url": str(result.url),
}, "http.flavor": "1.1",
est_delta=200, },
),
est_value_delta=200,
) )
self.assertEqual( self.assertEqual(
@ -207,13 +157,15 @@ class TestRequestsIntegration(TestBase):
) )
self.assert_metric_expected( self.assert_metric_expected(
client_request_size, client_request_size,
len(data_encoded), self.create_histogram_data_points(
{ len(data_encoded),
"http.status_code": str(result.code), attributes={
"http.method": "POST", "http.status_code": str(result.code),
"http.url": str(result.url), "http.method": "POST",
"http.flavor": "1.1", "http.url": str(result.url),
}, "http.flavor": "1.1",
},
),
) )
self.assertEqual( self.assertEqual(
@ -222,13 +174,15 @@ class TestRequestsIntegration(TestBase):
) )
self.assert_metric_expected( self.assert_metric_expected(
client_response_size, client_response_size,
result.length, self.create_histogram_data_points(
{ result.length,
"http.status_code": str(result.code), attributes={
"http.method": "POST", "http.status_code": str(result.code),
"http.url": str(result.url), "http.method": "POST",
"http.flavor": "1.1", "http.url": str(result.url),
}, "http.flavor": "1.1",
},
),
) )
def test_metric_uninstrument(self): def test_metric_uninstrument(self):

View File

@ -12,11 +12,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from timeit import default_timer
import urllib3 import urllib3
import urllib3.exceptions import urllib3.exceptions
from urllib3.request import encode_multipart_formdata
from opentelemetry import trace from opentelemetry import trace
from opentelemetry.instrumentation.urllib3 import URLLib3Instrumentor from opentelemetry.instrumentation.urllib3 import URLLib3Instrumentor
@ -87,136 +84,3 @@ class TestURLLib3InstrumentorWithRealSocket(HttpTestBase, TestBase):
"net.peer.ip": self.assert_ip, "net.peer.ip": self.assert_ip,
} }
self.assertGreaterEqual(span.attributes.items(), attributes.items()) self.assertGreaterEqual(span.attributes.items(), attributes.items())
class TestURLLib3InstrumentorMetric(HttpTestBase, TestBase):
def setUp(self):
super().setUp()
self.assert_ip = self.server.server_address[0]
self.assert_port = self.server.server_address[1]
self.http_host = ":".join(map(str, self.server.server_address[:2]))
self.http_url_base = "http://" + self.http_host
self.http_url = self.http_url_base + "/status/200"
URLLib3Instrumentor().instrument(meter_provider=self.meter_provider)
def tearDown(self):
super().tearDown()
URLLib3Instrumentor().uninstrument()
def test_metric_uninstrument(self):
with urllib3.PoolManager() as pool:
pool.request("GET", self.http_url)
URLLib3Instrumentor().uninstrument()
pool.request("GET", self.http_url)
metrics_list = self.memory_metrics_reader.get_metrics_data()
for resource_metric in metrics_list.resource_metrics:
for scope_metric in resource_metric.scope_metrics:
for metric in scope_metric.metrics:
for point in list(metric.data.data_points):
self.assertEqual(point.count, 1)
def test_basic_metric_check_client_size_get(self):
with urllib3.PoolManager() as pool:
start_time = default_timer()
response = pool.request("GET", self.http_url)
client_duration_estimated = (default_timer() - start_time) * 1000
expected_attributes = {
"http.status_code": 200,
"http.host": self.assert_ip,
"http.method": "GET",
"http.flavor": "1.1",
"http.scheme": "http",
"net.peer.name": self.assert_ip,
"net.peer.port": self.assert_port,
}
expected_data = {
"http.client.request.size": 0,
"http.client.response.size": len(response.data),
}
expected_metrics = [
"http.client.duration",
"http.client.request.size",
"http.client.response.size",
]
resource_metrics = (
self.memory_metrics_reader.get_metrics_data().resource_metrics
)
for metrics in resource_metrics:
for scope_metrics in metrics.scope_metrics:
self.assertEqual(len(scope_metrics.metrics), 3)
for metric in scope_metrics.metrics:
for data_point in metric.data.data_points:
if metric.name in expected_data:
self.assertEqual(
data_point.sum, expected_data[metric.name]
)
if metric.name == "http.client.duration":
self.assertAlmostEqual(
data_point.sum,
client_duration_estimated,
delta=1000,
)
self.assertIn(metric.name, expected_metrics)
self.assertDictEqual(
expected_attributes,
dict(data_point.attributes),
)
self.assertEqual(data_point.count, 1)
def test_basic_metric_check_client_size_post(self):
with urllib3.PoolManager() as pool:
start_time = default_timer()
data_fields = {"data": "test"}
response = pool.request("POST", self.http_url, fields=data_fields)
client_duration_estimated = (default_timer() - start_time) * 1000
expected_attributes = {
"http.status_code": 501,
"http.host": self.assert_ip,
"http.method": "POST",
"http.flavor": "1.1",
"http.scheme": "http",
"net.peer.name": self.assert_ip,
"net.peer.port": self.assert_port,
}
body = encode_multipart_formdata(data_fields)[0]
expected_data = {
"http.client.request.size": len(body),
"http.client.response.size": len(response.data),
}
expected_metrics = [
"http.client.duration",
"http.client.request.size",
"http.client.response.size",
]
resource_metrics = (
self.memory_metrics_reader.get_metrics_data().resource_metrics
)
for metrics in resource_metrics:
for scope_metrics in metrics.scope_metrics:
self.assertEqual(len(scope_metrics.metrics), 3)
for metric in scope_metrics.metrics:
for data_point in metric.data.data_points:
if metric.name in expected_data:
self.assertEqual(
data_point.sum, expected_data[metric.name]
)
if metric.name == "http.client.duration":
self.assertAlmostEqual(
data_point.sum,
client_duration_estimated,
delta=1000,
)
self.assertIn(metric.name, expected_metrics)
self.assertDictEqual(
expected_attributes,
dict(data_point.attributes),
)
self.assertEqual(data_point.count, 1)

View File

@ -0,0 +1,203 @@
# Copyright The OpenTelemetry Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from timeit import default_timer
import urllib3
import urllib3.exceptions
from urllib3.request import encode_multipart_formdata
from opentelemetry.instrumentation.urllib3 import URLLib3Instrumentor
from opentelemetry.test.httptest import HttpTestBase
from opentelemetry.test.test_base import TestBase
class TestUrllib3MetricsInstrumentation(HttpTestBase, TestBase):
def setUp(self):
super().setUp()
self.assert_ip = self.server.server_address[0]
self.assert_port = self.server.server_address[1]
self.http_host = ":".join(map(str, self.server.server_address[:2]))
self.http_url_base = "http://" + self.http_host
self.http_url = self.http_url_base + "/status/200"
URLLib3Instrumentor().instrument(meter_provider=self.meter_provider)
def tearDown(self):
super().tearDown()
URLLib3Instrumentor().uninstrument()
# Return Sequence with one histogram
def create_histogram_data_points(self, sum_data_point, attributes):
return [
self.create_histogram_data_point(
sum_data_point, 1, sum_data_point, sum_data_point, attributes
)
]
def test_basic_metric_check_client_size_get(self):
with urllib3.PoolManager() as pool:
start_time = default_timer()
response = pool.request("GET", self.http_url)
client_duration_estimated = (default_timer() - start_time) * 1000
metrics = self.get_sorted_metrics()
(
client_duration,
client_request_size,
client_response_size,
) = metrics
self.assertEqual(client_duration.name, "http.client.duration")
self.assert_metric_expected(
client_duration,
self.create_histogram_data_points(
client_duration_estimated,
attributes={
"http.status_code": 200,
"http.host": self.assert_ip,
"http.method": "GET",
"http.flavor": "1.1",
"http.scheme": "http",
"net.peer.name": self.assert_ip,
"net.peer.port": self.assert_port,
},
),
est_value_delta=200,
)
self.assertEqual(
client_request_size.name, "http.client.request.size"
)
self.assert_metric_expected(
client_request_size,
self.create_histogram_data_points(
0,
attributes={
"http.status_code": 200,
"http.host": self.assert_ip,
"http.method": "GET",
"http.flavor": "1.1",
"http.scheme": "http",
"net.peer.name": self.assert_ip,
"net.peer.port": self.assert_port,
},
),
)
self.assertEqual(
client_response_size.name, "http.client.response.size"
)
self.assert_metric_expected(
client_response_size,
self.create_histogram_data_points(
len(response.data),
attributes={
"http.status_code": 200,
"http.host": self.assert_ip,
"http.method": "GET",
"http.flavor": "1.1",
"http.scheme": "http",
"net.peer.name": self.assert_ip,
"net.peer.port": self.assert_port,
},
),
)
def test_basic_metric_check_client_size_post(self):
with urllib3.PoolManager() as pool:
start_time = default_timer()
data_fields = {"data": "test"}
response = pool.request("POST", self.http_url, fields=data_fields)
client_duration_estimated = (default_timer() - start_time) * 1000
body = encode_multipart_formdata(data_fields)[0]
metrics = self.get_sorted_metrics()
(
client_duration,
client_request_size,
client_response_size,
) = metrics
self.assertEqual(client_duration.name, "http.client.duration")
self.assert_metric_expected(
client_duration,
self.create_histogram_data_points(
client_duration_estimated,
attributes={
"http.status_code": 501,
"http.host": self.assert_ip,
"http.method": "POST",
"http.flavor": "1.1",
"http.scheme": "http",
"net.peer.name": self.assert_ip,
"net.peer.port": self.assert_port,
},
),
est_value_delta=200,
)
self.assertEqual(
client_request_size.name, "http.client.request.size"
)
client_request_size_expected = len(body)
self.assert_metric_expected(
client_request_size,
self.create_histogram_data_points(
client_request_size_expected,
attributes={
"http.status_code": 501,
"http.host": self.assert_ip,
"http.method": "POST",
"http.flavor": "1.1",
"http.scheme": "http",
"net.peer.name": self.assert_ip,
"net.peer.port": self.assert_port,
},
),
)
self.assertEqual(
client_response_size.name, "http.client.response.size"
)
client_response_size_expected = len(response.data)
self.assert_metric_expected(
client_response_size,
self.create_histogram_data_points(
client_response_size_expected,
attributes={
"http.status_code": 501,
"http.host": self.assert_ip,
"http.method": "POST",
"http.flavor": "1.1",
"http.scheme": "http",
"net.peer.name": self.assert_ip,
"net.peer.port": self.assert_port,
},
),
)
def test_metric_uninstrument(self):
with urllib3.PoolManager() as pool:
pool.request("GET", self.http_url)
URLLib3Instrumentor().uninstrument()
pool.request("GET", self.http_url)
metrics = self.get_sorted_metrics()
self.assertEqual(len(metrics), 3)
for metric in metrics:
for point in list(metric.data.data_points):
self.assertEqual(point.count, 1)