diff --git a/README.md b/README.md
index 613b4e6acbd..63cba3eb3b9 100644
--- a/README.md
+++ b/README.md
@@ -107,7 +107,6 @@ Approvers ([@open-telemetry/python-approvers](https://github.com/orgs/open-telem
- [Chris Kleinknecht](https://github.com/c24t), Google
- [Diego Hurtado](https://github.com/ocelotl)
- [Hector Hernandez](https://github.com/hectorhdzg), Microsoft
-- [Leighton Chen](https://github.com/lzchen), Microsoft
- [Mauricio Vásquez](https://github.com/mauriciovasquezbernal), Kinvolk
- [Reiley Yang](https://github.com/reyang), Microsoft
@@ -116,16 +115,17 @@ Approvers ([@open-telemetry/python-approvers](https://github.com/orgs/open-telem
Maintainers ([@open-telemetry/python-maintainers](https://github.com/orgs/open-telemetry/teams/python-maintainers)):
- [Alex Boten](https://github.com/codeboten), LightStep
+- [Leighton Chen](https://github.com/lzchen), Microsoft
- [Yusuke Tsutsumi](https://github.com/toumorokoshi), Zillow Group
+*Find more about the maintainer role in [community repository](https://github.com/open-telemetry/community/blob/master/community-membership.md#maintainer).*
+
### Thanks to all the people who already contributed!
-*Find more about the maintainer role in [community repository](https://github.com/open-telemetry/community/blob/master/community-membership.md#maintainer).*
-
## Release Schedule
OpenTelemetry Python is under active development.
diff --git a/dev-requirements.txt b/dev-requirements.txt
index be74d804b3d..253c0895072 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -2,7 +2,7 @@ pylint==2.4.4
flake8==3.7.9
isort~=4.3
black>=19.3b0,==19.*
-mypy==0.740
+mypy==0.770
sphinx~=2.1
sphinx-rtd-theme~=0.4
sphinx-autodoc-typehints~=1.10.2
diff --git a/docs/examples/basic_meter/observer.py b/docs/examples/basic_meter/observer.py
index aa70abe2a44..b61b9e4db80 100644
--- a/docs/examples/basic_meter/observer.py
+++ b/docs/examples/basic_meter/observer.py
@@ -19,7 +19,7 @@
import psutil
from opentelemetry import metrics
-from opentelemetry.sdk.metrics import MeterProvider
+from opentelemetry.sdk.metrics import MeterProvider, ValueObserver
from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter
from opentelemetry.sdk.metrics.export.batcher import UngroupedBatcher
from opentelemetry.sdk.metrics.export.controller import PushController
@@ -43,6 +43,7 @@ def get_cpu_usage_callback(observer):
description="per-cpu usage",
unit="1",
value_type=float,
+ observer_type=ValueObserver,
label_keys=("cpu_number",),
)
diff --git a/ext/opentelemetry-ext-opencensusexporter/src/opentelemetry/ext/opencensusexporter/metrics_exporter/__init__.py b/ext/opentelemetry-ext-opencensusexporter/src/opentelemetry/ext/opencensusexporter/metrics_exporter/__init__.py
index faa0788c7f1..bb1a1ee888c 100644
--- a/ext/opentelemetry-ext-opencensusexporter/src/opentelemetry/ext/opencensusexporter/metrics_exporter/__init__.py
+++ b/ext/opentelemetry-ext-opencensusexporter/src/opentelemetry/ext/opencensusexporter/metrics_exporter/__init__.py
@@ -114,10 +114,10 @@ def translate_to_collector(
)
metric_descriptor = metrics_pb2.MetricDescriptor(
- name=metric_record.metric.name,
- description=metric_record.metric.description,
- unit=metric_record.metric.unit,
- type=get_collector_metric_type(metric_record.metric),
+ name=metric_record.instrument.name,
+ description=metric_record.instrument.description,
+ unit=metric_record.instrument.unit,
+ type=get_collector_metric_type(metric_record.instrument),
label_keys=label_keys,
)
@@ -151,14 +151,14 @@ def get_collector_point(metric_record: MetricRecord) -> metrics_pb2.Point:
metric_record.aggregator.last_update_timestamp
)
)
- if metric_record.metric.value_type == int:
+ if metric_record.instrument.value_type == int:
point.int64_value = metric_record.aggregator.checkpoint
- elif metric_record.metric.value_type == float:
+ elif metric_record.instrument.value_type == float:
point.double_value = metric_record.aggregator.checkpoint
else:
raise TypeError(
"Unsupported metric type: {}".format(
- metric_record.metric.value_type
+ metric_record.instrument.value_type
)
)
return point
diff --git a/ext/opentelemetry-ext-opencensusexporter/tests/test_otcollector_metrics_exporter.py b/ext/opentelemetry-ext-opencensusexporter/tests/test_otcollector_metrics_exporter.py
index 18b4a328067..f9070126476 100644
--- a/ext/opentelemetry-ext-opencensusexporter/tests/test_otcollector_metrics_exporter.py
+++ b/ext/opentelemetry-ext-opencensusexporter/tests/test_otcollector_metrics_exporter.py
@@ -92,7 +92,7 @@ def test_get_collector_point(self):
"testName", "testDescription", "unit", float, ValueRecorder
)
result = metrics_exporter.get_collector_point(
- MetricRecord(aggregator, self._key_labels, int_counter)
+ MetricRecord(int_counter, self._key_labels, aggregator)
)
self.assertIsInstance(result, metrics_pb2.Point)
self.assertIsInstance(result.timestamp, Timestamp)
@@ -100,13 +100,13 @@ def test_get_collector_point(self):
aggregator.update(123.5)
aggregator.take_checkpoint()
result = metrics_exporter.get_collector_point(
- MetricRecord(aggregator, self._key_labels, float_counter)
+ MetricRecord(float_counter, self._key_labels, aggregator)
)
self.assertEqual(result.double_value, 123.5)
self.assertRaises(
TypeError,
metrics_exporter.get_collector_point(
- MetricRecord(aggregator, self._key_labels, valuerecorder)
+ MetricRecord(valuerecorder, self._key_labels, aggregator)
),
)
@@ -122,7 +122,7 @@ def test_export(self):
"testname", "testdesc", "unit", int, Counter, ["environment"]
)
record = MetricRecord(
- aggregate.CounterAggregator(), self._key_labels, test_metric
+ test_metric, self._key_labels, aggregate.CounterAggregator(),
)
result = collector_exporter.export([record])
@@ -147,7 +147,7 @@ def test_translate_to_collector(self):
aggregator = aggregate.CounterAggregator()
aggregator.update(123)
aggregator.take_checkpoint()
- record = MetricRecord(aggregator, self._key_labels, test_metric)
+ record = MetricRecord(test_metric, self._key_labels, aggregator,)
output_metrics = metrics_exporter.translate_to_collector([record])
self.assertEqual(len(output_metrics), 1)
self.assertIsInstance(output_metrics[0], metrics_pb2.Metric)
diff --git a/ext/opentelemetry-ext-prometheus/src/opentelemetry/ext/prometheus/__init__.py b/ext/opentelemetry-ext-prometheus/src/opentelemetry/ext/prometheus/__init__.py
index cc44621ac48..59ef3f1708a 100644
--- a/ext/opentelemetry-ext-prometheus/src/opentelemetry/ext/prometheus/__init__.py
+++ b/ext/opentelemetry-ext-prometheus/src/opentelemetry/ext/prometheus/__init__.py
@@ -152,22 +152,22 @@ def _translate_to_prometheus(self, metric_record: MetricRecord):
metric_name = ""
if self._prefix != "":
metric_name = self._prefix + "_"
- metric_name += self._sanitize(metric_record.metric.name)
+ metric_name += self._sanitize(metric_record.instrument.name)
- if isinstance(metric_record.metric, Counter):
+ if isinstance(metric_record.instrument, Counter):
prometheus_metric = CounterMetricFamily(
name=metric_name,
- documentation=metric_record.metric.description,
+ documentation=metric_record.instrument.description,
labels=label_keys,
)
prometheus_metric.add_metric(
labels=label_values, value=metric_record.aggregator.checkpoint
)
# TODO: Add support for histograms when supported in OT
- elif isinstance(metric_record.metric, ValueRecorder):
+ elif isinstance(metric_record.instrument, ValueRecorder):
prometheus_metric = UnknownMetricFamily(
name=metric_name,
- documentation=metric_record.metric.description,
+ documentation=metric_record.instrument.description,
labels=label_keys,
)
prometheus_metric.add_metric(
@@ -176,7 +176,7 @@ def _translate_to_prometheus(self, metric_record: MetricRecord):
else:
logger.warning(
- "Unsupported metric type. %s", type(metric_record.metric)
+ "Unsupported metric type. %s", type(metric_record.instrument)
)
return prometheus_metric
diff --git a/ext/opentelemetry-ext-prometheus/tests/test_prometheus_exporter.py b/ext/opentelemetry-ext-prometheus/tests/test_prometheus_exporter.py
index f986e0c4f5f..1862f789c0c 100644
--- a/ext/opentelemetry-ext-prometheus/tests/test_prometheus_exporter.py
+++ b/ext/opentelemetry-ext-prometheus/tests/test_prometheus_exporter.py
@@ -67,7 +67,7 @@ def test_shutdown(self):
def test_export(self):
with self._registry_register_patch:
record = MetricRecord(
- CounterAggregator(), self._labels_key, self._test_metric
+ self._test_metric, self._labels_key, CounterAggregator(),
)
exporter = PrometheusMetricsExporter()
result = exporter.export([record])
@@ -90,7 +90,7 @@ def test_counter_to_prometheus(self):
aggregator = CounterAggregator()
aggregator.update(123)
aggregator.take_checkpoint()
- record = MetricRecord(aggregator, key_labels, metric)
+ record = MetricRecord(metric, key_labels, aggregator)
collector = CustomCollector("testprefix")
collector.add_metrics_data([record])
@@ -118,7 +118,7 @@ def test_invalid_metric(self):
)
labels = {"environment": "staging"}
key_labels = metrics.get_labels_as_key(labels)
- record = MetricRecord(None, key_labels, metric)
+ record = MetricRecord(metric, key_labels, None)
collector = CustomCollector("testprefix")
collector.add_metrics_data([record])
collector.collect()
diff --git a/ext/opentelemetry-ext-requests/src/opentelemetry/ext/requests/__init__.py b/ext/opentelemetry-ext-requests/src/opentelemetry/ext/requests/__init__.py
index 1621c4a95e6..65bc21370f6 100644
--- a/ext/opentelemetry-ext-requests/src/opentelemetry/ext/requests/__init__.py
+++ b/ext/opentelemetry-ext-requests/src/opentelemetry/ext/requests/__init__.py
@@ -45,12 +45,14 @@
import types
from urllib.parse import urlparse
+from requests import Timeout, URLRequired
+from requests.exceptions import InvalidSchema, InvalidURL, MissingSchema
from requests.sessions import Session
from opentelemetry import context, propagators, trace
from opentelemetry.auto_instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.ext.requests.version import __version__
-from opentelemetry.trace import SpanKind, get_tracer
+from opentelemetry.trace import SpanKind
from opentelemetry.trace.status import Status, StatusCanonicalCode
@@ -80,31 +82,49 @@ def instrumented_request(self, method, url, *args, **kwargs):
# https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md#http-client
try:
parsed_url = urlparse(url)
+ span_name = parsed_url.path
except ValueError as exc: # Invalid URL
- path = "".format(exc)
- else:
- if parsed_url is None:
- path = ""
- path = parsed_url.path
+ span_name = "".format(exc)
- with tracer.start_as_current_span(path, kind=SpanKind.CLIENT) as span:
+ exception = None
+
+ with tracer.start_as_current_span(
+ span_name, kind=SpanKind.CLIENT
+ ) as span:
span.set_attribute("component", "http")
span.set_attribute("http.method", method.upper())
span.set_attribute("http.url", url)
headers = kwargs.setdefault("headers", {})
propagators.inject(type(headers).__setitem__, headers)
- result = wrapped(self, method, url, *args, **kwargs) # *** PROCEED
- span.set_attribute("http.status_code", result.status_code)
- span.set_attribute("http.status_text", result.reason)
- span.set_status(
- Status(_http_status_to_canonical_code(result.status_code))
- )
+ try:
+ result = wrapped(
+ self, method, url, *args, **kwargs
+ ) # *** PROCEED
+ except Exception as exc: # pylint: disable=W0703
+ exception = exc
+ result = getattr(exc, "response", None)
+
+ if exception is not None:
+ span.set_status(
+ Status(_exception_to_canonical_code(exception))
+ )
+
+ if result is not None:
+ span.set_attribute("http.status_code", result.status_code)
+ span.set_attribute("http.status_text", result.reason)
+ span.set_status(
+ Status(_http_status_to_canonical_code(result.status_code))
+ )
+
if span_callback is not None:
span_callback(span, result)
- return result
+ if exception is not None:
+ raise exception.with_traceback(exception.__traceback__)
+
+ return result
instrumented_request.opentelemetry_ext_requests_applied = True
@@ -157,6 +177,17 @@ def _http_status_to_canonical_code(code: int, allow_redirect: bool = True):
return StatusCanonicalCode.UNKNOWN
+def _exception_to_canonical_code(exc: Exception) -> StatusCanonicalCode:
+ if isinstance(
+ exc,
+ (InvalidURL, InvalidSchema, MissingSchema, URLRequired, ValueError),
+ ):
+ return StatusCanonicalCode.INVALID_ARGUMENT
+ if isinstance(exc, Timeout):
+ return StatusCanonicalCode.DEADLINE_EXCEEDED
+ return StatusCanonicalCode.UNKNOWN
+
+
class RequestsInstrumentor(BaseInstrumentor):
"""An instrumentor for requests
See `BaseInstrumentor`
diff --git a/ext/opentelemetry-ext-requests/tests/test_requests_integration.py b/ext/opentelemetry-ext-requests/tests/test_requests_integration.py
index 28359d8f38a..0ad5b9d19df 100644
--- a/ext/opentelemetry-ext-requests/tests/test_requests_integration.py
+++ b/ext/opentelemetry-ext-requests/tests/test_requests_integration.py
@@ -12,11 +12,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import sys
+from unittest import mock
import httpretty
import requests
-import urllib3
import opentelemetry.ext.requests
from opentelemetry import context, propagators, trace
@@ -24,6 +23,7 @@
from opentelemetry.sdk import resources
from opentelemetry.test.mock_httptextformat import MockHTTPTextFormat
from opentelemetry.test.test_base import TestBase
+from opentelemetry.trace.status import StatusCanonicalCode
class TestRequestsIntegration(TestBase):
@@ -92,13 +92,8 @@ def test_not_foundbasic(self):
def test_invalid_url(self):
url = "http://[::1/nope"
- exception_type = requests.exceptions.InvalidURL
- if sys.version_info[:2] < (3, 5) and tuple(
- map(int, urllib3.__version__.split(".")[:2])
- ) < (1, 25):
- exception_type = ValueError
- with self.assertRaises(exception_type):
+ with self.assertRaises(ValueError):
requests.post(url)
span_list = self.memory_exporter.get_finished_spans()
@@ -110,6 +105,9 @@ def test_invalid_url(self):
span.attributes,
{"component": "http", "http.method": "POST", "http.url": url},
)
+ self.assertEqual(
+ span.status.canonical_code, StatusCanonicalCode.INVALID_ARGUMENT
+ )
def test_uninstrument(self):
RequestsInstrumentor().uninstrument()
@@ -229,3 +227,75 @@ def test_custom_tracer_provider(self):
span = span_list[0]
self.assertIs(span.resource, resource)
+
+ @mock.patch("requests.Session.send", side_effect=requests.RequestException)
+ def test_requests_exception_without_response(self, *_, **__):
+
+ with self.assertRaises(requests.RequestException):
+ requests.get(self.URL)
+
+ span_list = self.memory_exporter.get_finished_spans()
+ self.assertEqual(len(span_list), 1)
+ span = span_list[0]
+ self.assertEqual(
+ span.attributes,
+ {"component": "http", "http.method": "GET", "http.url": self.URL},
+ )
+ self.assertEqual(
+ span.status.canonical_code, StatusCanonicalCode.UNKNOWN
+ )
+
+ mocked_response = requests.Response()
+ mocked_response.status_code = 500
+ mocked_response.reason = "Internal Server Error"
+
+ @mock.patch(
+ "requests.Session.send",
+ side_effect=requests.RequestException(response=mocked_response),
+ )
+ def test_requests_exception_with_response(self, *_, **__):
+
+ with self.assertRaises(requests.RequestException):
+ requests.get(self.URL)
+
+ span_list = self.memory_exporter.get_finished_spans()
+ self.assertEqual(len(span_list), 1)
+ span = span_list[0]
+ self.assertEqual(
+ span.attributes,
+ {
+ "component": "http",
+ "http.method": "GET",
+ "http.url": self.URL,
+ "http.status_code": 500,
+ "http.status_text": "Internal Server Error",
+ },
+ )
+ self.assertEqual(
+ span.status.canonical_code, StatusCanonicalCode.INTERNAL
+ )
+
+ @mock.patch("requests.Session.send", side_effect=Exception)
+ def test_requests_basic_exception(self, *_, **__):
+
+ with self.assertRaises(Exception):
+ requests.get(self.URL)
+
+ span_list = self.memory_exporter.get_finished_spans()
+ self.assertEqual(len(span_list), 1)
+ self.assertEqual(
+ span_list[0].status.canonical_code, StatusCanonicalCode.UNKNOWN
+ )
+
+ @mock.patch("requests.Session.send", side_effect=requests.Timeout)
+ def test_requests_timeout_exception(self, *_, **__):
+
+ with self.assertRaises(Exception):
+ requests.get(self.URL)
+
+ span_list = self.memory_exporter.get_finished_spans()
+ self.assertEqual(len(span_list), 1)
+ self.assertEqual(
+ span_list[0].status.canonical_code,
+ StatusCanonicalCode.DEADLINE_EXCEEDED,
+ )
diff --git a/ext/opentelemetry-ext-system-metrics/src/opentelemetry/ext/system_metrics/__init__.py b/ext/opentelemetry-ext-system-metrics/src/opentelemetry/ext/system_metrics/__init__.py
index 88f36f4ac48..09c633f14b4 100644
--- a/ext/opentelemetry-ext-system-metrics/src/opentelemetry/ext/system_metrics/__init__.py
+++ b/ext/opentelemetry-ext-system-metrics/src/opentelemetry/ext/system_metrics/__init__.py
@@ -58,6 +58,7 @@
import psutil
from opentelemetry import metrics
+from opentelemetry.sdk.metrics import ValueObserver
from opentelemetry.sdk.metrics.export import MetricsExporter
from opentelemetry.sdk.metrics.export.controller import PushController
@@ -106,6 +107,7 @@ def __init__(
description="System memory",
unit="bytes",
value_type=int,
+ observer_type=ValueObserver,
)
self.meter.register_observer(
@@ -114,6 +116,7 @@ def __init__(
description="System CPU",
unit="seconds",
value_type=float,
+ observer_type=ValueObserver,
)
self.meter.register_observer(
@@ -122,6 +125,7 @@ def __init__(
description="System network bytes",
unit="bytes",
value_type=int,
+ observer_type=ValueObserver,
)
self.meter.register_observer(
@@ -130,6 +134,7 @@ def __init__(
description="Runtime memory",
unit="bytes",
value_type=int,
+ observer_type=ValueObserver,
)
self.meter.register_observer(
@@ -138,6 +143,7 @@ def __init__(
description="Runtime CPU",
unit="seconds",
value_type=float,
+ observer_type=ValueObserver,
)
self.meter.register_observer(
@@ -146,9 +152,10 @@ def __init__(
description="Runtime: gc objects",
unit="objects",
value_type=int,
+ observer_type=ValueObserver,
)
- def _get_system_memory(self, observer: metrics.Observer) -> None:
+ def _get_system_memory(self, observer: metrics.ValueObserver) -> None:
"""Observer callback for memory available
Args:
@@ -161,7 +168,7 @@ def _get_system_memory(self, observer: metrics.Observer) -> None:
getattr(system_memory, metric), self._system_memory_labels
)
- def _get_system_cpu(self, observer: metrics.Observer) -> None:
+ def _get_system_cpu(self, observer: metrics.ValueObserver) -> None:
"""Observer callback for system cpu
Args:
@@ -174,7 +181,7 @@ def _get_system_cpu(self, observer: metrics.Observer) -> None:
getattr(cpu_times, _type), self._system_cpu_labels
)
- def _get_network_bytes(self, observer: metrics.Observer) -> None:
+ def _get_network_bytes(self, observer: metrics.ValueObserver) -> None:
"""Observer callback for network bytes
Args:
@@ -187,7 +194,7 @@ def _get_network_bytes(self, observer: metrics.Observer) -> None:
getattr(net_io, _type), self._network_bytes_labels
)
- def _get_runtime_memory(self, observer: metrics.Observer) -> None:
+ def _get_runtime_memory(self, observer: metrics.ValueObserver) -> None:
"""Observer callback for runtime memory
Args:
@@ -200,7 +207,7 @@ def _get_runtime_memory(self, observer: metrics.Observer) -> None:
getattr(proc_memory, _type), self._runtime_memory_labels
)
- def _get_runtime_cpu(self, observer: metrics.Observer) -> None:
+ def _get_runtime_cpu(self, observer: metrics.ValueObserver) -> None:
"""Observer callback for runtime CPU
Args:
@@ -213,7 +220,7 @@ def _get_runtime_cpu(self, observer: metrics.Observer) -> None:
getattr(proc_cpu, _type), self._runtime_cpu_labels
)
- def _get_runtime_gc_count(self, observer: metrics.Observer) -> None:
+ def _get_runtime_gc_count(self, observer: metrics.ValueObserver) -> None:
"""Observer callback for garbage collection
Args:
diff --git a/ext/opentelemetry-ext-system-metrics/tests/test_system_metrics.py b/ext/opentelemetry-ext-system-metrics/tests/test_system_metrics.py
index 70ead2c5152..b2d6bab4015 100644
--- a/ext/opentelemetry-ext-system-metrics/tests/test_system_metrics.py
+++ b/ext/opentelemetry-ext-system-metrics/tests/test_system_metrics.py
@@ -54,7 +54,7 @@ def _assert_metrics(self, observer_name, system_metrics, expected):
):
if (
metric.labels in expected
- and metric.metric.name == observer_name
+ and metric.instrument.name == observer_name
):
self.assertEqual(
metric.aggregator.checkpoint.last, expected[metric.labels],
diff --git a/opentelemetry-api/CHANGELOG.md b/opentelemetry-api/CHANGELOG.md
index c6759c1df27..b1610ab2c3d 100644
--- a/opentelemetry-api/CHANGELOG.md
+++ b/opentelemetry-api/CHANGELOG.md
@@ -8,6 +8,8 @@
([#761](https://github.com/open-telemetry/opentelemetry-python/pull/761))
- Adding trace.get_current_span, Removing Tracer.get_current_span
([#552](https://github.com/open-telemetry/opentelemetry-python/pull/552))
+- Rename Observer to ValueObserver
+ ([#764](https://github.com/open-telemetry/opentelemetry-python/pull/764))
## 0.8b0
diff --git a/opentelemetry-api/src/opentelemetry/__init__.pyi b/opentelemetry-api/src/opentelemetry/__init__.pyi
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/opentelemetry-api/src/opentelemetry/configuration/__init__.py b/opentelemetry-api/src/opentelemetry/configuration/__init__.py
index 48093415020..0bd2bce8d64 100644
--- a/opentelemetry-api/src/opentelemetry/configuration/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/configuration/__init__.py
@@ -12,9 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# FIXME find a better way to avoid all those "Expression has type "Any"" errors
-# type: ignore
-
"""
Simple configuration manager
@@ -95,17 +92,23 @@
from os import environ
from re import fullmatch
+from typing import ClassVar, Dict, Optional, TypeVar, Union
+ConfigValue = Union[str, bool, int, float]
+_T = TypeVar("_T", ConfigValue, Optional[ConfigValue])
-class Configuration:
- _instance = None
- __slots__ = []
+class Configuration:
+ _instance = None # type: ClassVar[Optional[Configuration]]
+ _config_map = {} # type: ClassVar[Dict[str, ConfigValue]]
def __new__(cls) -> "Configuration":
- if Configuration._instance is None:
+ if cls._instance is not None:
+ instance = cls._instance
+ else:
- for key, value in environ.items():
+ instance = super().__new__(cls)
+ for key, value_str in environ.items():
match = fullmatch(
r"OPENTELEMETRY_PYTHON_([A-Za-z_][\w_]*)", key
@@ -114,45 +117,47 @@ def __new__(cls) -> "Configuration":
if match is not None:
key = match.group(1)
+ value = value_str # type: ConfigValue
- if value == "True":
+ if value_str == "True":
value = True
- elif value == "False":
+ elif value_str == "False":
value = False
else:
try:
- value = int(value)
+ value = int(value_str)
except ValueError:
pass
try:
- value = float(value)
+ value = float(value_str)
except ValueError:
pass
- setattr(Configuration, "_{}".format(key), value)
- setattr(
- Configuration,
- key,
- property(
- fget=lambda cls, key=key: getattr(
- cls, "_{}".format(key)
- )
- ),
- )
+ instance._config_map[key] = value
- Configuration.__slots__.append(key)
+ cls._instance = instance
- Configuration.__slots__ = tuple(Configuration.__slots__)
+ return instance
- Configuration._instance = object.__new__(cls)
+ def __getattr__(self, name: str) -> Optional[ConfigValue]:
+ return self._config_map.get(name)
- return cls._instance
+ def __setattr__(self, key: str, val: ConfigValue) -> None:
+ if key == "_config_map":
+ super().__setattr__(key, val)
+ else:
+ raise AttributeError(key)
- def __getattr__(self, name):
- return None
+ def get(self, name: str, default: _T) -> _T:
+ """Use this typed method for dynamic access instead of `getattr`
+
+ :rtype: str or bool or int or float or None
+ """
+ val = self._config_map.get(name, default)
+ return val
@classmethod
- def _reset(cls):
+ def _reset(cls) -> None:
"""
This method "resets" the global configuration attributes
@@ -160,10 +165,6 @@ def _reset(cls):
only.
"""
- for slot in cls.__slots__:
- if slot in cls.__dict__.keys():
- delattr(cls, slot)
- delattr(cls, "_{}".format(slot))
-
- cls.__slots__ = []
- cls._instance = None
+ if cls._instance:
+ cls._instance._config_map.clear() # pylint: disable=protected-access
+ cls._instance = None
diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py
index 16ac4c096f0..da47356e058 100644
--- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py
@@ -31,7 +31,7 @@
from logging import getLogger
from typing import Callable, Dict, Optional, Sequence, Tuple, Type, TypeVar
-from opentelemetry.util import _load_provider
+from opentelemetry.util import _load_meter_provider
logger = getLogger(__name__)
ValueT = TypeVar("ValueT", int, float)
@@ -162,7 +162,6 @@ class Observer(abc.ABC):
"""An observer type metric instrument used to capture a current set of
values.
-
Observer instruments are asynchronous, a callback is invoked with the
observer instrument as argument allowing the user to capture multiple
values per collection interval.
@@ -190,6 +189,18 @@ def observe(self, value: ValueT, labels: Dict[str, str]) -> None:
"""
+class ValueObserver(Observer):
+ """No-op implementation of ``ValueObserver``."""
+
+ def observe(self, value: ValueT, labels: Dict[str, str]) -> None:
+ """Captures ``value`` to the valueobserver.
+
+ Args:
+ value: The value to capture to this valueobserver metric.
+ labels: Labels associated to ``value``.
+ """
+
+
class MeterProvider(abc.ABC):
@abc.abstractmethod
def get_meter(
@@ -232,7 +243,9 @@ def get_meter(
return DefaultMeter()
-MetricT = TypeVar("MetricT", Counter, ValueRecorder, Observer)
+MetricT = TypeVar("MetricT", Counter, ValueRecorder)
+InstrumentT = TypeVar("InstrumentT", Counter, Observer, ValueRecorder)
+ObserverT = TypeVar("ObserverT", bound=Observer)
ObserverCallbackT = Callable[[Observer], None]
@@ -297,6 +310,7 @@ def register_observer(
description: str,
unit: str,
value_type: Type[ValueT],
+ observer_type: Type[ObserverT],
label_keys: Sequence[str] = (),
enabled: bool = True,
) -> "Observer":
@@ -310,6 +324,7 @@ def register_observer(
unit: Unit of the metric values following the UCUM convention
(https://unitsofmeasure.org/ucum.html).
value_type: The type of values being recorded by the metric.
+ observer_type: The type of observer being registered.
label_keys: The keys for the labels with dynamic values.
enabled: Whether to report the metric by default.
Returns: A new ``Observer`` metric instrument.
@@ -354,6 +369,7 @@ def register_observer(
description: str,
unit: str,
value_type: Type[ValueT],
+ observer_type: Type[ObserverT],
label_keys: Sequence[str] = (),
enabled: bool = True,
) -> "Observer":
@@ -395,6 +411,6 @@ def get_meter_provider() -> MeterProvider:
global _METER_PROVIDER # pylint: disable=global-statement
if _METER_PROVIDER is None:
- _METER_PROVIDER = _load_provider("meter_provider")
+ _METER_PROVIDER = _load_meter_provider("meter_provider")
return _METER_PROVIDER
diff --git a/opentelemetry-api/src/opentelemetry/trace/__init__.py b/opentelemetry-api/src/opentelemetry/trace/__init__.py
index 52dc9bbae0a..88131738417 100644
--- a/opentelemetry-api/src/opentelemetry/trace/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/trace/__init__.py
@@ -124,7 +124,7 @@
format_trace_id,
)
from opentelemetry.trace.status import Status
-from opentelemetry.util import _load_provider, types
+from opentelemetry.util import _load_trace_provider, types
logger = getLogger(__name__)
@@ -467,6 +467,6 @@ def get_tracer_provider() -> TracerProvider:
global _TRACER_PROVIDER # pylint: disable=global-statement
if _TRACER_PROVIDER is None:
- _TRACER_PROVIDER = _load_provider("tracer_provider")
+ _TRACER_PROVIDER = _load_trace_provider("tracer_provider")
return _TRACER_PROVIDER
diff --git a/opentelemetry-api/src/opentelemetry/util/__init__.py b/opentelemetry-api/src/opentelemetry/util/__init__.py
index bab42b0bde2..ed1268fcb62 100644
--- a/opentelemetry-api/src/opentelemetry/util/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/util/__init__.py
@@ -14,11 +14,17 @@
import re
import time
from logging import getLogger
-from typing import Sequence, Union
+from typing import TYPE_CHECKING, Sequence, Union, cast
from pkg_resources import iter_entry_points
-from opentelemetry.configuration import Configuration # type: ignore
+from opentelemetry.configuration import Configuration
+
+if TYPE_CHECKING:
+ from opentelemetry.trace import TracerProvider
+ from opentelemetry.metrics import MeterProvider
+
+Provider = Union["TracerProvider", "MeterProvider"]
logger = getLogger(__name__)
@@ -34,25 +40,33 @@ def time_ns() -> int:
return int(time.time() * 1e9)
-def _load_provider(
- provider: str,
-) -> Union["TracerProvider", "MeterProvider"]: # type: ignore
+def _load_provider(provider: str) -> Provider:
try:
- return next( # type: ignore
+ entry_point = next(
iter_entry_points(
"opentelemetry_{}".format(provider),
- name=getattr(
- Configuration(), # type: ignore
- provider,
- "default_{}".format(provider),
+ name=cast(
+ str,
+ Configuration().get(
+ provider, "default_{}".format(provider),
+ ),
),
)
- ).load()()
+ )
+ return cast(Provider, entry_point.load()(),)
except Exception: # pylint: disable=broad-except
logger.error("Failed to load configured provider %s", provider)
raise
+def _load_meter_provider(provider: str) -> "MeterProvider":
+ return cast("MeterProvider", _load_provider(provider))
+
+
+def _load_trace_provider(provider: str) -> "TracerProvider":
+ return cast("TracerProvider", _load_provider(provider))
+
+
# Pattern for matching up until the first '/' after the 'https://' part.
_URL_PATTERN = r"(https?|ftp)://.*?/"
diff --git a/opentelemetry-api/tests/configuration/test_configuration.py b/opentelemetry-api/tests/configuration/test_configuration.py
index a817a1bf43d..32b62e619d5 100644
--- a/opentelemetry-api/tests/configuration/test_configuration.py
+++ b/opentelemetry-api/tests/configuration/test_configuration.py
@@ -16,16 +16,16 @@
from unittest import TestCase
from unittest.mock import patch
-from opentelemetry.configuration import Configuration # type: ignore
+from opentelemetry.configuration import Configuration
class TestConfiguration(TestCase):
- def tearDown(self):
+ def tearDown(self) -> None:
# This call resets the attributes of the Configuration class so that
# each test is executed in the same conditions.
Configuration._reset()
- def test_singleton(self):
+ def test_singleton(self) -> None:
self.assertIsInstance(Configuration(), Configuration)
self.assertIs(Configuration(), Configuration())
@@ -39,7 +39,7 @@ def test_singleton(self):
"OPENTELEMETRY_PTHON_TRACEX_PROVIDER": "tracex_provider",
},
)
- def test_environment_variables(self): # type: ignore
+ def test_environment_variables(self):
self.assertEqual(
Configuration().METER_PROVIDER, "meter_provider"
) # pylint: disable=no-member
@@ -62,16 +62,21 @@ def test_property(self):
with self.assertRaises(AttributeError):
Configuration().TRACER_PROVIDER = "new_tracer_provider"
- def test_slots(self):
+ def test_slots(self) -> None:
with self.assertRaises(AttributeError):
Configuration().XYZ = "xyz" # pylint: disable=assigning-non-slot
- def test_getattr(self):
+ def test_getattr(self) -> None:
+ # literal access
self.assertIsNone(Configuration().XYZ)
- def test_reset(self):
+ # dynamic access
+ self.assertIsNone(getattr(Configuration(), "XYZ"))
+ self.assertIsNone(Configuration().get("XYZ", None))
+
+ def test_reset(self) -> None:
environ_patcher = patch.dict(
- "os.environ", # type: ignore
+ "os.environ",
{"OPENTELEMETRY_PYTHON_TRACER_PROVIDER": "tracer_provider"},
)
@@ -96,7 +101,7 @@ def test_reset(self):
"OPENTELEMETRY_PYTHON_FALSE": "False",
},
)
- def test_boolean(self):
+ def test_boolean(self) -> None:
self.assertIsInstance(
Configuration().TRUE, bool
) # pylint: disable=no-member
@@ -114,7 +119,7 @@ def test_boolean(self):
"OPENTELEMETRY_PYTHON_NON_INTEGER": "-12z3",
},
)
- def test_integer(self):
+ def test_integer(self) -> None:
self.assertEqual(
Configuration().POSITIVE_INTEGER, 123
) # pylint: disable=no-member
@@ -133,7 +138,7 @@ def test_integer(self):
"OPENTELEMETRY_PYTHON_NON_FLOAT": "-12z3.123",
},
)
- def test_float(self):
+ def test_float(self) -> None:
self.assertEqual(
Configuration().POSITIVE_FLOAT, 123.123
) # pylint: disable=no-member
diff --git a/opentelemetry-api/tests/test_implementation.py b/opentelemetry-api/tests/test_implementation.py
index 735ac4a683f..d0f9404a911 100644
--- a/opentelemetry-api/tests/test_implementation.py
+++ b/opentelemetry-api/tests/test_implementation.py
@@ -83,7 +83,9 @@ def test_create_metric(self):
def test_register_observer(self):
meter = metrics.DefaultMeter()
callback = mock.Mock()
- observer = meter.register_observer(callback, "", "", "", int, (), True)
+ observer = meter.register_observer(
+ callback, "", "", "", int, metrics.ValueObserver
+ )
self.assertIsInstance(observer, metrics.DefaultObserver)
def test_unregister_observer(self):
diff --git a/opentelemetry-sdk/CHANGELOG.md b/opentelemetry-sdk/CHANGELOG.md
index 940f1f17b8b..713580dbd6b 100644
--- a/opentelemetry-sdk/CHANGELOG.md
+++ b/opentelemetry-sdk/CHANGELOG.md
@@ -8,6 +8,10 @@
([#761](https://github.com/open-telemetry/opentelemetry-python/pull/761))
- Adding trace.get_current_span, Removing Tracer.get_current_span
([#552](https://github.com/open-telemetry/opentelemetry-python/pull/552))
+- bugfix: byte type attributes are decoded before adding to attributes dict
+ ([#775](https://github.com/open-telemetry/opentelemetry-python/pull/775))
+- Rename Observer to ValueObserver
+ ([#764](https://github.com/open-telemetry/opentelemetry-python/pull/764))
## 0.8b0
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py
index f231182cc28..507e00d8ead 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py
@@ -190,8 +190,8 @@ def record(
UPDATE_FUNCTION = record
-class Observer(metrics_api.Observer):
- """See `opentelemetry.metrics.Observer`."""
+class ValueObserver(metrics_api.ValueObserver):
+ """See `opentelemetry.metrics.ValueObserver`."""
def __init__(
self,
@@ -257,11 +257,11 @@ class Record:
def __init__(
self,
- metric: metrics_api.MetricT,
+ instrument: metrics_api.InstrumentT,
labels: Dict[str, str],
aggregator: Aggregator,
):
- self.metric = metric
+ self.instrument = instrument
self.labels = labels
self.aggregator = aggregator
@@ -374,10 +374,11 @@ def register_observer(
description: str,
unit: str,
value_type: Type[metrics_api.ValueT],
+ observer_type=Type[metrics_api.ObserverT],
label_keys: Sequence[str] = (),
enabled: bool = True,
) -> metrics_api.Observer:
- ob = Observer(
+ ob = observer_type(
callback,
name,
description,
@@ -391,7 +392,7 @@ def register_observer(
self.observers.add(ob)
return ob
- def unregister_observer(self, observer: "Observer") -> None:
+ def unregister_observer(self, observer: metrics_api.Observer) -> None:
with self.observers_lock:
self.observers.remove(observer)
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py
index f5a8693268e..16911f94efb 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/__init__.py
@@ -27,13 +27,13 @@ class MetricsExportResult(Enum):
class MetricRecord:
def __init__(
self,
- aggregator: Aggregator,
+ instrument: metrics_api.InstrumentT,
labels: Tuple[Tuple[str, str]],
- metric: metrics_api.MetricT,
+ aggregator: Aggregator,
):
- self.aggregator = aggregator
+ self.instrument = instrument
self.labels = labels
- self.metric = metric
+ self.aggregator = aggregator
class MetricsExporter:
@@ -79,7 +79,7 @@ def export(
print(
'{}(data="{}", labels="{}", value={})'.format(
type(self).__name__,
- record.metric,
+ record.instrument,
record.labels,
record.aggregator.checkpoint,
)
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/aggregate.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/aggregate.py
index 7e1baba2c77..1745d854e9d 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/aggregate.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/aggregate.py
@@ -125,7 +125,7 @@ def merge(self, other):
)
-class ObserverAggregator(Aggregator):
+class ValueObserverAggregator(Aggregator):
"""Same as MinMaxSumCount but also with last value."""
_TYPE = namedtuple("minmaxsumcountlast", "min max sum count last")
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/batcher.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/batcher.py
index eda504d5684..db3675ecd61 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/batcher.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/export/batcher.py
@@ -15,13 +15,18 @@
import abc
from typing import Sequence, Type
-from opentelemetry.metrics import Counter, MetricT, Observer, ValueRecorder
+from opentelemetry.metrics import (
+ Counter,
+ InstrumentT,
+ ValueObserver,
+ ValueRecorder,
+)
from opentelemetry.sdk.metrics.export import MetricRecord
from opentelemetry.sdk.metrics.export.aggregate import (
Aggregator,
CounterAggregator,
MinMaxSumCountAggregator,
- ObserverAggregator,
+ ValueObserverAggregator,
)
@@ -41,18 +46,18 @@ def __init__(self, stateful: bool):
# (deltas)
self.stateful = stateful
- def aggregator_for(self, metric_type: Type[MetricT]) -> Aggregator:
- """Returns an aggregator based on metric type.
+ def aggregator_for(self, instrument_type: Type[InstrumentT]) -> Aggregator:
+ """Returns an aggregator based on metric instrument type.
Aggregators keep track of and updates values when metrics get updated.
"""
# pylint:disable=R0201
- if issubclass(metric_type, Counter):
+ if issubclass(instrument_type, Counter):
return CounterAggregator()
- if issubclass(metric_type, ValueRecorder):
+ if issubclass(instrument_type, ValueRecorder):
return MinMaxSumCountAggregator()
- if issubclass(metric_type, Observer):
- return ObserverAggregator()
+ if issubclass(instrument_type, ValueObserver):
+ return ValueObserverAggregator()
# TODO: Add other aggregators
return CounterAggregator()
@@ -63,8 +68,8 @@ def checkpoint_set(self) -> Sequence[MetricRecord]:
data in all of the aggregators in this batcher.
"""
metric_records = []
- for (metric, labels), aggregator in self._batch_map.items():
- metric_records.append(MetricRecord(aggregator, labels, metric))
+ for (instrument, labels), aggregator in self._batch_map.items():
+ metric_records.append(MetricRecord(instrument, labels, aggregator))
return metric_records
def finished_collection(self):
@@ -90,7 +95,7 @@ class UngroupedBatcher(Batcher):
def process(self, record):
# Checkpoints the current aggregator value to be collected for export
record.aggregator.take_checkpoint()
- batch_key = (record.metric, record.labels)
+ batch_key = (record.instrument, record.labels)
batch_value = self._batch_map.get(batch_key)
aggregator = record.aggregator
if batch_value:
@@ -101,6 +106,6 @@ def process(self, record):
if self.stateful:
# if stateful batcher, create a copy of the aggregator and update
# it with the current checkpointed value for long-term storage
- aggregator = self.aggregator_for(record.metric.__class__)
+ aggregator = self.aggregator_for(record.instrument.__class__)
aggregator.merge(record.aggregator)
self._batch_map[batch_key] = aggregator
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
index 678682f0db0..77f4df266bc 100644
--- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py
@@ -424,6 +424,12 @@ def set_attribute(self, key: str, value: types.AttributeValue) -> None:
# Freeze mutable sequences defensively
if isinstance(value, MutableSequence):
value = tuple(value)
+ if isinstance(value, bytes):
+ try:
+ value = value.decode()
+ except ValueError:
+ logger.warning("Byte attribute could not be decoded.")
+ return
with self._lock:
self.attributes[key] = value
diff --git a/opentelemetry-sdk/tests/metrics/export/test_export.py b/opentelemetry-sdk/tests/metrics/export/test_export.py
index c77a132459c..178e41b2134 100644
--- a/opentelemetry-sdk/tests/metrics/export/test_export.py
+++ b/opentelemetry-sdk/tests/metrics/export/test_export.py
@@ -26,7 +26,7 @@
from opentelemetry.sdk.metrics.export.aggregate import (
CounterAggregator,
MinMaxSumCountAggregator,
- ObserverAggregator,
+ ValueObserverAggregator,
)
from opentelemetry.sdk.metrics.export.batcher import UngroupedBatcher
from opentelemetry.sdk.metrics.export.controller import PushController
@@ -48,7 +48,7 @@ def test_export(self):
)
labels = {"environment": "staging"}
aggregator = CounterAggregator()
- record = MetricRecord(aggregator, labels, metric)
+ record = MetricRecord(metric, labels, aggregator)
result = '{}(data="{}", labels="{}", value={})'.format(
ConsoleMetricsExporter.__name__,
metric,
@@ -90,7 +90,7 @@ def test_checkpoint_set(self):
batcher._batch_map = _batch_map
records = batcher.checkpoint_set()
self.assertEqual(len(records), 1)
- self.assertEqual(records[0].metric, metric)
+ self.assertEqual(records[0].instrument, metric)
self.assertEqual(records[0].labels, labels)
self.assertEqual(records[0].aggregator, aggregator)
@@ -432,11 +432,11 @@ def test_concurrent_update_and_checkpoint(self):
self.assertEqual(checkpoint_total, fut.result())
-class TestObserverAggregator(unittest.TestCase):
+class TestValueObserverAggregator(unittest.TestCase):
@mock.patch("opentelemetry.sdk.metrics.export.aggregate.time_ns")
def test_update(self, time_mock):
time_mock.return_value = 123
- observer = ObserverAggregator()
+ observer = ValueObserverAggregator()
# test current values without any update
self.assertEqual(observer.mmsc.current, (None, None, None, 0))
self.assertIsNone(observer.current)
@@ -455,7 +455,7 @@ def test_update(self, time_mock):
self.assertEqual(observer.current, values[-1])
def test_checkpoint(self):
- observer = ObserverAggregator()
+ observer = ValueObserverAggregator()
# take checkpoint wihtout any update
observer.take_checkpoint()
@@ -473,15 +473,19 @@ def test_checkpoint(self):
)
def test_merge(self):
- observer1 = ObserverAggregator()
- observer2 = ObserverAggregator()
+ observer1 = ValueObserverAggregator()
+ observer2 = ValueObserverAggregator()
mmsc_checkpoint1 = MinMaxSumCountAggregator._TYPE(3, 150, 101, 3)
mmsc_checkpoint2 = MinMaxSumCountAggregator._TYPE(1, 33, 44, 2)
- checkpoint1 = ObserverAggregator._TYPE(*(mmsc_checkpoint1 + (23,)))
+ checkpoint1 = ValueObserverAggregator._TYPE(
+ *(mmsc_checkpoint1 + (23,))
+ )
- checkpoint2 = ObserverAggregator._TYPE(*(mmsc_checkpoint2 + (27,)))
+ checkpoint2 = ValueObserverAggregator._TYPE(
+ *(mmsc_checkpoint2 + (27,))
+ )
observer1.mmsc.checkpoint = mmsc_checkpoint1
observer2.mmsc.checkpoint = mmsc_checkpoint2
@@ -507,15 +511,19 @@ def test_merge(self):
self.assertEqual(observer1.last_update_timestamp, 123)
def test_merge_last_updated(self):
- observer1 = ObserverAggregator()
- observer2 = ObserverAggregator()
+ observer1 = ValueObserverAggregator()
+ observer2 = ValueObserverAggregator()
mmsc_checkpoint1 = MinMaxSumCountAggregator._TYPE(3, 150, 101, 3)
mmsc_checkpoint2 = MinMaxSumCountAggregator._TYPE(1, 33, 44, 2)
- checkpoint1 = ObserverAggregator._TYPE(*(mmsc_checkpoint1 + (23,)))
+ checkpoint1 = ValueObserverAggregator._TYPE(
+ *(mmsc_checkpoint1 + (23,))
+ )
- checkpoint2 = ObserverAggregator._TYPE(*(mmsc_checkpoint2 + (27,)))
+ checkpoint2 = ValueObserverAggregator._TYPE(
+ *(mmsc_checkpoint2 + (27,))
+ )
observer1.mmsc.checkpoint = mmsc_checkpoint1
observer2.mmsc.checkpoint = mmsc_checkpoint2
@@ -541,15 +549,19 @@ def test_merge_last_updated(self):
self.assertEqual(observer1.last_update_timestamp, 123)
def test_merge_last_updated_none(self):
- observer1 = ObserverAggregator()
- observer2 = ObserverAggregator()
+ observer1 = ValueObserverAggregator()
+ observer2 = ValueObserverAggregator()
mmsc_checkpoint1 = MinMaxSumCountAggregator._TYPE(3, 150, 101, 3)
mmsc_checkpoint2 = MinMaxSumCountAggregator._TYPE(1, 33, 44, 2)
- checkpoint1 = ObserverAggregator._TYPE(*(mmsc_checkpoint1 + (23,)))
+ checkpoint1 = ValueObserverAggregator._TYPE(
+ *(mmsc_checkpoint1 + (23,))
+ )
- checkpoint2 = ObserverAggregator._TYPE(*(mmsc_checkpoint2 + (27,)))
+ checkpoint2 = ValueObserverAggregator._TYPE(
+ *(mmsc_checkpoint2 + (27,))
+ )
observer1.mmsc.checkpoint = mmsc_checkpoint1
observer2.mmsc.checkpoint = mmsc_checkpoint2
@@ -575,11 +587,13 @@ def test_merge_last_updated_none(self):
self.assertEqual(observer1.last_update_timestamp, 100)
def test_merge_with_empty(self):
- observer1 = ObserverAggregator()
- observer2 = ObserverAggregator()
+ observer1 = ValueObserverAggregator()
+ observer2 = ValueObserverAggregator()
mmsc_checkpoint1 = MinMaxSumCountAggregator._TYPE(3, 150, 101, 3)
- checkpoint1 = ObserverAggregator._TYPE(*(mmsc_checkpoint1 + (23,)))
+ checkpoint1 = ValueObserverAggregator._TYPE(
+ *(mmsc_checkpoint1 + (23,))
+ )
observer1.mmsc.checkpoint = mmsc_checkpoint1
observer1.checkpoint = checkpoint1
diff --git a/opentelemetry-sdk/tests/metrics/test_metrics.py b/opentelemetry-sdk/tests/metrics/test_metrics.py
index 9d5d2b15d8e..4c2d691549d 100644
--- a/opentelemetry-sdk/tests/metrics/test_metrics.py
+++ b/opentelemetry-sdk/tests/metrics/test_metrics.py
@@ -88,7 +88,7 @@ def callback(observer):
self.assertIsInstance(observer, metrics_api.Observer)
observer.observe(45, {})
- observer = metrics.Observer(
+ observer = metrics.ValueObserver(
callback, "name", "desc", "unit", int, meter, (), True
)
@@ -165,7 +165,7 @@ def test_register_observer(self):
callback = mock.Mock()
observer = meter.register_observer(
- callback, "name", "desc", "unit", int, (), True
+ callback, "name", "desc", "unit", int, metrics.ValueObserver
)
self.assertIsInstance(observer, metrics_api.Observer)
@@ -185,7 +185,7 @@ def test_unregister_observer(self):
callback = mock.Mock()
observer = meter.register_observer(
- callback, "name", "desc", "unit", int, (), True
+ callback, "name", "desc", "unit", int, metrics.ValueObserver
)
meter.unregister_observer(observer)
@@ -290,10 +290,10 @@ def test_record(self):
)
-class TestObserver(unittest.TestCase):
+class TestValueObserver(unittest.TestCase):
def test_observe(self):
meter = metrics.MeterProvider().get_meter(__name__)
- observer = metrics.Observer(
+ observer = metrics.ValueObserver(
None, "name", "desc", "unit", int, meter, ("key",), True
)
labels = {"key": "value"}
@@ -310,7 +310,7 @@ def test_observe(self):
def test_observe_disabled(self):
meter = metrics.MeterProvider().get_meter(__name__)
- observer = metrics.Observer(
+ observer = metrics.ValueObserver(
None, "name", "desc", "unit", int, meter, ("key",), False
)
labels = {"key": "value"}
@@ -320,7 +320,7 @@ def test_observe_disabled(self):
@mock.patch("opentelemetry.sdk.metrics.logger")
def test_observe_incorrect_type(self, logger_mock):
meter = metrics.MeterProvider().get_meter(__name__)
- observer = metrics.Observer(
+ observer = metrics.ValueObserver(
None, "name", "desc", "unit", int, meter, ("key",), True
)
labels = {"key": "value"}
@@ -332,7 +332,7 @@ def test_run(self):
meter = metrics.MeterProvider().get_meter(__name__)
callback = mock.Mock()
- observer = metrics.Observer(
+ observer = metrics.ValueObserver(
callback, "name", "desc", "unit", int, meter, (), True
)
@@ -346,7 +346,7 @@ def test_run_exception(self, logger_mock):
callback = mock.Mock()
callback.side_effect = Exception("We have a problem!")
- observer = metrics.Observer(
+ observer = metrics.ValueObserver(
callback, "name", "desc", "unit", int, meter, (), True
)
diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py
index e60f23b8ce3..b3600f042fe 100644
--- a/opentelemetry-sdk/tests/trace/test_trace.py
+++ b/opentelemetry-sdk/tests/trace/test_trace.py
@@ -471,6 +471,22 @@ def test_invalid_attribute_values(self):
self.assertEqual(len(root.attributes), 0)
+ def test_byte_type_attribute_value(self):
+ with self.tracer.start_as_current_span("root") as root:
+ with self.assertLogs(level=WARNING):
+ root.set_attribute(
+ "invalid-byte-type-attribute",
+ b"\xd8\xe1\xb7\xeb\xa8\xe5 \xd2\xb7\xe1",
+ )
+ self.assertFalse(
+ "invalid-byte-type-attribute" in root.attributes
+ )
+
+ root.set_attribute("valid-byte-type-attribute", b"valid byte")
+ self.assertTrue(
+ isinstance(root.attributes["valid-byte-type-attribute"], str)
+ )
+
def test_check_attribute_helper(self):
# pylint: disable=protected-access
self.assertFalse(trace._is_valid_attribute_value([1, 2, 3.4, "ss", 4]))