From 54e2fef02acc8c7bc1ad8afef42a868aad124f34 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Sat, 21 May 2022 01:51:56 +0530 Subject: [PATCH 01/37] Add meter provider and in memory reader to TestBase (#2707) --- .../src/opentelemetry/test/test_base.py | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/test_base.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/test_base.py index f176238add3..98e820438d3 100644 --- a/tests/opentelemetry-test-utils/src/opentelemetry/test/test_base.py +++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/test_base.py @@ -15,13 +15,20 @@ import logging import unittest from contextlib import contextmanager +from typing import Tuple +from opentelemetry import metrics as metrics_api from opentelemetry import trace as trace_api +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export import InMemoryMetricReader, MetricReader from opentelemetry.sdk.trace import TracerProvider, export from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( InMemorySpanExporter, ) -from opentelemetry.test.globals_test import reset_trace_globals +from opentelemetry.test.globals_test import ( + reset_metrics_globals, + reset_trace_globals, +) class TestBase(unittest.TestCase): @@ -36,6 +43,11 @@ def setUpClass(cls): reset_trace_globals() trace_api.set_tracer_provider(cls.tracer_provider) + result = cls.create_meter_provider() + cls.meter_provider, cls.memory_metrics_reader = result + reset_metrics_globals() + metrics_api.set_meter_provider(cls.meter_provider) + @classmethod def tearDownClass(cls): # This is done because set_tracer_provider cannot override the @@ -92,6 +104,21 @@ def create_tracer_provider(**kwargs): return tracer_provider, memory_exporter + @staticmethod + def create_meter_provider(**kwargs) -> Tuple[MeterProvider, MetricReader]: + """Helper to create a configured meter provider + Creates a `MeterProvider` and an `InMemoryMetricReader`. + Returns: + A tuple with the meter provider in the first element and the + in-memory metrics exporter in the second + """ + memory_reader = InMemoryMetricReader() + metric_readers = kwargs.get("metric_readers", []) + metric_readers.append(memory_reader) + kwargs["metric_readers"] = metric_readers + meter_provider = MeterProvider(**kwargs) + return meter_provider, memory_reader + @staticmethod @contextmanager def disable_logging(highest_level=logging.CRITICAL): From abd9a5bd29525af969f7ab188e5e8e36ca548c53 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 24 May 2022 18:00:39 +0200 Subject: [PATCH 02/37] Document usage of stat_as_current_span as decorator (#2708) --- .../src/opentelemetry/trace/__init__.py | 8 +++ opentelemetry-api/tests/trace/test_tracer.py | 52 +++++++++++++++---- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/trace/__init__.py b/opentelemetry-api/src/opentelemetry/trace/__init__.py index 53eb0e96db4..be0d8933b7e 100644 --- a/opentelemetry-api/src/opentelemetry/trace/__init__.py +++ b/opentelemetry-api/src/opentelemetry/trace/__init__.py @@ -371,6 +371,14 @@ def start_as_current_span( with opentelemetry.trace.use_span(span, end_on_exit=True): do_work() + This can also be used as a decorator:: + + @tracer.start_as_current_span("name"): + def function(): + ... + + function() + Args: name: The name of the span to be created. context: An optional Context containing the span's parent. Defaults to the diff --git a/opentelemetry-api/tests/trace/test_tracer.py b/opentelemetry-api/tests/trace/test_tracer.py index 774746d41a6..a7ad589ae60 100644 --- a/opentelemetry-api/tests/trace/test_tracer.py +++ b/opentelemetry-api/tests/trace/test_tracer.py @@ -12,25 +12,59 @@ # See the License for the specific language governing permissions and # limitations under the License. -import unittest -from opentelemetry import trace +from contextlib import contextmanager +from unittest import TestCase +from unittest.mock import Mock +from opentelemetry.trace import ( + INVALID_SPAN, + NoOpTracer, + Span, + Tracer, + get_current_span, +) -class TestTracer(unittest.TestCase): + +class TestTracer(TestCase): def setUp(self): - self.tracer = trace.NoOpTracer() + self.tracer = NoOpTracer() def test_start_span(self): with self.tracer.start_span("") as span: - self.assertIsInstance(span, trace.Span) + self.assertIsInstance(span, Span) - def test_start_as_current_span(self): + def test_start_as_current_span_context_manager(self): with self.tracer.start_as_current_span("") as span: - self.assertIsInstance(span, trace.Span) + self.assertIsInstance(span, Span) + + def test_start_as_current_span_decorator(self): + + mock_call = Mock() + + class MockTracer(Tracer): + def start_span(self, *args, **kwargs): + return INVALID_SPAN + + @contextmanager + def start_as_current_span(self, *args, **kwargs): # type: ignore + mock_call() + yield INVALID_SPAN + + mock_tracer = MockTracer() + + @mock_tracer.start_as_current_span("name") + def function(): # type: ignore + pass + + function() # type: ignore + function() # type: ignore + function() # type: ignore + + self.assertEqual(mock_call.call_count, 3) def test_get_current_span(self): with self.tracer.start_as_current_span("test") as span: - trace.get_current_span().set_attribute("test", "test") - self.assertEqual(span, trace.INVALID_SPAN) + get_current_span().set_attribute("test", "test") + self.assertEqual(span, INVALID_SPAN) self.assertFalse(hasattr("span", "attributes")) From 8200f45a1fa1eb56c2b9ad73c12468542fb5184e Mon Sep 17 00:00:00 2001 From: Olivier VERMEULEN Date: Wed, 25 May 2022 12:02:16 +0200 Subject: [PATCH 03/37] Fix LogEmitterProvider.force_flush hanging randomly (#2714) Co-authored-by: Srikanth Chekuri --- CHANGELOG.md | 3 +++ .../src/opentelemetry/sdk/_logs/export/__init__.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a29ec31f03..965e4802c6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.12.0rc1-0.31b0...HEAD) +- Fix LogEmitterProvider.force_flush hanging randomly + ([#2714](https://github.com/open-telemetry/opentelemetry-python/pull/2714)) + ## [1.12.0rc1-0.31b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc1-0.31b0) - 2022-05-17 diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py index 87ac308317d..def39e32c9d 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py @@ -178,7 +178,7 @@ def worker(self): flush_request = self._get_and_unset_flush_request() if ( len(self._queue) < self._max_export_batch_size - and self._flush_request is None + and flush_request is None ): self._condition.wait(timeout) From 709afdd89e831780f9f76ade073f397990b8b70c Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 25 May 2022 18:48:15 +0200 Subject: [PATCH 04/37] Document preferred method for attribute setting (#2712) --- opentelemetry-api/src/opentelemetry/trace/span.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/trace/span.py b/opentelemetry-api/src/opentelemetry/trace/span.py index 8846ff50a50..cb9992557cc 100644 --- a/opentelemetry-api/src/opentelemetry/trace/span.py +++ b/opentelemetry-api/src/opentelemetry/trace/span.py @@ -86,7 +86,10 @@ def set_attributes( Sets Attributes with the key and value passed as arguments dict. - Note: The behavior of `None` value attributes is undefined, and hence strongly discouraged. + Note: The behavior of `None` value attributes is undefined, and hence + strongly discouraged. It is also preferred to set attributes at span + creation, instead of calling this method later since samplers can only + consider information already present during span creation. """ @abc.abstractmethod @@ -95,7 +98,10 @@ def set_attribute(self, key: str, value: types.AttributeValue) -> None: Sets a single Attribute with the key and value passed as arguments. - Note: The behavior of `None` value attributes is undefined, and hence strongly discouraged. + Note: The behavior of `None` value attributes is undefined, and hence + strongly discouraged. It is also preferred to set attributes at span + creation, instead of calling this method later since samplers can only + consider information already present during span creation. """ @abc.abstractmethod From 77b47993f86bb9a9f9dde6b4f3a33cfd43658128 Mon Sep 17 00:00:00 2001 From: "Paul \"TBBle\" Hampson" Date: Fri, 27 May 2022 06:05:03 +1000 Subject: [PATCH 05/37] narrow protobuf dependencies to exclude protobuf >= 4 (#2720) --- CHANGELOG.md | 2 ++ exporter/opentelemetry-exporter-opencensus/setup.cfg | 2 +- exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg | 2 +- opentelemetry-proto/setup.cfg | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 965e4802c6d..e6f75e7b965 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix LogEmitterProvider.force_flush hanging randomly ([#2714](https://github.com/open-telemetry/opentelemetry-python/pull/2714)) +- narrow protobuf dependencies to exclude protobuf >= 4 + ([#2720](https://github.com/open-telemetry/opentelemetry-python/pull/2720)) ## [1.12.0rc1-0.31b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc1-0.31b0) - 2022-05-17 diff --git a/exporter/opentelemetry-exporter-opencensus/setup.cfg b/exporter/opentelemetry-exporter-opencensus/setup.cfg index f770c1e74d9..dd984a23462 100644 --- a/exporter/opentelemetry-exporter-opencensus/setup.cfg +++ b/exporter/opentelemetry-exporter-opencensus/setup.cfg @@ -45,7 +45,7 @@ install_requires = opencensus-proto >= 0.1.0, < 1.0.0 opentelemetry-api ~= 1.3 opentelemetry-sdk ~= 1.3 - protobuf >= 3.13.0 + protobuf ~= 3.13 setuptools >= 16.0 [options.packages.find] diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg b/exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg index a7a35e4c988..70cbd52b139 100644 --- a/exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg @@ -41,7 +41,7 @@ package_dir= =src packages=find_namespace: install_requires = - protobuf >= 3.12 + protobuf ~= 3.12 requests ~= 2.7 opentelemetry-api ~= 1.3 opentelemetry-sdk ~= 1.11 diff --git a/opentelemetry-proto/setup.cfg b/opentelemetry-proto/setup.cfg index 6f19ae8fd3a..94ec991771d 100644 --- a/opentelemetry-proto/setup.cfg +++ b/opentelemetry-proto/setup.cfg @@ -42,7 +42,7 @@ packages=find_namespace: zip_safe = False include_package_data = True install_requires = - protobuf>=3.13.0 + protobuf~=3.13 [options.packages.find] where = src From eed40f7ef5206d6c0503b7408847ec3c002388c7 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 27 May 2022 00:29:01 +0200 Subject: [PATCH 06/37] Fix link in README (#2715) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 550e08936d8..f5620db91c8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ---

- Getting Started + Getting Started   •   API Documentation   •   From cad776a2031c84fb3c3a1af90ee2a939f3394b9a Mon Sep 17 00:00:00 2001 From: Alan Isaac Date: Fri, 27 May 2022 01:41:13 -0400 Subject: [PATCH 07/37] Added generic to textmap getter and setter (#2657) * added generic to textmap getter and setter * added CHANGELOG entry Co-authored-by: Srikanth Chekuri --- CHANGELOG.md | 2 ++ .../baggage/propagation/__init__.py | 4 ++-- .../src/opentelemetry/propagate/__init__.py | 4 ++-- .../opentelemetry/propagators/composite.py | 4 ++-- .../src/opentelemetry/propagators/textmap.py | 24 +++++++++---------- .../trace/propagation/tracecontext.py | 4 ++-- 6 files changed, 22 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6f75e7b965..6604cccea1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.12.0rc1-0.31b0...HEAD) +- Fix type hints for textmap `Getter` and `Setter` + ([#2657](https://github.com/open-telemetry/opentelemetry-python/pull/2657)) - Fix LogEmitterProvider.force_flush hanging randomly ([#2714](https://github.com/open-telemetry/opentelemetry-python/pull/2714)) - narrow protobuf dependencies to exclude protobuf >= 4 diff --git a/opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py b/opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py index d194acfe2ef..edba837b816 100644 --- a/opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py +++ b/opentelemetry-api/src/opentelemetry/baggage/propagation/__init__.py @@ -38,7 +38,7 @@ def extract( self, carrier: textmap.CarrierT, context: Optional[Context] = None, - getter: textmap.Getter = textmap.default_getter, + getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter, ) -> Context: """Extract Baggage from the carrier. @@ -109,7 +109,7 @@ def inject( self, carrier: textmap.CarrierT, context: Optional[Context] = None, - setter: textmap.Setter = textmap.default_setter, + setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter, ) -> None: """Injects Baggage into the carrier. diff --git a/opentelemetry-api/src/opentelemetry/propagate/__init__.py b/opentelemetry-api/src/opentelemetry/propagate/__init__.py index 5493c5f0882..f197f1f9149 100644 --- a/opentelemetry-api/src/opentelemetry/propagate/__init__.py +++ b/opentelemetry-api/src/opentelemetry/propagate/__init__.py @@ -84,7 +84,7 @@ def example_route(): def extract( carrier: textmap.CarrierT, context: typing.Optional[Context] = None, - getter: textmap.Getter = textmap.default_getter, + getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter, ) -> Context: """Uses the configured propagator to extract a Context from the carrier. @@ -105,7 +105,7 @@ def extract( def inject( carrier: textmap.CarrierT, context: typing.Optional[Context] = None, - setter: textmap.Setter = textmap.default_setter, + setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter, ) -> None: """Uses the configured propagator to inject a Context into the carrier. diff --git a/opentelemetry-api/src/opentelemetry/propagators/composite.py b/opentelemetry-api/src/opentelemetry/propagators/composite.py index b06e385b588..77330d94103 100644 --- a/opentelemetry-api/src/opentelemetry/propagators/composite.py +++ b/opentelemetry-api/src/opentelemetry/propagators/composite.py @@ -39,7 +39,7 @@ def extract( self, carrier: textmap.CarrierT, context: typing.Optional[Context] = None, - getter: textmap.Getter = textmap.default_getter, + getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter, ) -> Context: """Run each of the configured propagators with the given context and carrier. Propagators are run in the order they are configured, if multiple @@ -56,7 +56,7 @@ def inject( self, carrier: textmap.CarrierT, context: typing.Optional[Context] = None, - setter: textmap.Setter = textmap.default_setter, + setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter, ) -> None: """Run each of the configured propagators with the given context and carrier. Propagators are run in the order they are configured, if multiple diff --git a/opentelemetry-api/src/opentelemetry/propagators/textmap.py b/opentelemetry-api/src/opentelemetry/propagators/textmap.py index 0011315cf21..afadd35000b 100644 --- a/opentelemetry-api/src/opentelemetry/propagators/textmap.py +++ b/opentelemetry-api/src/opentelemetry/propagators/textmap.py @@ -21,7 +21,7 @@ CarrierValT = typing.Union[typing.List[str], str] -class Getter(abc.ABC): +class Getter(abc.ABC, typing.Generic[CarrierT]): """This class implements a Getter that enables extracting propagated fields from a carrier. """ @@ -54,7 +54,7 @@ def keys(self, carrier: CarrierT) -> typing.List[str]: """ -class Setter(abc.ABC): +class Setter(abc.ABC, typing.Generic[CarrierT]): """This class implements a Setter that enables injecting propagated fields into a carrier. """ @@ -71,8 +71,8 @@ def set(self, carrier: CarrierT, key: str, value: str) -> None: """ -class DefaultGetter(Getter): - def get( # type: ignore +class DefaultGetter(Getter[typing.Mapping[str, CarrierValT]]): + def get( self, carrier: typing.Mapping[str, CarrierValT], key: str ) -> typing.Optional[typing.List[str]]: """Getter implementation to retrieve a value from a dictionary. @@ -90,18 +90,18 @@ def get( # type: ignore return list(val) return [val] - def keys( # type: ignore - self, carrier: typing.Dict[str, CarrierValT] + def keys( + self, carrier: typing.Mapping[str, CarrierValT] ) -> typing.List[str]: """Keys implementation that returns all keys from a dictionary.""" return list(carrier.keys()) -default_getter = DefaultGetter() +default_getter: Getter[CarrierT] = DefaultGetter() # type: ignore -class DefaultSetter(Setter): - def set( # type: ignore +class DefaultSetter(Setter[typing.MutableMapping[str, CarrierValT]]): + def set( self, carrier: typing.MutableMapping[str, CarrierValT], key: str, @@ -117,7 +117,7 @@ def set( # type: ignore carrier[key] = value -default_setter = DefaultSetter() +default_setter: Setter[CarrierT] = DefaultSetter() # type: ignore class TextMapPropagator(abc.ABC): @@ -134,7 +134,7 @@ def extract( self, carrier: CarrierT, context: typing.Optional[Context] = None, - getter: Getter = default_getter, + getter: Getter[CarrierT] = default_getter, ) -> Context: """Create a Context from values in the carrier. @@ -162,7 +162,7 @@ def inject( self, carrier: CarrierT, context: typing.Optional[Context] = None, - setter: Setter = default_setter, + setter: Setter[CarrierT] = default_setter, ) -> None: """Inject values from a Context into a carrier. diff --git a/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py b/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py index 82cc078efcd..af16a08f0be 100644 --- a/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py +++ b/opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py @@ -37,7 +37,7 @@ def extract( self, carrier: textmap.CarrierT, context: typing.Optional[Context] = None, - getter: textmap.Getter = textmap.default_getter, + getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter, ) -> Context: """Extracts SpanContext from the carrier. @@ -90,7 +90,7 @@ def inject( self, carrier: textmap.CarrierT, context: typing.Optional[Context] = None, - setter: textmap.Setter = textmap.default_setter, + setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter, ) -> None: """Injects SpanContext into the carrier. From 0bfa9a64d51d597a506ae92ddb6a1c764fa3812c Mon Sep 17 00:00:00 2001 From: Hannes Ljungberg Date: Tue, 31 May 2022 19:20:04 +0200 Subject: [PATCH 08/37] Loosen dependency on backoff for newer Python versions (#2726) --- CHANGELOG.md | 2 ++ exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg | 3 ++- exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6604cccea1d..ea2027a09ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2714](https://github.com/open-telemetry/opentelemetry-python/pull/2714)) - narrow protobuf dependencies to exclude protobuf >= 4 ([#2720](https://github.com/open-telemetry/opentelemetry-python/pull/2720)) +- Loosen dependency on `backoff` for newer Python versions + ([#2726](https://github.com/open-telemetry/opentelemetry-python/pull/2726)) ## [1.12.0rc1-0.31b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc1-0.31b0) - 2022-05-17 diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg b/exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg index 13d06b2b15b..4206782846f 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg @@ -45,7 +45,8 @@ install_requires = opentelemetry-api ~= 1.3 opentelemetry-sdk ~= 1.11 opentelemetry-proto == 1.12.0rc1 - backoff >= 1.10.0, < 2.0.0 + backoff >= 1.10.0, < 2.0.0; python_version<'3.7' + backoff >= 1.10.0, < 3.0.0; python_version>='3.7' [options.extras_require] test = diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg b/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg index cf73900ed26..e434fb794eb 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg +++ b/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg @@ -45,7 +45,8 @@ install_requires = opentelemetry-api ~= 1.3 opentelemetry-sdk ~= 1.11 opentelemetry-proto == 1.12.0rc1 - backoff >= 1.10.0, < 2.0.0 + backoff >= 1.10.0, < 2.0.0; python_version<'3.7' + backoff >= 1.10.0, < 3.0.0; python_version>='3.7' [options.extras_require] test = From 1dd18556dfe1089d04c417adeddfdd3b18e6d67e Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Wed, 1 Jun 2022 23:58:01 +0530 Subject: [PATCH 09/37] fix: `frozenset` object has no attribute `items` (#2727) --- CHANGELOG.md | 4 ++-- .../sdk/metrics/_internal/_view_instrument_match.py | 10 +++++----- .../tests/metrics/test_view_instrument_match.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea2027a09ce..fbedb9a39f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,11 +15,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2720](https://github.com/open-telemetry/opentelemetry-python/pull/2720)) - Loosen dependency on `backoff` for newer Python versions ([#2726](https://github.com/open-telemetry/opentelemetry-python/pull/2726)) +- fix: frozenset object has no attribute items + ([#2727](https://github.com/open-telemetry/opentelemetry-python/pull/2727)) ## [1.12.0rc1-0.31b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc1-0.31b0) - 2022-05-17 - - - Fix LoggingHandler to handle LogRecord with exc_info=False ([#2690](https://github.com/open-telemetry/opentelemetry-python/pull/2690)) - Make metrics components public diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py index 972b39a321f..026b9702350 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py @@ -95,11 +95,11 @@ def consume_measurement(self, measurement: Measurement) -> None: else: attributes = {} - attributes = frozenset(attributes.items()) + aggr_key = frozenset(attributes.items()) - if attributes not in self._attributes_aggregation: + if aggr_key not in self._attributes_aggregation: with self._lock: - if attributes not in self._attributes_aggregation: + if aggr_key not in self._attributes_aggregation: if not isinstance( self._view._aggregation, DefaultAggregation ): @@ -118,9 +118,9 @@ def consume_measurement(self, measurement: Measurement) -> None: attributes, self._start_time_unix_nano, ) - self._attributes_aggregation[attributes] = aggregation + self._attributes_aggregation[aggr_key] = aggregation - self._attributes_aggregation[attributes].aggregate(measurement) + self._attributes_aggregation[aggr_key].aggregate(measurement) def collect( self, diff --git a/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py b/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py index 4904f74ddda..839f4ccfa20 100644 --- a/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py +++ b/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py @@ -209,7 +209,7 @@ def test_collect(self): number_data_point = number_data_points[0] - self.assertEqual(number_data_point.attributes, frozenset({("c", "d")})) + self.assertEqual(number_data_point.attributes, {"c": "d"}) self.assertEqual(number_data_point.value, 0) def test_setting_aggregation(self): From a31dcc3a9e947681ba955623849125bd6aecd610 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 2 Jun 2022 06:48:10 -0700 Subject: [PATCH 10/37] Specify name for worker threads for logging and metrics (#2724) --- CHANGELOG.md | 2 ++ .../src/opentelemetry/sdk/_logs/export/__init__.py | 12 ++++++++++-- .../sdk/metrics/_internal/export/__init__.py | 12 ++++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbedb9a39f9..ca239dc80ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2714](https://github.com/open-telemetry/opentelemetry-python/pull/2714)) - narrow protobuf dependencies to exclude protobuf >= 4 ([#2720](https://github.com/open-telemetry/opentelemetry-python/pull/2720)) +- Specify worker thread names + ([#2724](https://github.com/open-telemetry/opentelemetry-python/pull/2724)) - Loosen dependency on `backoff` for newer Python versions ([#2726](https://github.com/open-telemetry/opentelemetry-python/pull/2726)) - fix: frozenset object has no attribute items diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py index def39e32c9d..fb1fc828821 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py @@ -147,7 +147,11 @@ def __init__( self._max_export_batch_size = max_export_batch_size self._export_timeout_millis = export_timeout_millis self._queue = collections.deque() # type: Deque[LogData] - self._worker_thread = threading.Thread(target=self.worker, daemon=True) + self._worker_thread = threading.Thread( + name="OtelBatchLogProcessor", + target=self.worker, + daemon=True, + ) self._condition = threading.Condition(threading.Lock()) self._shutdown = False self._flush_request = None # type: Optional[_FlushRequest] @@ -164,7 +168,11 @@ def __init__( def _at_fork_reinit(self): self._condition = threading.Condition(threading.Lock()) self._queue.clear() - self._worker_thread = threading.Thread(target=self.worker, daemon=True) + self._worker_thread = threading.Thread( + name="OtelBatchLogProcessor", + target=self.worker, + daemon=True, + ) self._worker_thread.start() def worker(self): diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py index bcaca4281ee..684cb4c5067 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py @@ -373,7 +373,11 @@ def __init__( self._shutdown = False self._shutdown_event = Event() self._shutdown_once = Once() - self._daemon_thread = Thread(target=self._ticker, daemon=True) + self._daemon_thread = Thread( + name="OtelPeriodicExportingMetricReader", + target=self._ticker, + daemon=True, + ) self._daemon_thread.start() if hasattr(os, "register_at_fork"): os.register_at_fork( @@ -381,7 +385,11 @@ def __init__( ) # pylint: disable=protected-access def _at_fork_reinit(self): - self._daemon_thread = Thread(target=self._ticker, daemon=True) + self._daemon_thread = Thread( + name="OtelPeriodicExportingMetricReader", + target=self._ticker, + daemon=True, + ) self._daemon_thread.start() def _ticker(self) -> None: From f8d6795b658b83599842d1b250fe4619e299e194 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 2 Jun 2022 22:08:34 +0530 Subject: [PATCH 11/37] fix: update entry point object references for metrics (#2731) --- CHANGELOG.md | 2 ++ opentelemetry-api/setup.cfg | 2 +- opentelemetry-sdk/setup.cfg | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca239dc80ee..5807688a184 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2726](https://github.com/open-telemetry/opentelemetry-python/pull/2726)) - fix: frozenset object has no attribute items ([#2727](https://github.com/open-telemetry/opentelemetry-python/pull/2727)) +- fix: update entry point object references for metrics + ([#2731](https://github.com/open-telemetry/opentelemetry-python/pull/2731)) ## [1.12.0rc1-0.31b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc1-0.31b0) - 2022-05-17 diff --git a/opentelemetry-api/setup.cfg b/opentelemetry-api/setup.cfg index 95c2092cb9f..c15cd53a37b 100644 --- a/opentelemetry-api/setup.cfg +++ b/opentelemetry-api/setup.cfg @@ -56,7 +56,7 @@ opentelemetry_context = opentelemetry_tracer_provider = default_tracer_provider = opentelemetry.trace:NoOpTracerProvider opentelemetry_meter_provider = - default_meter_provider = opentelemetry._metrics:NoOpMeterProvider + default_meter_provider = opentelemetry.metrics:NoOpMeterProvider opentelemetry_propagator = tracecontext = opentelemetry.trace.propagation.tracecontext:TraceContextTextMapPropagator baggage = opentelemetry.baggage.propagation:W3CBaggagePropagator diff --git a/opentelemetry-sdk/setup.cfg b/opentelemetry-sdk/setup.cfg index 0581931d8e6..7ee57a00a3f 100644 --- a/opentelemetry-sdk/setup.cfg +++ b/opentelemetry-sdk/setup.cfg @@ -58,13 +58,13 @@ opentelemetry_tracer_provider = opentelemetry_traces_exporter = console = opentelemetry.sdk.trace.export:ConsoleSpanExporter opentelemetry_meter_provider = - sdk_meter_provider = opentelemetry.sdk._metrics:MeterProvider + sdk_meter_provider = opentelemetry.sdk.metrics:MeterProvider opentelemetry_log_emitter_provider = sdk_log_emitter_provider = opentelemetry.sdk._logs:LogEmitterProvider opentelemetry_logs_exporter = console = opentelemetry.sdk._logs.export:ConsoleLogExporter opentelemetry_metrics_exporter = - console = opentelemetry.sdk._metrics.export:ConsoleMetricExporter + console = opentelemetry.sdk.metrics.export:ConsoleMetricExporter opentelemetry_id_generator = random = opentelemetry.sdk.trace.id_generator:RandomIdGenerator opentelemetry_environment_variables = From 928d333546a5ea792bc977a4fdb415e565627e7b Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Tue, 7 Jun 2022 20:02:00 +0530 Subject: [PATCH 12/37] Support logs SDK auto instrumentation enable/disable with env (#2728) --- CHANGELOG.md | 2 ++ .../sdk/_configuration/__init__.py | 10 ++++++- .../sdk/environment_variables.py | 14 ++++++++++ opentelemetry-sdk/tests/test_configurator.py | 26 +++++++++++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5807688a184..b0ee24bfd90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2726](https://github.com/open-telemetry/opentelemetry-python/pull/2726)) - fix: frozenset object has no attribute items ([#2727](https://github.com/open-telemetry/opentelemetry-python/pull/2727)) +- Support logs SDK auto instrumentation enable/disable with env + ([#2728](https://github.com/open-telemetry/opentelemetry-python/pull/2728)) - fix: update entry point object references for metrics ([#2731](https://github.com/open-telemetry/opentelemetry-python/pull/2731)) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py index efa6f31f894..8cb1d0f412f 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py @@ -18,6 +18,7 @@ """ import logging +import os from abc import ABC, abstractmethod from os import environ from typing import Dict, Optional, Sequence, Tuple, Type @@ -36,6 +37,9 @@ set_log_emitter_provider, ) from opentelemetry.sdk._logs.export import BatchLogProcessor, LogExporter +from opentelemetry.sdk.environment_variables import ( + _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, +) from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor, SpanExporter @@ -185,7 +189,11 @@ def _initialize_components(auto_instrumentation_version): id_generator_name = _get_id_generator() id_generator = _import_id_generator(id_generator_name) _init_tracing(trace_exporters, id_generator, auto_instrumentation_version) - _init_logging(log_exporters, auto_instrumentation_version) + logging_enabled = os.getenv( + _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, "false" + ) + if logging_enabled.strip().lower() == "true": + _init_logging(log_exporters, auto_instrumentation_version) class _BaseConfigurator(ABC): diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables.py b/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables.py index 145489a683e..be105c3fbb8 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/environment_variables.py @@ -407,6 +407,20 @@ LogEmitterProvider is used. """ +_OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED = ( + "OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED" +) +""" +.. envvar:: OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED + +The :envvar:`OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED` environment variable allows users to +enable/disabe the logging SDK auto instrumentation. +Default: False + +Note: Logs SDK and its related settings are experimental. +""" + + OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE = ( "OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE" ) diff --git a/opentelemetry-sdk/tests/test_configurator.py b/opentelemetry-sdk/tests/test_configurator.py index 1905e6fc384..1b007d3b165 100644 --- a/opentelemetry-sdk/tests/test_configurator.py +++ b/opentelemetry-sdk/tests/test_configurator.py @@ -29,6 +29,7 @@ _import_id_generator, _init_logging, _init_tracing, + _initialize_components, ) from opentelemetry.sdk._logs import LoggingHandler from opentelemetry.sdk._logs.export import ConsoleLogExporter @@ -277,6 +278,31 @@ def test_logging_init_exporter(self): logging.getLogger(__name__).error("hello") self.assertTrue(provider.processor.exporter.export_called) + @patch.dict( + environ, + {"OTEL_RESOURCE_ATTRIBUTES": "service.name=otlp-service"}, + ) + @patch("opentelemetry.sdk._configuration._init_tracing") + @patch("opentelemetry.sdk._configuration._init_logging") + def test_logging_init_disable_default(self, logging_mock, tracing_mock): + _initialize_components("auto-version") + self.assertEqual(logging_mock.call_count, 0) + self.assertEqual(tracing_mock.call_count, 1) + + @patch.dict( + environ, + { + "OTEL_RESOURCE_ATTRIBUTES": "service.name=otlp-service", + "OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED": "True", + }, + ) + @patch("opentelemetry.sdk._configuration._init_tracing") + @patch("opentelemetry.sdk._configuration._init_logging") + def test_logging_init_enable_env(self, logging_mock, tracing_mock): + _initialize_components("auto-version") + self.assertEqual(logging_mock.call_count, 1) + self.assertEqual(tracing_mock.call_count, 1) + class TestExporterNames(TestCase): def test_otlp_exporter_overwrite(self): From b83c2ae75c6e69c0484aa2141f47ff9ac63236f6 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Wed, 8 Jun 2022 16:28:16 +0530 Subject: [PATCH 13/37] Configure auto instrumentation to support metrics (#2705) * Configure auto instrumentation to support metrics * Add tests * Update test_configurator.py * Add CHANGELOG entry * lint --- CHANGELOG.md | 2 + .../sdk/_configuration/__init__.py | 60 ++++++++- opentelemetry-sdk/tests/test_configurator.py | 118 +++++++++++++++++- 3 files changed, 170 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0ee24bfd90..e33019f119e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2728](https://github.com/open-telemetry/opentelemetry-python/pull/2728)) - fix: update entry point object references for metrics ([#2731](https://github.com/open-telemetry/opentelemetry-python/pull/2731)) +- Configure auto instrumentation to support metrics + ([#2705](https://github.com/open-telemetry/opentelemetry-python/pull/2705)) ## [1.12.0rc1-0.31b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc1-0.31b0) - 2022-05-17 diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py index 8cb1d0f412f..55463e00be9 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py @@ -25,12 +25,13 @@ from pkg_resources import iter_entry_points -from opentelemetry import trace from opentelemetry.environment_variables import ( OTEL_LOGS_EXPORTER, + OTEL_METRICS_EXPORTER, OTEL_PYTHON_ID_GENERATOR, OTEL_TRACES_EXPORTER, ) +from opentelemetry.metrics import set_meter_provider from opentelemetry.sdk._logs import ( LogEmitterProvider, LoggingHandler, @@ -40,11 +41,17 @@ from opentelemetry.sdk.environment_variables import ( _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, ) +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export import ( + MetricExporter, + PeriodicExportingMetricReader, +) from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor, SpanExporter from opentelemetry.sdk.trace.id_generator import IdGenerator from opentelemetry.semconv.resource import ResourceAttributes +from opentelemetry.trace import set_tracer_provider _EXPORTER_OTLP = "otlp" _EXPORTER_OTLP_PROTO_GRPC = "otlp_proto_grpc" @@ -87,7 +94,7 @@ def _init_tracing( id_generator=id_generator(), resource=Resource.create(auto_resource), ) - trace.set_tracer_provider(provider) + set_tracer_provider(provider) for _, exporter_class in exporters.items(): exporter_args = {} @@ -96,6 +103,33 @@ def _init_tracing( ) +def _init_metrics( + exporters: Dict[str, Type[MetricExporter]], + auto_instrumentation_version: Optional[str] = None, +): + # if env var OTEL_RESOURCE_ATTRIBUTES is given, it will read the service_name + # from the env variable else defaults to "unknown_service" + auto_resource = {} + # populate version if using auto-instrumentation + if auto_instrumentation_version: + auto_resource[ + ResourceAttributes.TELEMETRY_AUTO_VERSION + ] = auto_instrumentation_version + + metric_readers = [] + + for _, exporter_class in exporters.items(): + exporter_args = {} + metric_readers.append( + PeriodicExportingMetricReader(exporter_class(**exporter_args)) + ) + + provider = MeterProvider( + resource=Resource.create(auto_resource), metric_readers=metric_readers + ) + set_meter_provider(provider) + + def _init_logging( exporters: Dict[str, Type[LogExporter]], auto_instrumentation_version: Optional[str] = None, @@ -145,9 +179,15 @@ def _import_config_components( def _import_exporters( trace_exporter_names: Sequence[str], + metric_exporter_names: Sequence[str], log_exporter_names: Sequence[str], -) -> Tuple[Dict[str, Type[SpanExporter]], Dict[str, Type[LogExporter]]]: +) -> Tuple[ + Dict[str, Type[SpanExporter]], + Dict[str, Type[MetricExporter]], + Dict[str, Type[LogExporter]], +]: trace_exporters = {} + metric_exporters = {} log_exporters = {} for (exporter_name, exporter_impl,) in _import_config_components( @@ -158,6 +198,14 @@ def _import_exporters( else: raise RuntimeError(f"{exporter_name} is not a trace exporter") + for (exporter_name, exporter_impl,) in _import_config_components( + metric_exporter_names, "opentelemetry_metrics_exporter" + ): + if issubclass(exporter_impl, MetricExporter): + metric_exporters[exporter_name] = exporter_impl + else: + raise RuntimeError(f"{exporter_name} is not a metric exporter") + for (exporter_name, exporter_impl,) in _import_config_components( log_exporter_names, "opentelemetry_logs_exporter" ): @@ -166,7 +214,7 @@ def _import_exporters( else: raise RuntimeError(f"{exporter_name} is not a log exporter") - return trace_exporters, log_exporters + return trace_exporters, metric_exporters, log_exporters def _import_id_generator(id_generator_name: str) -> IdGenerator: @@ -182,13 +230,15 @@ def _import_id_generator(id_generator_name: str) -> IdGenerator: def _initialize_components(auto_instrumentation_version): - trace_exporters, log_exporters = _import_exporters( + trace_exporters, metric_exporters, log_exporters = _import_exporters( _get_exporter_names(environ.get(OTEL_TRACES_EXPORTER)), + _get_exporter_names(environ.get(OTEL_METRICS_EXPORTER)), _get_exporter_names(environ.get(OTEL_LOGS_EXPORTER)), ) id_generator_name = _get_id_generator() id_generator = _import_id_generator(id_generator_name) _init_tracing(trace_exporters, id_generator, auto_instrumentation_version) + _init_metrics(metric_exporters, auto_instrumentation_version) logging_enabled = os.getenv( _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, "false" ) diff --git a/opentelemetry-sdk/tests/test_configurator.py b/opentelemetry-sdk/tests/test_configurator.py index 1b007d3b165..39548ff5d45 100644 --- a/opentelemetry-sdk/tests/test_configurator.py +++ b/opentelemetry-sdk/tests/test_configurator.py @@ -12,9 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. # type: ignore +# pylint: skip-file import logging from os import environ +from typing import Dict, Iterable, Optional from unittest import TestCase from unittest.mock import patch @@ -28,12 +30,21 @@ _import_exporters, _import_id_generator, _init_logging, + _init_metrics, _init_tracing, _initialize_components, ) from opentelemetry.sdk._logs import LoggingHandler from opentelemetry.sdk._logs.export import ConsoleLogExporter -from opentelemetry.sdk.metrics.export import ConsoleMetricExporter +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export import ( + AggregationTemporality, + ConsoleMetricExporter, + Metric, + MetricExporter, + MetricReader, +) +from opentelemetry.sdk.metrics.view import Aggregation from opentelemetry.sdk.resources import SERVICE_NAME, Resource from opentelemetry.sdk.trace.export import ConsoleSpanExporter from opentelemetry.sdk.trace.id_generator import IdGenerator, RandomIdGenerator @@ -61,6 +72,10 @@ def get_log_emitter(self, name): return DummyLogEmitter(name, self.resource, self.processor) +class DummyMeterProvider(MeterProvider): + pass + + class DummyLogEmitter: def __init__(self, name, resource, processor): self.name = name @@ -93,6 +108,44 @@ def __init__(self, exporter): self.exporter = exporter +class DummyMetricReader(MetricReader): + def __init__( + self, + exporter: MetricExporter, + preferred_temporality: Dict[type, AggregationTemporality] = None, + preferred_aggregation: Dict[type, Aggregation] = None, + export_interval_millis: Optional[float] = None, + export_timeout_millis: Optional[float] = None, + ) -> None: + super().__init__( + preferred_temporality=preferred_temporality, + preferred_aggregation=preferred_aggregation, + ) + self.exporter = exporter + + def _receive_metrics( + self, + metrics: Iterable[Metric], + timeout_millis: float = 10_000, + **kwargs, + ) -> None: + self.exporter.export(None) + + def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None: + return True + + +class DummyOTLPMetricExporter: + def __init__(self, *args, **kwargs): + self.export_called = False + + def export(self, batch): + self.export_called = True + + def shutdown(self): + pass + + class Exporter: def __init__(self): tracer_provider = trace.get_tracer_provider() @@ -148,7 +201,7 @@ def setUp(self): "opentelemetry.sdk._configuration.BatchSpanProcessor", Processor ) self.set_provider_patcher = patch( - "opentelemetry.trace.set_tracer_provider" + "opentelemetry.sdk._configuration.set_tracer_provider" ) self.get_provider_mock = self.get_provider_patcher.start() @@ -304,6 +357,61 @@ def test_logging_init_enable_env(self, logging_mock, tracing_mock): self.assertEqual(tracing_mock.call_count, 1) +class TestMetricsInit(TestCase): + def setUp(self): + self.metric_reader_patch = patch( + "opentelemetry.sdk._configuration.PeriodicExportingMetricReader", + DummyMetricReader, + ) + self.provider_patch = patch( + "opentelemetry.sdk._configuration.MeterProvider", + DummyMeterProvider, + ) + self.set_provider_patch = patch( + "opentelemetry.sdk._configuration.set_meter_provider" + ) + + self.metric_reader_mock = self.metric_reader_patch.start() + self.provider_mock = self.provider_patch.start() + self.set_provider_mock = self.set_provider_patch.start() + + def tearDown(self): + self.metric_reader_patch.stop() + self.set_provider_patch.stop() + self.provider_patch.stop() + + def test_metrics_init_empty(self): + _init_metrics({}, "auto-version") + self.assertEqual(self.set_provider_mock.call_count, 1) + provider = self.set_provider_mock.call_args[0][0] + self.assertIsInstance(provider, DummyMeterProvider) + self.assertIsInstance(provider._sdk_config.resource, Resource) + self.assertEqual( + provider._sdk_config.resource.attributes.get( + "telemetry.auto.version" + ), + "auto-version", + ) + + @patch.dict( + environ, + {"OTEL_RESOURCE_ATTRIBUTES": "service.name=otlp-service"}, + ) + def test_metrics_init_exporter(self): + _init_metrics({"otlp": DummyOTLPMetricExporter}) + self.assertEqual(self.set_provider_mock.call_count, 1) + provider = self.set_provider_mock.call_args[0][0] + self.assertIsInstance(provider, DummyMeterProvider) + self.assertIsInstance(provider._sdk_config.resource, Resource) + self.assertEqual( + provider._sdk_config.resource.attributes.get("service.name"), + "otlp-service", + ) + reader = provider._sdk_config.metric_readers[0] + self.assertIsInstance(reader, DummyMetricReader) + self.assertIsInstance(reader.exporter, DummyOTLPMetricExporter) + + class TestExporterNames(TestCase): def test_otlp_exporter_overwrite(self): for exporter in [_EXPORTER_OTLP, _EXPORTER_OTLP_PROTO_GRPC]: @@ -328,8 +436,8 @@ def test_empty_exporters(self): class TestImportExporters(TestCase): def test_console_exporters(self): - trace_exporters, logs_exporters = _import_exporters( - ["console"], ["console"] + trace_exporters, metric_exporterts, logs_exporters = _import_exporters( + ["console"], ["console"], ["console"] ) self.assertEqual( trace_exporters["console"].__class__, ConsoleSpanExporter.__class__ @@ -338,6 +446,6 @@ def test_console_exporters(self): logs_exporters["console"].__class__, ConsoleLogExporter.__class__ ) self.assertEqual( - logs_exporters["console"].__class__, + metric_exporterts["console"].__class__, ConsoleMetricExporter.__class__, ) From 9fbc93baddd27f00ab36b64ba9626dd94b29eb54 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Wed, 8 Jun 2022 21:44:43 +0530 Subject: [PATCH 14/37] bugfix: batch processor doesn't work with uwsgi (#2277) --- .../src/opentelemetry/sdk/_logs/export/__init__.py | 9 +++++++++ .../src/opentelemetry/sdk/trace/export/__init__.py | 9 +++++++++ opentelemetry-sdk/tests/logs/test_export.py | 9 +++------ opentelemetry-sdk/tests/trace/export/test_export.py | 5 ++--- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py index fb1fc828821..826ba3f3d63 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/export/__init__.py @@ -24,6 +24,7 @@ from opentelemetry.context import attach, detach, set_value from opentelemetry.sdk._logs import LogData, LogProcessor, LogRecord +from opentelemetry.util._once import Once from opentelemetry.util._time import _time_ns _logger = logging.getLogger(__name__) @@ -129,6 +130,9 @@ def __init__(self): self.num_log_records = 0 +_BSP_RESET_ONCE = Once() + + class BatchLogProcessor(LogProcessor): """This is an implementation of LogProcessor which creates batches of received logs in the export-friendly LogData representation and @@ -164,6 +168,7 @@ def __init__( os.register_at_fork( after_in_child=self._at_fork_reinit ) # pylint: disable=protected-access + self._pid = os.getpid() def _at_fork_reinit(self): self._condition = threading.Condition(threading.Lock()) @@ -174,6 +179,7 @@ def _at_fork_reinit(self): daemon=True, ) self._worker_thread.start() + self._pid = os.getpid() def worker(self): timeout = self._schedule_delay_millis / 1e3 @@ -293,6 +299,9 @@ def emit(self, log_data: LogData) -> None: """ if self._shutdown: return + if self._pid != os.getpid(): + _BSP_RESET_ONCE.do_once(self._at_fork_reinit) + self._queue.appendleft(log_data) if len(self._queue) >= self._max_export_batch_size: with self._condition: diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py index d40bb4968c0..c574a327f09 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py @@ -36,6 +36,7 @@ OTEL_BSP_SCHEDULE_DELAY, ) from opentelemetry.sdk.trace import ReadableSpan, Span, SpanProcessor +from opentelemetry.util._once import Once from opentelemetry.util._time import _time_ns logger = logging.getLogger(__name__) @@ -119,6 +120,9 @@ def __init__(self): self.num_spans = 0 +_BSP_RESET_ONCE = Once() + + class BatchSpanProcessor(SpanProcessor): """Batch span processor implementation. @@ -203,6 +207,7 @@ def __init__( os.register_at_fork( after_in_child=self._at_fork_reinit ) # pylint: disable=protected-access + self._pid = os.getpid() def on_start( self, span: Span, parent_context: typing.Optional[Context] = None @@ -215,6 +220,9 @@ def on_end(self, span: ReadableSpan) -> None: return if not span.context.trace_flags.sampled: return + if self._pid != os.getpid(): + _BSP_RESET_ONCE.do_once(self._at_fork_reinit) + if len(self.queue) == self.max_queue_size: if not self._spans_dropped: logger.warning("Queue is full, likely spans will be dropped.") @@ -236,6 +244,7 @@ def _at_fork_reinit(self): name="OtelBatchSpanProcessor", target=self.worker, daemon=True ) self.worker_thread.start() + self._pid = os.getpid() def worker(self): timeout = self.schedule_delay_millis / 1e3 diff --git a/opentelemetry-sdk/tests/logs/test_export.py b/opentelemetry-sdk/tests/logs/test_export.py index a1c39d6df2b..d7dbed76a6d 100644 --- a/opentelemetry-sdk/tests/logs/test_export.py +++ b/opentelemetry-sdk/tests/logs/test_export.py @@ -16,7 +16,6 @@ import logging import multiprocessing import os -import sys import time import unittest from concurrent.futures import ThreadPoolExecutor @@ -44,8 +43,6 @@ from opentelemetry.trace import TraceFlags from opentelemetry.trace.span import INVALID_SPAN_CONTEXT -supports_register_at_fork = hasattr(os, "fork") and sys.version_info >= (3, 7) - class TestSimpleLogProcessor(unittest.TestCase): def test_simple_log_processor_default_level(self): @@ -274,9 +271,9 @@ def bulk_log_and_flush(num_logs): finished_logs = exporter.get_finished_logs() self.assertEqual(len(finished_logs), 2415) - @unittest.skipIf( - not supports_register_at_fork, - "needs *nix and minor version 7 or later", + @unittest.skipUnless( + hasattr(os, "fork"), + "needs *nix", ) def test_batch_log_processor_fork(self): # pylint: disable=invalid-name diff --git a/opentelemetry-sdk/tests/trace/export/test_export.py b/opentelemetry-sdk/tests/trace/export/test_export.py index 00ccfe44d38..b1eb98ce2e3 100644 --- a/opentelemetry-sdk/tests/trace/export/test_export.py +++ b/opentelemetry-sdk/tests/trace/export/test_export.py @@ -14,7 +14,6 @@ import multiprocessing import os -import sys import threading import time import unittest @@ -369,8 +368,8 @@ def _check_fork_trace(self, exporter, expected): self.assertIn(span.name, expected) @unittest.skipUnless( - hasattr(os, "fork") and sys.version_info >= (3, 7), - "needs *nix and minor version 7 or later", + hasattr(os, "fork"), + "needs *nix", ) def test_batch_span_processor_fork(self): # pylint: disable=invalid-name From 101db90768fb2008cad6e504ea565a3ee93bb9a8 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 8 Jun 2022 17:54:01 +0100 Subject: [PATCH 15/37] Add missing to_json methods (#2722) * Add missing to_json methods Fixes #2716 * Added resource to_json method * Rename to metrics data * Add to_json to Instrumentation Scope as well Fixes #2716 --- CHANGELOG.md | 2 + .../sdk/metrics/_internal/export/__init__.py | 4 +- .../sdk/metrics/_internal/point.py | 131 +++++--- .../opentelemetry/sdk/resources/__init__.py | 9 + .../opentelemetry/sdk/util/instrumentation.py | 29 +- .../integration_test/test_console_exporter.py | 37 +++ opentelemetry-sdk/tests/metrics/test_point.py | 279 ++++++++++++++---- 7 files changed, 380 insertions(+), 111 deletions(-) create mode 100644 opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py diff --git a/CHANGELOG.md b/CHANGELOG.md index e33019f119e..8209e3c182a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.12.0rc1-0.31b0...HEAD) +- Add missing `to_json` methods + ([#2722](https://github.com/open-telemetry/opentelemetry-python/pull/2722) - Fix type hints for textmap `Getter` and `Setter` ([#2657](https://github.com/open-telemetry/opentelemetry-python/pull/2657)) - Fix LogEmitterProvider.force_flush hanging randomly diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py index 684cb4c5067..8ed5596c81a 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py @@ -105,8 +105,8 @@ def __init__( self, out: IO = stdout, formatter: Callable[ - ["opentelemetry.sdk.metrics.export.Metric"], str - ] = lambda metric: metric.to_json() + ["opentelemetry.sdk.metrics.export.MetricsData"], str + ] = lambda metrics_data: metrics_data.to_json() + linesep, ): self.out = out diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/point.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/point.py index 4ae68629678..b4d813accaf 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/point.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/point.py @@ -15,8 +15,8 @@ # pylint: disable=unused-import from dataclasses import asdict, dataclass -from json import dumps -from typing import Sequence, Union +from json import dumps, loads +from typing import Optional, Sequence, Union # This kind of import is needed to avoid Sphinx errors. import opentelemetry.sdk.metrics._internal @@ -36,6 +36,29 @@ class NumberDataPoint: time_unix_nano: int value: Union[int, float] + def to_json(self, indent=4) -> str: + return dumps(asdict(self), indent=indent) + + +@dataclass(frozen=True) +class HistogramDataPoint: + """Single data point in a timeseries that describes the time-varying scalar + value of a metric. + """ + + attributes: Attributes + start_time_unix_nano: int + time_unix_nano: int + count: int + sum: Union[int, float] + bucket_counts: Sequence[int] + explicit_bounds: Sequence[float] + min: float + max: float + + def to_json(self, indent=4) -> str: + return dumps(asdict(self), indent=indent) + @dataclass(frozen=True) class Sum: @@ -48,15 +71,17 @@ class Sum: ) is_monotonic: bool - def to_json(self) -> str: + def to_json(self, indent=4) -> str: return dumps( { - "data_points": dumps( - [asdict(data_point) for data_point in self.data_points] - ), + "data_points": [ + loads(data_point.to_json(indent=indent)) + for data_point in self.data_points + ], "aggregation_temporality": self.aggregation_temporality, "is_monotonic": self.is_monotonic, - } + }, + indent=indent, ) @@ -68,33 +93,18 @@ class Gauge: data_points: Sequence[NumberDataPoint] - def to_json(self) -> str: + def to_json(self, indent=4) -> str: return dumps( { - "data_points": dumps( - [asdict(data_point) for data_point in self.data_points] - ) - } + "data_points": [ + loads(data_point.to_json(indent=indent)) + for data_point in self.data_points + ], + }, + indent=indent, ) -@dataclass(frozen=True) -class HistogramDataPoint: - """Single data point in a timeseries that describes the time-varying scalar - value of a metric. - """ - - attributes: Attributes - start_time_unix_nano: int - time_unix_nano: int - count: int - sum: Union[int, float] - bucket_counts: Sequence[int] - explicit_bounds: Sequence[float] - min: float - max: float - - @dataclass(frozen=True) class Histogram: """Represents the type of a metric that is calculated by aggregating as a @@ -105,14 +115,16 @@ class Histogram: "opentelemetry.sdk.metrics.export.AggregationTemporality" ) - def to_json(self) -> str: + def to_json(self, indent=4) -> str: return dumps( { - "data_points": dumps( - [asdict(data_point) for data_point in self.data_points] - ), + "data_points": [ + loads(data_point.to_json(indent=indent)) + for data_point in self.data_points + ], "aggregation_temporality": self.aggregation_temporality, - } + }, + indent=indent, ) @@ -126,18 +138,19 @@ class Metric: exported.""" name: str - description: str - unit: str + description: Optional[str] + unit: Optional[str] data: DataT - def to_json(self) -> str: + def to_json(self, indent=4) -> str: return dumps( { "name": self.name, - "description": self.description if self.description else "", - "unit": self.unit if self.unit else "", - "data": self.data.to_json(), - } + "description": self.description or "", + "unit": self.unit or "", + "data": loads(self.data.to_json(indent=indent)), + }, + indent=indent, ) @@ -149,6 +162,19 @@ class ScopeMetrics: metrics: Sequence[Metric] schema_url: str + def to_json(self, indent=4) -> str: + return dumps( + { + "scope": loads(self.scope.to_json(indent=indent)), + "metrics": [ + loads(metric.to_json(indent=indent)) + for metric in self.metrics + ], + "schema_url": self.schema_url, + }, + indent=indent, + ) + @dataclass(frozen=True) class ResourceMetrics: @@ -158,9 +184,32 @@ class ResourceMetrics: scope_metrics: Sequence[ScopeMetrics] schema_url: str + def to_json(self, indent=4) -> str: + return dumps( + { + "resource": loads(self.resource.to_json(indent=indent)), + "scope_metrics": [ + loads(scope_metrics.to_json(indent=indent)) + for scope_metrics in self.scope_metrics + ], + "schema_url": self.schema_url, + }, + indent=indent, + ) + @dataclass(frozen=True) class MetricsData: """An array of ResourceMetrics""" resource_metrics: Sequence[ResourceMetrics] + + def to_json(self, indent=4) -> str: + return dumps( + { + "resource_metrics": [ + loads(resource_metrics.to_json(indent=indent)) + for resource_metrics in self.resource_metrics + ] + } + ) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py index d99f097b389..996a7f28002 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py @@ -242,6 +242,15 @@ def __hash__(self): f"{dumps(self._attributes.copy(), sort_keys=True)}|{self._schema_url}" ) + def to_json(self, indent=4) -> str: + return dumps( + { + "attributes": dict(self._attributes), + "schema_url": self._schema_url, + }, + indent=indent, + ) + _EMPTY_RESOURCE = Resource({}) _DEFAULT_RESOURCE = Resource( diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py b/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py index a489c207a03..55d7c6277ba 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py @@ -11,7 +11,8 @@ # 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. -import typing +from json import dumps +from typing import Optional from deprecated import deprecated @@ -29,8 +30,8 @@ class InstrumentationInfo: def __init__( self, name: str, - version: typing.Optional[str] = None, - schema_url: typing.Optional[str] = None, + version: Optional[str] = None, + schema_url: Optional[str] = None, ): self._name = name self._version = version @@ -59,11 +60,11 @@ def __lt__(self, value): ) @property - def schema_url(self) -> typing.Optional[str]: + def schema_url(self) -> Optional[str]: return self._schema_url @property - def version(self) -> typing.Optional[str]: + def version(self) -> Optional[str]: return self._version @property @@ -84,8 +85,8 @@ class InstrumentationScope: def __init__( self, name: str, - version: typing.Optional[str] = None, - schema_url: typing.Optional[str] = None, + version: Optional[str] = None, + schema_url: Optional[str] = None, ) -> None: self._name = name self._version = version @@ -116,13 +117,23 @@ def __lt__(self, value: object) -> bool: ) @property - def schema_url(self) -> typing.Optional[str]: + def schema_url(self) -> Optional[str]: return self._schema_url @property - def version(self) -> typing.Optional[str]: + def version(self) -> Optional[str]: return self._version @property def name(self) -> str: return self._name + + def to_json(self, indent=4) -> str: + return dumps( + { + "name": self._name, + "version": self._version, + "schema_url": self._schema_url, + }, + indent=indent, + ) diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py b/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py new file mode 100644 index 00000000000..d09ade6a29e --- /dev/null +++ b/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py @@ -0,0 +1,37 @@ +# 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 unittest import TestCase + +from opentelemetry import metrics +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export import ( + ConsoleMetricExporter, + PeriodicExportingMetricReader, +) + + +class TestConsoleExporter(TestCase): + def test_console_exporter(self): + + try: + exporter = ConsoleMetricExporter() + reader = PeriodicExportingMetricReader(exporter) + provider = MeterProvider(metric_readers=[reader]) + metrics.set_meter_provider(provider) + meter = metrics.get_meter(__name__) + counter = meter.create_counter("test") + counter.add(1) + except Exception as error: + self.fail(f"Unexpected exception {error} raised") diff --git a/opentelemetry-sdk/tests/metrics/test_point.py b/opentelemetry-sdk/tests/metrics/test_point.py index ce3e73b7b0b..5d6640fdea6 100644 --- a/opentelemetry-sdk/tests/metrics/test_point.py +++ b/opentelemetry-sdk/tests/metrics/test_point.py @@ -15,85 +15,246 @@ from unittest import TestCase from opentelemetry.sdk.metrics.export import ( + AggregationTemporality, Gauge, Histogram, HistogramDataPoint, Metric, + MetricsData, NumberDataPoint, + ResourceMetrics, + ScopeMetrics, Sum, ) +from opentelemetry.sdk.resources import Resource +from opentelemetry.sdk.util.instrumentation import InstrumentationScope -def _create_metric(data): - return Metric( - name="test-name", - description="test-description", - unit="test-unit", - data=data, - ) +class TestToJson(TestCase): + @classmethod + def setUpClass(cls): + cls.attributes_0 = { + "a": "b", + "b": True, + "c": 1, + "d": 1.1, + "e": ["a", "b"], + "f": [True, False], + "g": [1, 2], + "h": [1.1, 2.2], + } + cls.attributes_0_str = '{"a": "b", "b": true, "c": 1, "d": 1.1, "e": ["a", "b"], "f": [true, false], "g": [1, 2], "h": [1.1, 2.2]}' -class TestDatapointToJSON(TestCase): - def test_sum(self): - self.maxDiff = None - point = _create_metric( - Sum( - data_points=[ - NumberDataPoint( - attributes={"attr-key": "test-val"}, - start_time_unix_nano=10, - time_unix_nano=20, - value=9, - ) - ], - aggregation_temporality=2, - is_monotonic=True, - ) + cls.attributes_1 = { + "i": "a", + "j": False, + "k": 2, + "l": 2.2, + "m": ["b", "a"], + "n": [False, True], + "o": [2, 1], + "p": [2.2, 1.1], + } + cls.attributes_1_str = '{"i": "a", "j": false, "k": 2, "l": 2.2, "m": ["b", "a"], "n": [false, true], "o": [2, 1], "p": [2.2, 1.1]}' + + cls.number_data_point_0 = NumberDataPoint( + attributes=cls.attributes_0, + start_time_unix_nano=1, + time_unix_nano=2, + value=3.3, + ) + cls.number_data_point_0_str = f'{{"attributes": {cls.attributes_0_str}, "start_time_unix_nano": 1, "time_unix_nano": 2, "value": 3.3}}' + + cls.number_data_point_1 = NumberDataPoint( + attributes=cls.attributes_1, + start_time_unix_nano=2, + time_unix_nano=3, + value=4.4, + ) + cls.number_data_point_1_str = f'{{"attributes": {cls.attributes_1_str}, "start_time_unix_nano": 2, "time_unix_nano": 3, "value": 4.4}}' + + cls.histogram_data_point_0 = HistogramDataPoint( + attributes=cls.attributes_0, + start_time_unix_nano=1, + time_unix_nano=2, + count=3, + sum=3.3, + bucket_counts=[1, 1, 1], + explicit_bounds=[0.1, 1.2, 2.3, 3.4], + min=0.2, + max=3.3, + ) + cls.histogram_data_point_0_str = f'{{"attributes": {cls.attributes_0_str}, "start_time_unix_nano": 1, "time_unix_nano": 2, "count": 3, "sum": 3.3, "bucket_counts": [1, 1, 1], "explicit_bounds": [0.1, 1.2, 2.3, 3.4], "min": 0.2, "max": 3.3}}' + + cls.histogram_data_point_1 = HistogramDataPoint( + attributes=cls.attributes_1, + start_time_unix_nano=2, + time_unix_nano=3, + count=4, + sum=4.4, + bucket_counts=[2, 1, 1], + explicit_bounds=[1.2, 2.3, 3.4, 4.5], + min=0.3, + max=4.4, + ) + cls.histogram_data_point_1_str = f'{{"attributes": {cls.attributes_1_str}, "start_time_unix_nano": 2, "time_unix_nano": 3, "count": 4, "sum": 4.4, "bucket_counts": [2, 1, 1], "explicit_bounds": [1.2, 2.3, 3.4, 4.5], "min": 0.3, "max": 4.4}}' + + cls.sum_0 = Sum( + data_points=[cls.number_data_point_0, cls.number_data_point_1], + aggregation_temporality=AggregationTemporality.DELTA, + is_monotonic=False, + ) + cls.sum_0_str = f'{{"data_points": [{cls.number_data_point_0_str}, {cls.number_data_point_1_str}], "aggregation_temporality": 1, "is_monotonic": false}}' + + cls.gauge_0 = Gauge( + data_points=[cls.number_data_point_0, cls.number_data_point_1], + ) + cls.gauge_0_str = f'{{"data_points": [{cls.number_data_point_0_str}, {cls.number_data_point_1_str}]}}' + + cls.histogram_0 = Histogram( + data_points=[ + cls.histogram_data_point_0, + cls.histogram_data_point_1, + ], + aggregation_temporality=AggregationTemporality.DELTA, + ) + cls.histogram_0_str = f'{{"data_points": [{cls.histogram_data_point_0_str}, {cls.histogram_data_point_1_str}], "aggregation_temporality": 1}}' + + cls.metric_0 = Metric( + name="metric_0", + description="description_0", + unit="unit_0", + data=cls.sum_0, ) + cls.metric_0_str = f'{{"name": "metric_0", "description": "description_0", "unit": "unit_0", "data": {cls.sum_0_str}}}' + + cls.metric_1 = Metric( + name="metric_1", description=None, unit="unit_1", data=cls.gauge_0 + ) + cls.metric_1_str = f'{{"name": "metric_1", "description": "", "unit": "unit_1", "data": {cls.gauge_0_str}}}' + + cls.metric_2 = Metric( + name="metric_2", + description="description_2", + unit=None, + data=cls.histogram_0, + ) + cls.metric_2_str = f'{{"name": "metric_2", "description": "description_2", "unit": "", "data": {cls.histogram_0_str}}}' + + cls.scope_metrics_0 = ScopeMetrics( + scope=InstrumentationScope( + name="name_0", + version="version_0", + schema_url="schema_url_0", + ), + metrics=[cls.metric_0, cls.metric_1, cls.metric_2], + schema_url="schema_url_0", + ) + cls.scope_metrics_0_str = f'{{"scope": {{"name": "name_0", "version": "version_0", "schema_url": "schema_url_0"}}, "metrics": [{cls.metric_0_str}, {cls.metric_1_str}, {cls.metric_2_str}], "schema_url": "schema_url_0"}}' + + cls.scope_metrics_1 = ScopeMetrics( + scope=InstrumentationScope( + name="name_1", + version="version_1", + schema_url="schema_url_1", + ), + metrics=[cls.metric_0, cls.metric_1, cls.metric_2], + schema_url="schema_url_1", + ) + cls.scope_metrics_1_str = f'{{"scope": {{"name": "name_1", "version": "version_1", "schema_url": "schema_url_1"}}, "metrics": [{cls.metric_0_str}, {cls.metric_1_str}, {cls.metric_2_str}], "schema_url": "schema_url_1"}}' + + cls.resource_metrics_0 = ResourceMetrics( + resource=Resource( + attributes=cls.attributes_0, schema_url="schema_url_0" + ), + scope_metrics=[cls.scope_metrics_0, cls.scope_metrics_1], + schema_url="schema_url_0", + ) + cls.resource_metrics_0_str = f'{{"resource": {{"attributes": {cls.attributes_0_str}, "schema_url": "schema_url_0"}}, "scope_metrics": [{cls.scope_metrics_0_str}, {cls.scope_metrics_1_str}], "schema_url": "schema_url_0"}}' + + cls.resource_metrics_1 = ResourceMetrics( + resource=Resource( + attributes=cls.attributes_1, schema_url="schema_url_1" + ), + scope_metrics=[cls.scope_metrics_0, cls.scope_metrics_1], + schema_url="schema_url_1", + ) + cls.resource_metrics_1_str = f'{{"resource": {{"attributes": {cls.attributes_1_str}, "schema_url": "schema_url_1"}}, "scope_metrics": [{cls.scope_metrics_0_str}, {cls.scope_metrics_1_str}], "schema_url": "schema_url_1"}}' + + cls.metrics_data_0 = MetricsData( + resource_metrics=[cls.resource_metrics_0, cls.resource_metrics_1] + ) + cls.metrics_data_0_str = f'{{"resource_metrics": [{cls.resource_metrics_0_str}, {cls.resource_metrics_1_str}]}}' + + def test_number_data_point(self): + self.assertEqual( - '{"name": "test-name", "description": "test-description", "unit": "test-unit", "data": "{\\"data_points\\": \\"[{\\\\\\"attributes\\\\\\": {\\\\\\"attr-key\\\\\\": \\\\\\"test-val\\\\\\"}, \\\\\\"start_time_unix_nano\\\\\\": 10, \\\\\\"time_unix_nano\\\\\\": 20, \\\\\\"value\\\\\\": 9}]\\", \\"aggregation_temporality\\": 2, \\"is_monotonic\\": true}"}', - point.to_json(), + self.number_data_point_0.to_json(indent=None), + self.number_data_point_0_str, + ) + self.assertEqual( + self.number_data_point_1.to_json(indent=None), + self.number_data_point_1_str, ) - def test_gauge(self): - point = _create_metric( - Gauge( - data_points=[ - NumberDataPoint( - attributes={"attr-key": "test-val"}, - start_time_unix_nano=10, - time_unix_nano=20, - value=9, - ) - ] - ) + def test_histogram_data_point(self): + + self.assertEqual( + self.histogram_data_point_0.to_json(indent=None), + self.histogram_data_point_0_str, ) self.assertEqual( - '{"name": "test-name", "description": "test-description", "unit": "test-unit", "data": "{\\"data_points\\": \\"[{\\\\\\"attributes\\\\\\": {\\\\\\"attr-key\\\\\\": \\\\\\"test-val\\\\\\"}, \\\\\\"start_time_unix_nano\\\\\\": 10, \\\\\\"time_unix_nano\\\\\\": 20, \\\\\\"value\\\\\\": 9}]\\"}"}', - point.to_json(), + self.histogram_data_point_1.to_json(indent=None), + self.histogram_data_point_1_str, ) + def test_sum(self): + + self.assertEqual(self.sum_0.to_json(indent=None), self.sum_0_str) + + def test_gauge(self): + + self.maxDiff = None + + self.assertEqual(self.gauge_0.to_json(indent=None), self.gauge_0_str) + def test_histogram(self): - point = _create_metric( - Histogram( - data_points=[ - HistogramDataPoint( - attributes={"attr-key": "test-val"}, - start_time_unix_nano=50, - time_unix_nano=60, - count=1, - sum=0.8, - bucket_counts=[0, 0, 1, 0], - explicit_bounds=[0.1, 0.5, 0.9, 1], - min=0.8, - max=0.8, - ) - ], - aggregation_temporality=1, - ) + + self.assertEqual( + self.histogram_0.to_json(indent=None), self.histogram_0_str ) - self.maxDiff = None + + def test_metric(self): + + self.assertEqual(self.metric_0.to_json(indent=None), self.metric_0_str) + + self.assertEqual(self.metric_1.to_json(indent=None), self.metric_1_str) + + self.assertEqual(self.metric_2.to_json(indent=None), self.metric_2_str) + + def test_scope_metrics(self): + + self.assertEqual( + self.scope_metrics_0.to_json(indent=None), self.scope_metrics_0_str + ) + self.assertEqual( + self.scope_metrics_1.to_json(indent=None), self.scope_metrics_1_str + ) + + def test_resource_metrics(self): + + self.assertEqual( + self.resource_metrics_0.to_json(indent=None), + self.resource_metrics_0_str, + ) + self.assertEqual( + self.resource_metrics_1.to_json(indent=None), + self.resource_metrics_1_str, + ) + + def test_metrics_data(self): + self.assertEqual( - '{"name": "test-name", "description": "test-description", "unit": "test-unit", "data": "{\\"data_points\\": \\"[{\\\\\\"attributes\\\\\\": {\\\\\\"attr-key\\\\\\": \\\\\\"test-val\\\\\\"}, \\\\\\"start_time_unix_nano\\\\\\": 50, \\\\\\"time_unix_nano\\\\\\": 60, \\\\\\"count\\\\\\": 1, \\\\\\"sum\\\\\\": 0.8, \\\\\\"bucket_counts\\\\\\": [0, 0, 1, 0], \\\\\\"explicit_bounds\\\\\\": [0.1, 0.5, 0.9, 1], \\\\\\"min\\\\\\": 0.8, \\\\\\"max\\\\\\": 0.8}]\\", \\"aggregation_temporality\\": 1}"}', - point.to_json(), + self.metrics_data_0.to_json(indent=None), self.metrics_data_0_str ) From 953e422a6123fd433fed7285e2562e03b77a2493 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Fri, 10 Jun 2022 03:18:53 +0530 Subject: [PATCH 16/37] Add entrypoint for metrics exporter (#2748) --- CHANGELOG.md | 2 ++ exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg | 2 ++ exporter/opentelemetry-exporter-otlp/setup.cfg | 2 ++ 3 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8209e3c182a..eb7e4d6550f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2731](https://github.com/open-telemetry/opentelemetry-python/pull/2731)) - Configure auto instrumentation to support metrics ([#2705](https://github.com/open-telemetry/opentelemetry-python/pull/2705)) +- Add entrypoint for metrics exporter + ([#2748](https://github.com/open-telemetry/opentelemetry-python/pull/2748)) ## [1.12.0rc1-0.31b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc1-0.31b0) - 2022-05-17 diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg b/exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg index 4206782846f..3c35fef7716 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg @@ -58,5 +58,7 @@ where = src [options.entry_points] opentelemetry_traces_exporter = otlp_proto_grpc = opentelemetry.exporter.otlp.proto.grpc.trace_exporter:OTLPSpanExporter +opentelemetry_metrics_exporter = + otlp_proto_grpc = opentelemetry.exporter.otlp.proto.grpc.metric_exporter:OTLPMetricExporter opentelemetry_logs_exporter = otlp_proto_grpc = opentelemetry.exporter.otlp.proto.grpc._log_exporter:OTLPLogExporter diff --git a/exporter/opentelemetry-exporter-otlp/setup.cfg b/exporter/opentelemetry-exporter-otlp/setup.cfg index 13a7ab92b6b..61573650016 100644 --- a/exporter/opentelemetry-exporter-otlp/setup.cfg +++ b/exporter/opentelemetry-exporter-otlp/setup.cfg @@ -50,5 +50,7 @@ where = src [options.entry_points] opentelemetry_traces_exporter = otlp = opentelemetry.exporter.otlp.proto.grpc.trace_exporter:OTLPSpanExporter +opentelemetry_metrics_exporter = + otlp = opentelemetry.exporter.otlp.proto.grpc.metric_exporter:OTLPMetricExporter opentelemetry_logs_exporter = otlp = opentelemetry.exporter.otlp.proto.grpc._log_exporter:OTLPLogExporter From 6aee8195fdc30fe670c01782cf394da00c5858cc Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 10 Jun 2022 20:19:06 +0100 Subject: [PATCH 17/37] Check data point before yielding (#2745) --- CHANGELOG.md | 2 + .../_internal/_view_instrument_match.py | 4 +- .../metrics/test_view_instrument_match.py | 72 +++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb7e4d6550f..cb8b44adb9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.12.0rc1-0.31b0...HEAD) +- Fix yield of `None`-valued points + ([#2745](https://github.com/open-telemetry/opentelemetry-python/pull/2745)) - Add missing `to_json` methods ([#2722](https://github.com/open-telemetry/opentelemetry-python/pull/2722) - Fix type hints for textmap `Getter` and `Setter` diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py index 026b9702350..4ada52d91b0 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py @@ -130,6 +130,8 @@ def collect( with self._lock: for aggregation in self._attributes_aggregation.values(): - yield aggregation.collect( + data_point = aggregation.collect( aggregation_temporality, collection_start_nanos ) + if data_point is not None: + yield data_point diff --git a/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py b/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py index 839f4ccfa20..d2730086a47 100644 --- a/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py +++ b/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py @@ -212,6 +212,78 @@ def test_collect(self): self.assertEqual(number_data_point.attributes, {"c": "d"}) self.assertEqual(number_data_point.value, 0) + def test_data_point_check(self): + instrument1 = Counter( + "instrument1", + Mock(), + Mock(), + description="description", + unit="unit", + ) + instrument1.instrumentation_scope = self.mock_instrumentation_scope + + view_instrument_match = _ViewInstrumentMatch( + view=View( + instrument_name="instrument1", + name="name", + aggregation=DefaultAggregation(), + ), + instrument=instrument1, + instrument_class_aggregation=MagicMock( + **{ + "__getitem__.return_value": Mock( + **{ + "_create_aggregation.return_value": Mock( + **{ + "collect.side_effect": [ + Mock(), + Mock(), + None, + Mock(), + ] + } + ) + } + ) + } + ), + ) + + view_instrument_match.consume_measurement( + Measurement( + value=0, + instrument=Mock(name="instrument1"), + attributes={"c": "d", "f": "g"}, + ) + ) + view_instrument_match.consume_measurement( + Measurement( + value=0, + instrument=Mock(name="instrument1"), + attributes={"h": "i", "j": "k"}, + ) + ) + view_instrument_match.consume_measurement( + Measurement( + value=0, + instrument=Mock(name="instrument1"), + attributes={"l": "m", "n": "o"}, + ) + ) + view_instrument_match.consume_measurement( + Measurement( + value=0, + instrument=Mock(name="instrument1"), + attributes={"p": "q", "r": "s"}, + ) + ) + + result = view_instrument_match.collect( + AggregationTemporality.CUMULATIVE, 0 + ) + + self.assertEqual(len(list(result)), 3) + def test_setting_aggregation(self): instrument1 = Counter( name="instrument1", From 6e282d27e5a7fa337322dda154fe6eecf64380f0 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Sun, 12 Jun 2022 00:41:46 +0530 Subject: [PATCH 18/37] Allow `set_status` to accept the StatusCode and optional description (#2735) --- CHANGELOG.md | 2 ++ .../src/opentelemetry/trace/span.py | 14 ++++++-- opentelemetry-api/tests/trace/test_globals.py | 2 +- .../src/opentelemetry/sdk/trace/__init__.py | 34 +++++++++++++----- opentelemetry-sdk/tests/trace/test_trace.py | 35 ++++++++++++++++++- 5 files changed, 74 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb8b44adb9c..068fdf7bc5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2728](https://github.com/open-telemetry/opentelemetry-python/pull/2728)) - fix: update entry point object references for metrics ([#2731](https://github.com/open-telemetry/opentelemetry-python/pull/2731)) +- Allow set_status to accept the StatusCode and optional description + ([#2735](https://github.com/open-telemetry/opentelemetry-python/pull/2735)) - Configure auto instrumentation to support metrics ([#2705](https://github.com/open-telemetry/opentelemetry-python/pull/2705)) - Add entrypoint for metrics exporter diff --git a/opentelemetry-api/src/opentelemetry/trace/span.py b/opentelemetry-api/src/opentelemetry/trace/span.py index cb9992557cc..805b2b06b18 100644 --- a/opentelemetry-api/src/opentelemetry/trace/span.py +++ b/opentelemetry-api/src/opentelemetry/trace/span.py @@ -5,7 +5,7 @@ import typing from collections import OrderedDict -from opentelemetry.trace.status import Status +from opentelemetry.trace.status import Status, StatusCode from opentelemetry.util import types # The key MUST begin with a lowercase letter or a digit, @@ -137,7 +137,11 @@ def is_recording(self) -> bool: """ @abc.abstractmethod - def set_status(self, status: Status) -> None: + def set_status( + self, + status: typing.Union[Status, StatusCode], + description: typing.Optional[str] = None, + ) -> None: """Sets the Status of the Span. If used, this will override the default Span status. """ @@ -524,7 +528,11 @@ def add_event( def update_name(self, name: str) -> None: pass - def set_status(self, status: Status) -> None: + def set_status( + self, + status: typing.Union[Status, StatusCode], + description: typing.Optional[str] = None, + ) -> None: pass def record_exception( diff --git a/opentelemetry-api/tests/trace/test_globals.py b/opentelemetry-api/tests/trace/test_globals.py index 0ead559f862..a448437e984 100644 --- a/opentelemetry-api/tests/trace/test_globals.py +++ b/opentelemetry-api/tests/trace/test_globals.py @@ -12,7 +12,7 @@ class TestSpan(trace.NonRecordingSpan): recorded_exception = None recorded_status = Status(status_code=StatusCode.UNSET) - def set_status(self, status): + def set_status(self, status, description=None): self.recorded_status = status def end(self, end_time=None): diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py index f5279673781..7dc65600f4c 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py @@ -889,16 +889,34 @@ def is_recording(self) -> bool: return self._end_time is None @_check_span_ended - def set_status(self, status: trace_api.Status) -> None: + def set_status( + self, + status: typing.Union[Status, StatusCode], + description: typing.Optional[str] = None, + ) -> None: # Ignore future calls if status is already set to OK # Ignore calls to set to StatusCode.UNSET - if ( - self._status - and self._status.status_code is StatusCode.OK - or status.status_code is StatusCode.UNSET - ): - return - self._status = status + if isinstance(status, Status): + if ( + self._status + and self._status.status_code is StatusCode.OK + or status.status_code is StatusCode.UNSET + ): + return + if description is not None: + logger.warning( + "Description %s ignored. Use either `Status` or `(StatusCode, Description)`", + description, + ) + self._status = status + elif isinstance(status, StatusCode): + if ( + self._status + and self._status.status_code is StatusCode.OK + or status is StatusCode.UNSET + ): + return + self._status = Status(status, description) def __exit__( self, diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py index ad165789734..b89734db2f2 100644 --- a/opentelemetry-sdk/tests/trace/test_trace.py +++ b/opentelemetry-sdk/tests/trace/test_trace.py @@ -45,7 +45,7 @@ get_span_with_dropped_attributes_events_links, new_tracer, ) -from opentelemetry.trace import StatusCode +from opentelemetry.trace import Status, StatusCode from opentelemetry.util._time import _time_ns @@ -903,6 +903,39 @@ def test_span_override_start_and_end_time(self): span.end(end_time) self.assertEqual(end_time, span.end_time) + def test_span_set_status(self): + + span1 = self.tracer.start_span("span1") + span1.set_status(Status(status_code=StatusCode.ERROR)) + self.assertEqual(span1.status.status_code, StatusCode.ERROR) + self.assertEqual(span1.status.description, None) + + span2 = self.tracer.start_span("span2") + span2.set_status( + Status(status_code=StatusCode.ERROR, description="desc") + ) + self.assertEqual(span2.status.status_code, StatusCode.ERROR) + self.assertEqual(span2.status.description, "desc") + + span3 = self.tracer.start_span("span3") + span3.set_status(StatusCode.ERROR) + self.assertEqual(span3.status.status_code, StatusCode.ERROR) + self.assertEqual(span3.status.description, None) + + span4 = self.tracer.start_span("span4") + span4.set_status(StatusCode.ERROR, "span4 desc") + self.assertEqual(span4.status.status_code, StatusCode.ERROR) + self.assertEqual(span4.status.description, "span4 desc") + + span5 = self.tracer.start_span("span5") + with self.assertLogs(level=WARNING): + span5.set_status( + Status(status_code=StatusCode.ERROR, description="desc"), + description="ignored", + ) + self.assertEqual(span5.status.status_code, StatusCode.ERROR) + self.assertEqual(span5.status.description, "desc") + def test_ended_span(self): """Events, attributes are not allowed after span is ended""" From e073d4dafaad42506c08d70727a8c9d47663ca23 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Mon, 13 Jun 2022 19:42:23 +0530 Subject: [PATCH 19/37] Add support for OTLP/HTTP log exporter (#2462) --- CHANGELOG.md | 4 +- .../otlp/proto/http/_log_exporter/__init__.py | 167 +++++++++ .../http/_log_exporter/encoder/__init__.py | 102 +++++ .../tests/test_proto_log_exporter.py | 354 ++++++++++++++++++ 4 files changed, 626 insertions(+), 1 deletion(-) create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/encoder/__init__.py create mode 100644 exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 068fdf7bc5c..9d449a57e2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.12.0rc1-0.31b0...HEAD) +- `opentelemetry-exporter-otlp-proto-http` Add support for OTLP/HTTP log exporter + ([#2462](https://github.com/open-telemetry/opentelemetry-python/pull/2462)) - Fix yield of `None`-valued points ([#2745](https://github.com/open-telemetry/opentelemetry-python/pull/2745)) - Add missing `to_json` methods @@ -115,7 +117,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 pages that have moved, see [#2453](https://github.com/open-telemetry/opentelemetry-python/pull/2453), and [#2498](https://github.com/open-telemetry/opentelemetry-python/pull/2498). -- `opentelemetry-exporter-otlp-grpc` update SDK dependency to ~1.9. +- `opentelemetry-exporter-otlp-proto-grpc` update SDK dependency to ~1.9. ([#2442](https://github.com/open-telemetry/opentelemetry-python/pull/2442)) - bugfix(auto-instrumentation): attach OTLPHandler to root logger ([#2450](https://github.com/open-telemetry/opentelemetry-python/pull/2450)) diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py new file mode 100644 index 00000000000..0cca6995675 --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py @@ -0,0 +1,167 @@ +# 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. + +import gzip +import logging +import zlib +from io import BytesIO +from os import environ +from typing import Dict, Optional, Sequence +from time import sleep + +import requests +from backoff import expo + +from opentelemetry.sdk.environment_variables import ( + OTEL_EXPORTER_OTLP_CERTIFICATE, + OTEL_EXPORTER_OTLP_COMPRESSION, + OTEL_EXPORTER_OTLP_ENDPOINT, + OTEL_EXPORTER_OTLP_HEADERS, + OTEL_EXPORTER_OTLP_TIMEOUT, +) +from opentelemetry.sdk._logs.export import ( + LogExporter, + LogExportResult, + LogData, +) +from opentelemetry.exporter.otlp.proto.http import Compression +from opentelemetry.exporter.otlp.proto.http._log_exporter.encoder import ( + _ProtobufEncoder, +) +from opentelemetry.util.re import parse_headers + + +_logger = logging.getLogger(__name__) + + +DEFAULT_COMPRESSION = Compression.NoCompression +DEFAULT_ENDPOINT = "http://localhost:4318/" +DEFAULT_LOGS_EXPORT_PATH = "v1/logs" +DEFAULT_TIMEOUT = 10 # in seconds + + +class OTLPLogExporter(LogExporter): + + _MAX_RETRY_TIMEOUT = 64 + + def __init__( + self, + endpoint: Optional[str] = None, + certificate_file: Optional[str] = None, + headers: Optional[Dict[str, str]] = None, + timeout: Optional[int] = None, + compression: Optional[Compression] = None, + ): + self._endpoint = endpoint or _append_logs_path( + environ.get(OTEL_EXPORTER_OTLP_ENDPOINT, DEFAULT_ENDPOINT) + ) + self._certificate_file = certificate_file or environ.get( + OTEL_EXPORTER_OTLP_CERTIFICATE, True + ) + headers_string = environ.get(OTEL_EXPORTER_OTLP_HEADERS, "") + self._headers = headers or parse_headers(headers_string) + self._timeout = timeout or int( + environ.get(OTEL_EXPORTER_OTLP_TIMEOUT, DEFAULT_TIMEOUT) + ) + self._compression = compression or _compression_from_env() + self._session = requests.Session() + self._session.headers.update(self._headers) + self._session.headers.update( + {"Content-Type": _ProtobufEncoder._CONTENT_TYPE} + ) + if self._compression is not Compression.NoCompression: + self._session.headers.update( + {"Content-Encoding": self._compression.value} + ) + self._shutdown = False + + def _export(self, serialized_data: str): + data = serialized_data + if self._compression == Compression.Gzip: + gzip_data = BytesIO() + with gzip.GzipFile(fileobj=gzip_data, mode="w") as gzip_stream: + gzip_stream.write(serialized_data) + data = gzip_data.getvalue() + elif self._compression == Compression.Deflate: + data = zlib.compress(bytes(serialized_data)) + + return self._session.post( + url=self._endpoint, + data=data, + verify=self._certificate_file, + timeout=self._timeout, + ) + + @staticmethod + def _retryable(resp: requests.Response) -> bool: + if resp.status_code == 408: + return True + if resp.status_code >= 500 and resp.status_code <= 599: + return True + return False + + def export(self, batch: Sequence[LogData]) -> LogExportResult: + # After the call to Shutdown subsequent calls to Export are + # not allowed and should return a Failure result. + if self._shutdown: + _logger.warning("Exporter already shutdown, ignoring batch") + return LogExportResult.FAILURE + + serialized_data = _ProtobufEncoder.serialize(batch) + + for delay in expo(max_value=self._MAX_RETRY_TIMEOUT): + + if delay == self._MAX_RETRY_TIMEOUT: + return LogExportResult.FAILURE + + resp = self._export(serialized_data) + # pylint: disable=no-else-return + if resp.status_code in (200, 202): + return LogExportResult.SUCCESS + elif self._retryable(resp): + _logger.warning( + "Transient error %s encountered while exporting logs batch, retrying in %ss.", + resp.reason, + delay, + ) + sleep(delay) + continue + else: + _logger.error( + "Failed to export logs batch code: %s, reason: %s", + resp.status_code, + resp.text, + ) + return LogExportResult.FAILURE + return LogExportResult.FAILURE + + def shutdown(self): + if self._shutdown: + _logger.warning("Exporter already shutdown, ignoring call") + return + self._session.close() + self._shutdown = True + + +def _compression_from_env() -> Compression: + compression = ( + environ.get(OTEL_EXPORTER_OTLP_COMPRESSION, "none").lower().strip() + ) + return Compression(compression) + + +def _append_logs_path(endpoint: str) -> str: + if endpoint.endswith("/"): + return endpoint + DEFAULT_LOGS_EXPORT_PATH + return endpoint + f"/{DEFAULT_LOGS_EXPORT_PATH}" diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/encoder/__init__.py new file mode 100644 index 00000000000..bf8784aacf8 --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/encoder/__init__.py @@ -0,0 +1,102 @@ +# 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 typing import Sequence, List + +from opentelemetry.proto.collector.logs.v1.logs_service_pb2 import ( + ExportLogsServiceRequest, +) +from opentelemetry.proto.logs.v1.logs_pb2 import ( + ScopeLogs, + ResourceLogs, +) +from opentelemetry.proto.logs.v1.logs_pb2 import LogRecord as PB2LogRecord +from opentelemetry.exporter.otlp.proto.http.trace_exporter.encoder import ( + _encode_instrumentation_scope, + _encode_resource, + _encode_span_id, + _encode_trace_id, + _encode_value, + _encode_attributes, +) + + +from opentelemetry.sdk._logs.export import LogData + + +class _ProtobufEncoder: + _CONTENT_TYPE = "application/x-protobuf" + + @classmethod + def serialize(cls, batch: Sequence[LogData]) -> str: + return cls.encode(batch).SerializeToString() + + @staticmethod + def encode(batch: Sequence[LogData]) -> ExportLogsServiceRequest: + return ExportLogsServiceRequest( + resource_logs=_encode_resource_logs(batch) + ) + + +def _encode_log(log_data: LogData) -> PB2LogRecord: + kwargs = {} + kwargs["time_unix_nano"] = log_data.log_record.timestamp + kwargs["span_id"] = _encode_span_id(log_data.log_record.span_id) + kwargs["trace_id"] = _encode_trace_id(log_data.log_record.trace_id) + kwargs["flags"] = int(log_data.log_record.trace_flags) + kwargs["body"] = _encode_value(log_data.log_record.body) + kwargs["severity_text"] = log_data.log_record.severity_text + kwargs["attributes"] = _encode_attributes(log_data.log_record.attributes) + kwargs["severity_number"] = log_data.log_record.severity_number.value + + return PB2LogRecord(**kwargs) + + +def _encode_resource_logs(batch: Sequence[LogData]) -> List[ResourceLogs]: + + sdk_resource_logs = {} + + for sdk_log in batch: + sdk_resource = sdk_log.log_record.resource + sdk_instrumentation = sdk_log.instrumentation_scope or None + pb2_log = _encode_log(sdk_log) + + if sdk_resource not in sdk_resource_logs.keys(): + sdk_resource_logs[sdk_resource] = {sdk_instrumentation: [pb2_log]} + elif sdk_instrumentation not in sdk_resource_logs[sdk_resource].keys(): + sdk_resource_logs[sdk_resource][sdk_instrumentation] = [pb2_log] + else: + sdk_resource_logs[sdk_resource][sdk_instrumentation].append( + pb2_log + ) + + pb2_resource_logs = [] + + for sdk_resource, sdk_instrumentations in sdk_resource_logs.items(): + scope_logs = [] + for sdk_instrumentation, pb2_logs in sdk_instrumentations.items(): + scope_logs.append( + ScopeLogs( + scope=(_encode_instrumentation_scope(sdk_instrumentation)), + log_records=pb2_logs, + ) + ) + pb2_resource_logs.append( + ResourceLogs( + resource=_encode_resource(sdk_resource), + scope_logs=scope_logs, + ) + ) + + return pb2_resource_logs diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py new file mode 100644 index 00000000000..13b20190da5 --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py @@ -0,0 +1,354 @@ +# 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. + +# pylint: disable=protected-access + +import unittest +from typing import List, Tuple +from unittest.mock import patch + +from opentelemetry.exporter.otlp.proto.http import Compression +from opentelemetry.exporter.otlp.proto.http._log_exporter import ( + DEFAULT_COMPRESSION, + DEFAULT_ENDPOINT, + DEFAULT_LOGS_EXPORT_PATH, + DEFAULT_TIMEOUT, + OTLPLogExporter, +) +from opentelemetry.exporter.otlp.proto.http._log_exporter.encoder import ( + _encode_attributes, + _encode_span_id, + _encode_trace_id, + _encode_value, + _ProtobufEncoder, +) +from opentelemetry.proto.collector.logs.v1.logs_service_pb2 import ( + ExportLogsServiceRequest, +) +from opentelemetry.proto.common.v1.common_pb2 import AnyValue as PB2AnyValue +from opentelemetry.proto.common.v1.common_pb2 import ( + InstrumentationScope as PB2InstrumentationScope, +) +from opentelemetry.proto.common.v1.common_pb2 import KeyValue as PB2KeyValue +from opentelemetry.proto.logs.v1.logs_pb2 import LogRecord as PB2LogRecord +from opentelemetry.proto.logs.v1.logs_pb2 import ( + ResourceLogs as PB2ResourceLogs, +) +from opentelemetry.proto.logs.v1.logs_pb2 import ScopeLogs as PB2ScopeLogs +from opentelemetry.proto.resource.v1.resource_pb2 import ( + Resource as PB2Resource, +) +from opentelemetry.sdk._logs import LogData +from opentelemetry.sdk._logs import LogRecord as SDKLogRecord +from opentelemetry.sdk._logs.severity import SeverityNumber +from opentelemetry.sdk.environment_variables import ( + OTEL_EXPORTER_OTLP_CERTIFICATE, + OTEL_EXPORTER_OTLP_COMPRESSION, + OTEL_EXPORTER_OTLP_ENDPOINT, + OTEL_EXPORTER_OTLP_HEADERS, + OTEL_EXPORTER_OTLP_TIMEOUT, +) +from opentelemetry.sdk.resources import Resource as SDKResource +from opentelemetry.sdk.util.instrumentation import InstrumentationScope +from opentelemetry.trace import TraceFlags + +ENV_ENDPOINT = "http://localhost.env:8080/" +ENV_CERTIFICATE = "/etc/base.crt" +ENV_HEADERS = "envHeader1=val1,envHeader2=val2" +ENV_TIMEOUT = "30" + + +class TestOTLPHTTPLogExporter(unittest.TestCase): + def test_constructor_default(self): + + exporter = OTLPLogExporter() + + self.assertEqual( + exporter._endpoint, DEFAULT_ENDPOINT + DEFAULT_LOGS_EXPORT_PATH + ) + self.assertEqual(exporter._certificate_file, True) + self.assertEqual(exporter._timeout, DEFAULT_TIMEOUT) + self.assertIs(exporter._compression, DEFAULT_COMPRESSION) + self.assertEqual(exporter._headers, {}) + + @patch.dict( + "os.environ", + { + OTEL_EXPORTER_OTLP_CERTIFICATE: ENV_CERTIFICATE, + OTEL_EXPORTER_OTLP_COMPRESSION: Compression.Gzip.value, + OTEL_EXPORTER_OTLP_ENDPOINT: ENV_ENDPOINT, + OTEL_EXPORTER_OTLP_HEADERS: ENV_HEADERS, + OTEL_EXPORTER_OTLP_TIMEOUT: ENV_TIMEOUT, + }, + ) + def test_exporter_constructor_take_priority(self): + exporter = OTLPLogExporter( + endpoint="endpoint.local:69/logs", + certificate_file="/hello.crt", + headers={"testHeader1": "value1", "testHeader2": "value2"}, + timeout=70, + compression=Compression.NoCompression, + ) + + self.assertEqual(exporter._endpoint, "endpoint.local:69/logs") + self.assertEqual(exporter._certificate_file, "/hello.crt") + self.assertEqual(exporter._timeout, 70) + self.assertIs(exporter._compression, Compression.NoCompression) + self.assertEqual( + exporter._headers, + {"testHeader1": "value1", "testHeader2": "value2"}, + ) + + @patch.dict( + "os.environ", + { + OTEL_EXPORTER_OTLP_CERTIFICATE: ENV_CERTIFICATE, + OTEL_EXPORTER_OTLP_COMPRESSION: Compression.Gzip.value, + OTEL_EXPORTER_OTLP_ENDPOINT: ENV_ENDPOINT, + OTEL_EXPORTER_OTLP_HEADERS: ENV_HEADERS, + OTEL_EXPORTER_OTLP_TIMEOUT: ENV_TIMEOUT, + }, + ) + def test_exporter_env(self): + + exporter = OTLPLogExporter() + + self.assertEqual( + exporter._endpoint, ENV_ENDPOINT + DEFAULT_LOGS_EXPORT_PATH + ) + self.assertEqual(exporter._certificate_file, ENV_CERTIFICATE) + self.assertEqual(exporter._timeout, int(ENV_TIMEOUT)) + self.assertIs(exporter._compression, Compression.Gzip) + self.assertEqual( + exporter._headers, {"envheader1": "val1", "envheader2": "val2"} + ) + + def test_encode(self): + sdk_logs, expected_encoding = self.get_test_logs() + self.assertEqual( + _ProtobufEncoder().encode(sdk_logs), expected_encoding + ) + + def test_serialize(self): + sdk_logs, expected_encoding = self.get_test_logs() + self.assertEqual( + _ProtobufEncoder().serialize(sdk_logs), + expected_encoding.SerializeToString(), + ) + + def test_content_type(self): + self.assertEqual( + _ProtobufEncoder._CONTENT_TYPE, "application/x-protobuf" + ) + + @staticmethod + def _get_sdk_log_data() -> List[LogData]: + log1 = LogData( + log_record=SDKLogRecord( + timestamp=1644650195189786880, + trace_id=89564621134313219400156819398935297684, + span_id=1312458408527513268, + trace_flags=TraceFlags(0x01), + severity_text="WARN", + severity_number=SeverityNumber.WARN, + body="Do not go gentle into that good night. Rage, rage against the dying of the light", + resource=SDKResource({"first_resource": "value"}), + attributes={"a": 1, "b": "c"}, + ), + instrumentation_scope=InstrumentationScope( + "first_name", "first_version" + ), + ) + + log2 = LogData( + log_record=SDKLogRecord( + timestamp=1644650249738562048, + trace_id=0, + span_id=0, + trace_flags=TraceFlags.DEFAULT, + severity_text="WARN", + severity_number=SeverityNumber.WARN, + body="Cooper, this is no time for caution!", + resource=SDKResource({"second_resource": "CASE"}), + attributes={}, + ), + instrumentation_scope=InstrumentationScope( + "second_name", "second_version" + ), + ) + + log3 = LogData( + log_record=SDKLogRecord( + timestamp=1644650427658989056, + trace_id=271615924622795969659406376515024083555, + span_id=4242561578944770265, + trace_flags=TraceFlags(0x01), + severity_text="DEBUG", + severity_number=SeverityNumber.DEBUG, + body="To our galaxy", + resource=SDKResource({"second_resource": "CASE"}), + attributes={"a": 1, "b": "c"}, + ), + instrumentation_scope=None, + ) + + log4 = LogData( + log_record=SDKLogRecord( + timestamp=1644650584292683008, + trace_id=212592107417388365804938480559624925555, + span_id=6077757853989569223, + trace_flags=TraceFlags(0x01), + severity_text="INFO", + severity_number=SeverityNumber.INFO, + body="Love is the one thing that transcends time and space", + resource=SDKResource({"first_resource": "value"}), + attributes={"filename": "model.py", "func_name": "run_method"}, + ), + instrumentation_scope=InstrumentationScope( + "another_name", "another_version" + ), + ) + + return [log1, log2, log3, log4] + + def get_test_logs( + self, + ) -> Tuple[List[SDKLogRecord], ExportLogsServiceRequest]: + sdk_logs = self._get_sdk_log_data() + + pb2_service_request = ExportLogsServiceRequest( + resource_logs=[ + PB2ResourceLogs( + resource=PB2Resource( + attributes=[ + PB2KeyValue( + key="first_resource", + value=PB2AnyValue(string_value="value"), + ) + ] + ), + scope_logs=[ + PB2ScopeLogs( + scope=PB2InstrumentationScope( + name="first_name", version="first_version" + ), + log_records=[ + PB2LogRecord( + time_unix_nano=1644650195189786880, + trace_id=_encode_trace_id( + 89564621134313219400156819398935297684 + ), + span_id=_encode_span_id( + 1312458408527513268 + ), + flags=int(TraceFlags(0x01)), + severity_text="WARN", + severity_number=SeverityNumber.WARN.value, + body=_encode_value( + "Do not go gentle into that good night. Rage, rage against the dying of the light" + ), + attributes=_encode_attributes( + {"a": 1, "b": "c"} + ), + ) + ], + ), + PB2ScopeLogs( + scope=PB2InstrumentationScope( + name="another_name", + version="another_version", + ), + log_records=[ + PB2LogRecord( + time_unix_nano=1644650584292683008, + trace_id=_encode_trace_id( + 212592107417388365804938480559624925555 + ), + span_id=_encode_span_id( + 6077757853989569223 + ), + flags=int(TraceFlags(0x01)), + severity_text="INFO", + severity_number=SeverityNumber.INFO.value, + body=_encode_value( + "Love is the one thing that transcends time and space" + ), + attributes=_encode_attributes( + { + "filename": "model.py", + "func_name": "run_method", + } + ), + ) + ], + ), + ], + ), + PB2ResourceLogs( + resource=PB2Resource( + attributes=[ + PB2KeyValue( + key="second_resource", + value=PB2AnyValue(string_value="CASE"), + ) + ] + ), + scope_logs=[ + PB2ScopeLogs( + scope=PB2InstrumentationScope( + name="second_name", + version="second_version", + ), + log_records=[ + PB2LogRecord( + time_unix_nano=1644650249738562048, + trace_id=_encode_trace_id(0), + span_id=_encode_span_id(0), + flags=int(TraceFlags.DEFAULT), + severity_text="WARN", + severity_number=SeverityNumber.WARN.value, + body=_encode_value( + "Cooper, this is no time for caution!" + ), + attributes={}, + ), + ], + ), + PB2ScopeLogs( + scope=PB2InstrumentationScope(), + log_records=[ + PB2LogRecord( + time_unix_nano=1644650427658989056, + trace_id=_encode_trace_id( + 271615924622795969659406376515024083555 + ), + span_id=_encode_span_id( + 4242561578944770265 + ), + flags=int(TraceFlags(0x01)), + severity_text="DEBUG", + severity_number=SeverityNumber.DEBUG.value, + body=_encode_value("To our galaxy"), + attributes=_encode_attributes( + {"a": 1, "b": "c"} + ), + ), + ], + ), + ], + ), + ] + ) + + return sdk_logs, pb2_service_request From ca7a3fab278e8c8222b3a46d6e2e52af9dd5462f Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 14 Jun 2022 02:12:02 +0100 Subject: [PATCH 20/37] Add min max fields to Histogram (#2759) --- CHANGELOG.md | 2 ++ .../otlp/proto/grpc/metric_exporter/__init__.py | 2 ++ .../tests/metrics/test_otlp_metrics_exporter.py | 10 ++++++++++ 3 files changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d449a57e2c..cfc30d5886a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.12.0rc1-0.31b0...HEAD) +- Add min/max fields to Histogram + ([#2759](https://github.com/open-telemetry/opentelemetry-python/pull/2759)) - `opentelemetry-exporter-otlp-proto-http` Add support for OTLP/HTTP log exporter ([#2462](https://github.com/open-telemetry/opentelemetry-python/pull/2462)) - Fix yield of `None`-valued points diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py index 83846da81fc..c5f4acad06c 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py @@ -146,6 +146,8 @@ def _translate_data( sum=data_point.sum, bucket_counts=data_point.bucket_counts, explicit_bounds=data_point.explicit_bounds, + max=data_point.max, + min=data_point.min, ) pb2_metric.histogram.aggregation_temporality = ( metric.data.aggregation_temporality diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/metrics/test_otlp_metrics_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/metrics/test_otlp_metrics_exporter.py index 0d4418030ba..8936272bef0 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/metrics/test_otlp_metrics_exporter.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/metrics/test_otlp_metrics_exporter.py @@ -720,6 +720,8 @@ def test_translate_histogram(self): explicit_bounds=[10.0, 20.0], exemplars=[], flags=pb2.DataPointFlags.FLAG_NONE, + max=18.0, + min=8.0, ) ], aggregation_temporality=AggregationTemporality.DELTA, @@ -782,6 +784,8 @@ def test_translate_multiple_scope_histogram(self): explicit_bounds=[10.0, 20.0], exemplars=[], flags=pb2.DataPointFlags.FLAG_NONE, + max=18.0, + min=8.0, ) ], aggregation_temporality=AggregationTemporality.DELTA, @@ -816,6 +820,8 @@ def test_translate_multiple_scope_histogram(self): explicit_bounds=[10.0, 20.0], exemplars=[], flags=pb2.DataPointFlags.FLAG_NONE, + max=18.0, + min=8.0, ) ], aggregation_temporality=AggregationTemporality.DELTA, @@ -857,6 +863,8 @@ def test_translate_multiple_scope_histogram(self): explicit_bounds=[10.0, 20.0], exemplars=[], flags=pb2.DataPointFlags.FLAG_NONE, + max=18.0, + min=8.0, ) ], aggregation_temporality=AggregationTemporality.DELTA, @@ -898,6 +906,8 @@ def test_translate_multiple_scope_histogram(self): explicit_bounds=[10.0, 20.0], exemplars=[], flags=pb2.DataPointFlags.FLAG_NONE, + max=18.0, + min=8.0, ) ], aggregation_temporality=AggregationTemporality.DELTA, From b03b5ca6912e355ef81ef81ed340a3e8d014305e Mon Sep 17 00:00:00 2001 From: Brian Saville Date: Wed, 15 Jun 2022 16:15:56 -0600 Subject: [PATCH 21/37] Fix #2348, add handling for non recording span in jaeger propagator (#2762) * Fix #2348, add handling for non recording span in jaeger exporter * Fix pylint errors * Changes for black --- CHANGELOG.md | 2 ++ .../src/opentelemetry/propagators/jaeger/__init__.py | 5 ++++- .../tests/test_jaeger_propagator.py | 10 ++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfc30d5886a..6f387f22f9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2705](https://github.com/open-telemetry/opentelemetry-python/pull/2705)) - Add entrypoint for metrics exporter ([#2748](https://github.com/open-telemetry/opentelemetry-python/pull/2748)) +- Fix Jaeger propagator usage with NonRecordingSpan + ([#2762](https://github.com/open-telemetry/opentelemetry-python/pull/2762)) ## [1.12.0rc1-0.31b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc1-0.31b0) - 2022-05-17 diff --git a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py index 9589f97619a..201d8bf3d3d 100644 --- a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py +++ b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/__init__.py @@ -81,7 +81,10 @@ def inject( if span_context == trace.INVALID_SPAN_CONTEXT: return - span_parent_id = span.parent.span_id if span.parent else 0 + # Non-recording spans do not have a parent + span_parent_id = ( + span.parent.span_id if span.is_recording() and span.parent else 0 + ) trace_flags = span_context.trace_flags if trace_flags.sampled: trace_flags |= self.DEBUG_FLAG diff --git a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py index f01e0e53da8..81187389a16 100644 --- a/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py +++ b/propagator/opentelemetry-propagator-jaeger/tests/test_jaeger_propagator.py @@ -230,3 +230,13 @@ def test_extract_invalid_uber_trace_id_header_to_implicit_ctx(self): ctx = FORMAT.extract(carrier) self.assertDictEqual(Context(), ctx) + + def test_non_recording_span_does_not_crash(self): + """Make sure propagator does not crash when working with NonRecordingSpan""" + mock_setter = Mock() + span = trace_api.NonRecordingSpan(trace_api.SpanContext(1, 1, True)) + with trace_api.use_span(span, end_on_exit=True): + try: + FORMAT.inject({}, setter=mock_setter) + except Exception as exc: # pylint: disable=broad-except + self.fail(f"Injecting failed for NonRecordingSpan with {exc}") From 537e235c1be88edfc86aebc1d47d1c8c67ced656 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 16 Jun 2022 22:36:29 +0530 Subject: [PATCH 22/37] TestBase and SpanTestBase are redundant (#2757) --- .../src/opentelemetry/test/asgitestutil.py | 4 +-- .../src/opentelemetry/test/spantestutil.py | 28 +------------------ .../src/opentelemetry/test/test_base.py | 13 +++++---- .../src/opentelemetry/test/wsgitestutil.py | 4 +-- 4 files changed, 13 insertions(+), 36 deletions(-) diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/asgitestutil.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/asgitestutil.py index 7d23039bf30..05be4e02148 100644 --- a/tests/opentelemetry-test-utils/src/opentelemetry/test/asgitestutil.py +++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/asgitestutil.py @@ -16,7 +16,7 @@ from asgiref.testing import ApplicationCommunicator -from opentelemetry.test.spantestutil import SpanTestBase +from opentelemetry.test.test_base import TestBase def setup_testing_defaults(scope): @@ -35,7 +35,7 @@ def setup_testing_defaults(scope): ) -class AsgiTestBase(SpanTestBase): +class AsgiTestBase(TestBase): def setUp(self): super().setUp() diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/spantestutil.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/spantestutil.py index ea83b90b8d4..912de9ee031 100644 --- a/tests/opentelemetry-test-utils/src/opentelemetry/test/spantestutil.py +++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/spantestutil.py @@ -12,18 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import unittest from functools import partial -from importlib import reload from opentelemetry import trace as trace_api from opentelemetry.sdk import trace as trace_sdk -from opentelemetry.sdk.trace import Resource, TracerProvider, export -from opentelemetry.sdk.trace.export.in_memory_span_exporter import ( - InMemorySpanExporter, -) - -_MEMORY_EXPORTER = None +from opentelemetry.sdk.trace import Resource def new_tracer(span_limits=None, resource=None) -> trace_api.Tracer: @@ -33,25 +26,6 @@ def new_tracer(span_limits=None, resource=None) -> trace_api.Tracer: return provider_factory(span_limits=span_limits).get_tracer(__name__) -class SpanTestBase(unittest.TestCase): - @classmethod - def setUpClass(cls): - global _MEMORY_EXPORTER # pylint:disable=global-statement - trace_api.set_tracer_provider(TracerProvider()) - tracer_provider = trace_api.get_tracer_provider() - _MEMORY_EXPORTER = InMemorySpanExporter() - span_processor = export.SimpleSpanProcessor(_MEMORY_EXPORTER) - tracer_provider.add_span_processor(span_processor) - - @classmethod - def tearDownClass(cls): - reload(trace_api) - - def setUp(self): - self.memory_exporter = _MEMORY_EXPORTER - self.memory_exporter.clear() - - def get_span_with_dropped_attributes_events_links(): attributes = {} for index in range(130): diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/test_base.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/test_base.py index 98e820438d3..b0a9f08fd5c 100644 --- a/tests/opentelemetry-test-utils/src/opentelemetry/test/test_base.py +++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/test_base.py @@ -43,11 +43,6 @@ def setUpClass(cls): reset_trace_globals() trace_api.set_tracer_provider(cls.tracer_provider) - result = cls.create_meter_provider() - cls.meter_provider, cls.memory_metrics_reader = result - reset_metrics_globals() - metrics_api.set_meter_provider(cls.meter_provider) - @classmethod def tearDownClass(cls): # This is done because set_tracer_provider cannot override the @@ -56,6 +51,14 @@ def tearDownClass(cls): def setUp(self): self.memory_exporter.clear() + # This is done because set_meter_provider cannot override the + # current meter provider. + reset_metrics_globals() + ( + self.meter_provider, + self.memory_metrics_reader, + ) = self.create_meter_provider() + metrics_api.set_meter_provider(self.meter_provider) def get_finished_spans(self): return FinishedTestSpans( diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/wsgitestutil.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/wsgitestutil.py index aa2379a2b72..28a4c2698e6 100644 --- a/tests/opentelemetry-test-utils/src/opentelemetry/test/wsgitestutil.py +++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/wsgitestutil.py @@ -16,10 +16,10 @@ import wsgiref.util as wsgiref_util from opentelemetry import trace -from opentelemetry.test.spantestutil import SpanTestBase +from opentelemetry.test.test_base import TestBase -class WsgiTestBase(SpanTestBase): +class WsgiTestBase(TestBase): def setUp(self): super().setUp() From d4d7c67663cc22615748d632e1c8c5799e8eacae Mon Sep 17 00:00:00 2001 From: Carol Abadeer <60774943+carolabadeer@users.noreply.github.com> Date: Thu, 16 Jun 2022 14:20:47 -0700 Subject: [PATCH 23/37] Create Suppress HTTP Instrumentation key in opentelemetry context (#2729) --- CHANGELOG.md | 3 +++ opentelemetry-api/src/opentelemetry/context/__init__.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f387f22f9b..40b62a40d03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2726](https://github.com/open-telemetry/opentelemetry-python/pull/2726)) - fix: frozenset object has no attribute items ([#2727](https://github.com/open-telemetry/opentelemetry-python/pull/2727)) +- fix: create suppress HTTP instrumentation key in opentelemetry context + ([#2729](https://github.com/open-telemetry/opentelemetry-python/pull/2729)) - Support logs SDK auto instrumentation enable/disable with env ([#2728](https://github.com/open-telemetry/opentelemetry-python/pull/2728)) - fix: update entry point object references for metrics @@ -40,6 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix Jaeger propagator usage with NonRecordingSpan ([#2762](https://github.com/open-telemetry/opentelemetry-python/pull/2762)) + ## [1.12.0rc1-0.31b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc1-0.31b0) - 2022-05-17 - Fix LoggingHandler to handle LogRecord with exc_info=False diff --git a/opentelemetry-api/src/opentelemetry/context/__init__.py b/opentelemetry-api/src/opentelemetry/context/__init__.py index 7f56cdb216b..388dc0fba9b 100644 --- a/opentelemetry-api/src/opentelemetry/context/__init__.py +++ b/opentelemetry-api/src/opentelemetry/context/__init__.py @@ -163,3 +163,6 @@ def detach(token: object) -> None: # Once the decision around how to suppress instrumentation is made in the # spec, this key should be moved accordingly. _SUPPRESS_INSTRUMENTATION_KEY = create_key("suppress_instrumentation") +_SUPPRESS_HTTP_INSTRUMENTATION_KEY = create_key( + "suppress_http_instrumentation" +) From aa367302989978a1ae04badee7e3948502b96616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 21 Jun 2022 12:45:59 +0200 Subject: [PATCH 24/37] Python 3.11: Enhanced error locations in tracebacks (#2771) * Python 3.11: Enhanced error locations in tracebacks Expect ^^^^^^^^^ in tracebacks when testing them * Update opentelemetry-sdk/tests/trace/test_trace.py Co-authored-by: Srikanth Chekuri --- opentelemetry-sdk/tests/trace/test_trace.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py index b89734db2f2..890ae3e1e2b 100644 --- a/opentelemetry-sdk/tests/trace/test_trace.py +++ b/opentelemetry-sdk/tests/trace/test_trace.py @@ -15,6 +15,7 @@ # pylint: disable=too-many-lines import shutil import subprocess +import sys import unittest from importlib import reload from logging import ERROR, WARNING @@ -1186,6 +1187,13 @@ def test_record_exception_context_manager(self): stacktrace = """in test_record_exception_context_manager raise RuntimeError("example error") RuntimeError: example error""" + if sys.version_info >= (3, 11): + # https://docs.python.org/3.11/whatsnew/3.11.html#enhanced-error-locations-in-tracebacks + tracelines = stacktrace.splitlines() + tracelines.insert( + -1, " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" + ) + stacktrace = "\n".join(tracelines) self.assertIn(stacktrace, event.attributes["exception.stacktrace"]) try: From 4d615015f1d5d636f211ce1f73e26e963ae60f48 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Fri, 24 Jun 2022 03:00:36 -0700 Subject: [PATCH 25/37] Update setup.cfg (#2777) --- exporter/opentelemetry-exporter-jaeger-proto-grpc/setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporter/opentelemetry-exporter-jaeger-proto-grpc/setup.cfg b/exporter/opentelemetry-exporter-jaeger-proto-grpc/setup.cfg index 2bc9200fc30..36a5d2b27e3 100644 --- a/exporter/opentelemetry-exporter-jaeger-proto-grpc/setup.cfg +++ b/exporter/opentelemetry-exporter-jaeger-proto-grpc/setup.cfg @@ -42,7 +42,7 @@ package_dir= packages=find_namespace: install_requires = grpcio >= 1.0.0, < 2.0.0 - googleapis-common-protos ~= 1.52 + googleapis-common-protos ~= 1.52, < 1.56.3 opentelemetry-api ~= 1.3 opentelemetry-sdk ~= 1.11 From 71d2ab47661c2175d77a87b523e773d5fa10b9cb Mon Sep 17 00:00:00 2001 From: kasium <15907922+kasium@users.noreply.github.com> Date: Fri, 24 Jun 2022 12:42:07 +0200 Subject: [PATCH 26/37] Change log of detach error to exception (#2774) Co-authored-by: Diego Hurtado --- opentelemetry-api/src/opentelemetry/context/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/context/__init__.py b/opentelemetry-api/src/opentelemetry/context/__init__.py index 388dc0fba9b..97ffcf8f728 100644 --- a/opentelemetry-api/src/opentelemetry/context/__init__.py +++ b/opentelemetry-api/src/opentelemetry/context/__init__.py @@ -156,7 +156,7 @@ def detach(token: object) -> None: try: _RUNTIME_CONTEXT.detach(token) # type: ignore except Exception: # pylint: disable=broad-except - logger.error("Failed to detach context") + logger.exception("Failed to detach context") # FIXME This is a temporary location for the suppress instrumentation key. From 25771ecdac685a5bf7ada1da21092d2061dbfc02 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Fri, 24 Jun 2022 17:46:00 +0200 Subject: [PATCH 27/37] Use always empty for None schema url value (#2764) * Use always None for empty schema url value Fixes #2750 * Revert "Use always None for empty schema url value" This reverts commit 8ef29dbd665c5c3ed812266452cb510081f50f16. * Use empty string as default value for schema_url --- .../src/opentelemetry/sdk/util/instrumentation.py | 4 ++++ opentelemetry-sdk/tests/trace/test_trace.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py b/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py index 55d7c6277ba..085d3fd874f 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py @@ -35,6 +35,8 @@ def __init__( ): self._name = name self._version = version + if schema_url is None: + schema_url = "" self._schema_url = schema_url def __repr__(self): @@ -90,6 +92,8 @@ def __init__( ) -> None: self._name = name self._version = version + if schema_url is None: + schema_url = "" self._schema_url = schema_url def __repr__(self) -> str: diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py index 890ae3e1e2b..24d7b6fa3d7 100644 --- a/opentelemetry-sdk/tests/trace/test_trace.py +++ b/opentelemetry-sdk/tests/trace/test_trace.py @@ -271,7 +271,7 @@ def test_invalid_instrumentation_info(self): ) span1 = tracer1.start_span("foo") self.assertTrue(span1.is_recording()) - self.assertEqual(tracer1.instrumentation_info.schema_url, None) + self.assertEqual(tracer1.instrumentation_info.schema_url, "") self.assertEqual(tracer1.instrumentation_info.version, "") self.assertEqual(tracer1.instrumentation_info.name, "") @@ -280,7 +280,7 @@ def test_invalid_instrumentation_info(self): ) span2 = tracer2.start_span("bar") self.assertTrue(span2.is_recording()) - self.assertEqual(tracer2.instrumentation_info.schema_url, None) + self.assertEqual(tracer2.instrumentation_info.schema_url, "") self.assertEqual(tracer2.instrumentation_info.version, "") self.assertEqual(tracer2.instrumentation_info.name, "") From ef9847f5effb81368ce4030002ca42cb27504be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Neum=C3=BCller?= Date: Wed, 29 Jun 2022 11:50:57 +0200 Subject: [PATCH 28/37] Propagate API module & propagators package missing from API docs (#2785) * Propagate API module missing from docs. * Add CHANGELOG. * Sphinx complains about more missing classes. * More fixes, understood why Sphinx didn't find some link targets. * Update CHANGELOG. --- CHANGELOG.md | 3 +++ docs/api/index.rst | 2 ++ docs/api/propagate.rst | 7 +++++++ docs/api/propagators.composite.rst | 7 +++++++ docs/api/propagators.rst | 10 ++++++++++ docs/api/propagators.textmap.rst | 7 +++++++ docs/conf.py | 20 +++----------------- 7 files changed, 39 insertions(+), 17 deletions(-) create mode 100644 docs/api/propagate.rst create mode 100644 docs/api/propagators.composite.rst create mode 100644 docs/api/propagators.rst create mode 100644 docs/api/propagators.textmap.rst diff --git a/CHANGELOG.md b/CHANGELOG.md index 40b62a40d03..2aa395855a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2748](https://github.com/open-telemetry/opentelemetry-python/pull/2748)) - Fix Jaeger propagator usage with NonRecordingSpan ([#2762](https://github.com/open-telemetry/opentelemetry-python/pull/2762)) +- Add `opentelemetry.propagate` module and `opentelemetry.propagators` package + to the API reference documentation + ([#2785](https://github.com/open-telemetry/opentelemetry-python/pull/2785)) ## [1.12.0rc1-0.31b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc1-0.31b0) - 2022-05-17 diff --git a/docs/api/index.rst b/docs/api/index.rst index a13c9e698bb..22d77fc5a08 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -8,6 +8,8 @@ OpenTelemetry Python API baggage context + propagate + propagators trace metrics environment_variables diff --git a/docs/api/propagate.rst b/docs/api/propagate.rst new file mode 100644 index 00000000000..a86beeaddce --- /dev/null +++ b/docs/api/propagate.rst @@ -0,0 +1,7 @@ +opentelemetry.propagate package +======================================== + +Module contents +--------------- + +.. automodule:: opentelemetry.propagate diff --git a/docs/api/propagators.composite.rst b/docs/api/propagators.composite.rst new file mode 100644 index 00000000000..930ca0b88d7 --- /dev/null +++ b/docs/api/propagators.composite.rst @@ -0,0 +1,7 @@ +opentelemetry.propagators.composite +==================================================== + +Module contents +--------------- + +.. automodule:: opentelemetry.propagators.composite diff --git a/docs/api/propagators.rst b/docs/api/propagators.rst new file mode 100644 index 00000000000..08825315bef --- /dev/null +++ b/docs/api/propagators.rst @@ -0,0 +1,10 @@ +opentelemetry.propagators package +======================================== + +Subpackages +----------- + +.. toctree:: + + propagators.textmap + propagators.composite diff --git a/docs/api/propagators.textmap.rst b/docs/api/propagators.textmap.rst new file mode 100644 index 00000000000..a5db537b80f --- /dev/null +++ b/docs/api/propagators.textmap.rst @@ -0,0 +1,7 @@ +opentelemetry.propagators.textmap +==================================================== + +Module contents +--------------- + +.. automodule:: opentelemetry.propagators.textmap diff --git a/docs/conf.py b/docs/conf.py index 55b1af7331b..6e42aa1bd1f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -97,6 +97,9 @@ # https://github.com/sphinx-doc/sphinx/pull/3744 nitpick_ignore = [ ("py:class", "ValueT"), + ("py:class", "CarrierT"), + ("py:obj", "opentelemetry.propagators.textmap.CarrierT"), + ("py:obj", "Union"), ( "py:class", "opentelemetry.sdk.metrics._internal.instrument._Synchronous", @@ -112,23 +115,6 @@ "py:class", "opentelemetry.trace._LinkBase", ), - # TODO: Understand why sphinx is not able to find this local class - ( - "py:class", - "opentelemetry.propagators.textmap.TextMapPropagator", - ), - ( - "py:class", - "opentelemetry.propagators.textmap.DefaultGetter", - ), - ( - "any", - "opentelemetry.propagators.textmap.TextMapPropagator.extract", - ), - ( - "any", - "opentelemetry.propagators.textmap.TextMapPropagator.inject", - ), ] # Add any paths that contain templates here, relative to this directory. From af09df390680829fe366cefee367a19a0d4557f1 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 30 Jun 2022 19:27:41 +0200 Subject: [PATCH 29/37] Allow git to check out files with long names in Windows (#2790) * Allow git to check out files with long names in Windows Fixes #2789 * Update contrib SHA --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fd6261b8d4f..78619460a22 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ env: # Otherwise, set variable to the commit of your branch on # opentelemetry-python-contrib which is compatible with these Core repo # changes. - CONTRIB_REPO_SHA: 7b42e4354dc3244ef2878bfd0d7d4c80d25cba0a + CONTRIB_REPO_SHA: ac84e9968fc5bfb16016a4e0ca82059bf1e86511 # This is needed because we do not clone the core repo in contrib builds anymore. # When running contrib builds as part of core builds, we use actions/checkout@v2 which # does not set an environment variable (simply just runs tox), which is different when @@ -60,6 +60,9 @@ jobs: if: ${{ matrix.os == 'windows-2019' && matrix.python-version == 'py36' }} shell: pwsh run: Remove-Item .\.tox\ -Force -Recurse -ErrorAction Ignore + - name: Windows does not let git check out files with long names + if: ${{ matrix.os == 'windows-2019'}} + run: git config --system core.longpaths true - name: run tox run: tox -f ${{ matrix.python-version }}-${{ matrix.package }} -- --benchmark-json=${{ env.RUN_MATRIX_COMBINATION }}-benchmark.json - name: Find and merge benchmarks From f7a3e6277703c1dd06659d0b30e3fe92dce8555b Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 30 Jun 2022 23:00:20 +0200 Subject: [PATCH 30/37] Verify previous point is returned for cumulative instruments (#2773) * Verify previous point is returned for cumulative instruments Fixes #2755 * Reject non-cumulative temporalities for Prometheus metric reader * Add test case for InMemoryMetricReader * Fix prometheus import paths * Remove parameters for Prometheus init * Add sleep for windows tests * Add temporality overriding dictionary --- .../exporter/prometheus/__init__.py | 21 +++++++- .../tests/test_prometheus_exporter.py | 16 +++++- .../metrics/test_in_memory_metric_reader.py | 50 ++++++++++++++++++- 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py index ac1ea58c660..dbf4493f9f4 100644 --- a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py +++ b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py @@ -77,7 +77,16 @@ ) from prometheus_client.core import Metric as PrometheusMetric +from opentelemetry.sdk.metrics import Counter +from opentelemetry.sdk.metrics import Histogram as HistogramInstrument +from opentelemetry.sdk.metrics import ( + ObservableCounter, + ObservableGauge, + ObservableUpDownCounter, + UpDownCounter, +) from opentelemetry.sdk.metrics.export import ( + AggregationTemporality, Gauge, Histogram, HistogramDataPoint, @@ -113,7 +122,17 @@ class PrometheusMetricReader(MetricReader): """ def __init__(self, prefix: str = "") -> None: - super().__init__() + + super().__init__( + preferred_temporality={ + Counter: AggregationTemporality.CUMULATIVE, + UpDownCounter: AggregationTemporality.CUMULATIVE, + HistogramInstrument: AggregationTemporality.CUMULATIVE, + ObservableCounter: AggregationTemporality.CUMULATIVE, + ObservableUpDownCounter: AggregationTemporality.CUMULATIVE, + ObservableGauge: AggregationTemporality.CUMULATIVE, + } + ) self._collector = _CustomCollector(prefix) REGISTRY.register(self._collector) self._collector._callback = self.collect diff --git a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py index 4b6118bdab1..9e7f143e01c 100644 --- a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py +++ b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py @@ -23,6 +23,7 @@ PrometheusMetricReader, _CustomCollector, ) +from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics.export import ( AggregationTemporality, Histogram, @@ -51,7 +52,7 @@ def setUp(self): def test_constructor(self): """Test the constructor.""" with self._registry_register_patch: - exporter = PrometheusMetricReader("testprefix") + exporter = PrometheusMetricReader(prefix="testprefix") self.assertEqual(exporter._collector._prefix, "testprefix") self.assertTrue(self._mock_registry_register.called) @@ -286,3 +287,16 @@ def test_check_value(self): self.assertEqual(collector._check_value(True), "true") self.assertEqual(collector._check_value(False), "false") self.assertEqual(collector._check_value(None), "null") + + def test_multiple_collection_calls(self): + + metric_reader = PrometheusMetricReader(prefix="prefix") + provider = MeterProvider(metric_readers=[metric_reader]) + meter = provider.get_meter("getting-started", "0.1.2") + counter = meter.create_counter("counter") + counter.add(1) + result_0 = list(metric_reader._collector.collect()) + result_1 = list(metric_reader._collector.collect()) + result_2 = list(metric_reader._collector.collect()) + self.assertEqual(result_0, result_1) + self.assertEqual(result_1, result_2) diff --git a/opentelemetry-sdk/tests/metrics/test_in_memory_metric_reader.py b/opentelemetry-sdk/tests/metrics/test_in_memory_metric_reader.py index c32acc7aacd..68c81e8b7ef 100644 --- a/opentelemetry-sdk/tests/metrics/test_in_memory_metric_reader.py +++ b/opentelemetry-sdk/tests/metrics/test_in_memory_metric_reader.py @@ -12,11 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +from time import sleep from unittest import TestCase from unittest.mock import Mock from opentelemetry.metrics import Observation -from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics import Counter, MeterProvider from opentelemetry.sdk.metrics.export import ( AggregationTemporality, InMemoryMetricReader, @@ -106,3 +107,50 @@ def test_integration(self): ), 1, ) + + def test_cumulative_multiple_collect(self): + + reader = InMemoryMetricReader( + preferred_temporality={Counter: AggregationTemporality.CUMULATIVE} + ) + meter = MeterProvider(metric_readers=[reader]).get_meter("test_meter") + counter = meter.create_counter("counter1") + counter.add(1, attributes={"key": "value"}) + + reader.collect() + + number_data_point_0 = list( + reader._metrics_data.resource_metrics[0] + .scope_metrics[0] + .metrics[0] + .data.data_points + )[0] + + # Windows tests fail without this sleep because both time_unix_nano + # values are the same. + sleep(0.1) + reader.collect() + + number_data_point_1 = list( + reader._metrics_data.resource_metrics[0] + .scope_metrics[0] + .metrics[0] + .data.data_points + )[0] + + self.assertEqual( + number_data_point_0.attributes, number_data_point_1.attributes + ) + self.assertEqual( + number_data_point_0.start_time_unix_nano, + number_data_point_1.start_time_unix_nano, + ) + self.assertEqual( + number_data_point_0.start_time_unix_nano, + number_data_point_1.start_time_unix_nano, + ) + self.assertEqual(number_data_point_0.value, number_data_point_1.value) + self.assertGreater( + number_data_point_1.time_unix_nano, + number_data_point_0.time_unix_nano, + ) From 05906171301bd5d6852bbdb2046f4ad12646f322 Mon Sep 17 00:00:00 2001 From: Ron Nathaniel <45116635+ronnathaniel@users.noreply.github.com> Date: Fri, 1 Jul 2022 09:34:18 -0400 Subject: [PATCH 31/37] Adding optional session parameter to Trace Exporters constructors (#2783) --- CHANGELOG.md | 2 ++ .../otlp/proto/http/_log_exporter/__init__.py | 3 ++- .../otlp/proto/http/trace_exporter/__init__.py | 3 ++- .../tests/test_proto_log_exporter.py | 9 ++++++++- .../tests/test_proto_span_exporter.py | 6 ++++++ .../opentelemetry/exporter/zipkin/json/__init__.py | 11 ++++++++--- .../tests/test_zipkin_exporter.py | 7 +++++++ .../exporter/zipkin/proto/http/__init__.py | 11 ++++++++--- .../tests/test_zipkin_exporter.py | 7 +++++++ 9 files changed, 50 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aa395855a5..51135293b7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.12.0rc1-0.31b0...HEAD) +- Add optional sessions parameter to all Exporters leveraging requests.Session +([#2783](https://github.com/open-telemetry/opentelemetry-python/pull/2783)) - Add min/max fields to Histogram ([#2759](https://github.com/open-telemetry/opentelemetry-python/pull/2759)) - `opentelemetry-exporter-otlp-proto-http` Add support for OTLP/HTTP log exporter diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py index 0cca6995675..041f1ab3c07 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/_log_exporter/__init__.py @@ -62,6 +62,7 @@ def __init__( headers: Optional[Dict[str, str]] = None, timeout: Optional[int] = None, compression: Optional[Compression] = None, + session: Optional[requests.Session] = None, ): self._endpoint = endpoint or _append_logs_path( environ.get(OTEL_EXPORTER_OTLP_ENDPOINT, DEFAULT_ENDPOINT) @@ -75,7 +76,7 @@ def __init__( environ.get(OTEL_EXPORTER_OTLP_TIMEOUT, DEFAULT_TIMEOUT) ) self._compression = compression or _compression_from_env() - self._session = requests.Session() + self._session = session or requests.Session() self._session.headers.update(self._headers) self._session.headers.update( {"Content-Type": _ProtobufEncoder._CONTENT_TYPE} diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py index 156afc247d9..6f0d6ee58df 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py @@ -63,6 +63,7 @@ def __init__( headers: Optional[Dict[str, str]] = None, timeout: Optional[int] = None, compression: Optional[Compression] = None, + session: Optional[requests.Session] = None, ): self._endpoint = endpoint or environ.get( OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, @@ -86,7 +87,7 @@ def __init__( ) ) self._compression = compression or _compression_from_env() - self._session = requests.Session() + self._session = session or requests.Session() self._session.headers.update(self._headers) self._session.headers.update( {"Content-Type": _ProtobufEncoder._CONTENT_TYPE} diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py index 13b20190da5..d5e34b7463d 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_log_exporter.py @@ -16,7 +16,9 @@ import unittest from typing import List, Tuple -from unittest.mock import patch +from unittest.mock import MagicMock, patch + +import requests from opentelemetry.exporter.otlp.proto.http import Compression from opentelemetry.exporter.otlp.proto.http._log_exporter import ( @@ -81,6 +83,7 @@ def test_constructor_default(self): self.assertEqual(exporter._timeout, DEFAULT_TIMEOUT) self.assertIs(exporter._compression, DEFAULT_COMPRESSION) self.assertEqual(exporter._headers, {}) + self.assertIsInstance(exporter._session, requests.Session) @patch.dict( "os.environ", @@ -93,12 +96,14 @@ def test_constructor_default(self): }, ) def test_exporter_constructor_take_priority(self): + sess = MagicMock() exporter = OTLPLogExporter( endpoint="endpoint.local:69/logs", certificate_file="/hello.crt", headers={"testHeader1": "value1", "testHeader2": "value2"}, timeout=70, compression=Compression.NoCompression, + session=sess(), ) self.assertEqual(exporter._endpoint, "endpoint.local:69/logs") @@ -109,6 +114,7 @@ def test_exporter_constructor_take_priority(self): exporter._headers, {"testHeader1": "value1", "testHeader2": "value2"}, ) + self.assertTrue(sess.called) @patch.dict( "os.environ", @@ -133,6 +139,7 @@ def test_exporter_env(self): self.assertEqual( exporter._headers, {"envheader1": "val1", "envheader2": "val2"} ) + self.assertIsInstance(exporter._session, requests.Session) def test_encode(self): sdk_logs, expected_encoding = self.get_test_logs() diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py index e3cd2046267..4eb0db6160c 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/tests/test_proto_span_exporter.py @@ -15,6 +15,8 @@ import unittest from unittest.mock import patch +import requests + from opentelemetry.exporter.otlp.proto.http import Compression from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( DEFAULT_COMPRESSION, @@ -55,6 +57,7 @@ def test_constructor_default(self): self.assertEqual(exporter._timeout, DEFAULT_TIMEOUT) self.assertIs(exporter._compression, DEFAULT_COMPRESSION) self.assertEqual(exporter._headers, {}) + self.assertIsInstance(exporter._session, requests.Session) @patch.dict( "os.environ", @@ -86,6 +89,7 @@ def test_exporter_traces_env_take_priority(self): "traceenv3": "==val3==", }, ) + self.assertIsInstance(exporter._session, requests.Session) @patch.dict( "os.environ", @@ -105,6 +109,7 @@ def test_exporter_constructor_take_priority(self): headers={"testHeader1": "value1", "testHeader2": "value2"}, timeout=20, compression=Compression.NoCompression, + session=requests.Session(), ) self.assertEqual(exporter._endpoint, "example.com/1234") @@ -115,6 +120,7 @@ def test_exporter_constructor_take_priority(self): exporter._headers, {"testHeader1": "value1", "testHeader2": "value2"}, ) + self.assertIsInstance(exporter._session, requests.Session) @patch.dict( "os.environ", diff --git a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/__init__.py b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/__init__.py index 0e0642d0bea..7728090f546 100644 --- a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/__init__.py +++ b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/__init__.py @@ -31,6 +31,8 @@ .. code:: python + import requests + from opentelemetry import trace from opentelemetry.exporter.zipkin.json import ZipkinExporter from opentelemetry.sdk.trace import TracerProvider @@ -47,8 +49,9 @@ # local_node_ipv4="192.168.0.1", # local_node_ipv6="2001:db8::c001", # local_node_port=31313, - # max_tag_value_length=256 - # timeout=5 (in seconds) + # max_tag_value_length=256, + # timeout=5 (in seconds), + # session=requests.Session(), ) # Create a BatchSpanProcessor and add the exporter to it @@ -103,6 +106,7 @@ def __init__( local_node_port: Optional[int] = None, max_tag_value_length: Optional[int] = None, timeout: Optional[int] = None, + session: Optional[requests.Session] = None, ): """Zipkin exporter. @@ -116,6 +120,7 @@ def __init__( max_tag_value_length: Max length string attribute values can have. timeout: Maximum time the Zipkin exporter will wait for each batch export. The default value is 10s. + session: Connection session to the Zipkin collector endpoint. The tuple (local_node_ipv4, local_node_ipv6, local_node_port) is used to represent the network context of a node in the service graph. @@ -135,7 +140,7 @@ def __init__( elif version == Protocol.V2: self.encoder = JsonV2Encoder(max_tag_value_length) - self.session = requests.Session() + self.session = session or requests.Session() self.session.headers.update( {"Content-Type": self.encoder.content_type()} ) diff --git a/exporter/opentelemetry-exporter-zipkin-json/tests/test_zipkin_exporter.py b/exporter/opentelemetry-exporter-zipkin-json/tests/test_zipkin_exporter.py index 5c2aa0cbe69..77e3ef53755 100644 --- a/exporter/opentelemetry-exporter-zipkin-json/tests/test_zipkin_exporter.py +++ b/exporter/opentelemetry-exporter-zipkin-json/tests/test_zipkin_exporter.py @@ -17,6 +17,8 @@ import unittest from unittest.mock import patch +import requests + from opentelemetry import trace from opentelemetry.exporter.zipkin.encoder import Protocol from opentelemetry.exporter.zipkin.json import DEFAULT_ENDPOINT, ZipkinExporter @@ -55,6 +57,7 @@ def tearDown(self): def test_constructor_default(self): exporter = ZipkinExporter() self.assertIsInstance(exporter.encoder, JsonV2Encoder) + self.assertIsInstance(exporter.session, requests.Session) self.assertEqual(exporter.endpoint, DEFAULT_ENDPOINT) self.assertEqual(exporter.local_node.service_name, TEST_SERVICE_NAME) self.assertEqual(exporter.local_node.ipv4, None) @@ -83,6 +86,7 @@ def test_constructor_protocol_endpoint(self): exporter = ZipkinExporter(endpoint=endpoint) self.assertIsInstance(exporter.encoder, JsonV2Encoder) + self.assertIsInstance(exporter.session, requests.Session) self.assertEqual(exporter.endpoint, endpoint) self.assertEqual(exporter.local_node.service_name, TEST_SERVICE_NAME) self.assertEqual(exporter.local_node.ipv4, None) @@ -104,6 +108,7 @@ def test_constructor_all_params_and_env_vars(self): local_node_port = 30301 max_tag_value_length = 56 timeout_param = 20 + session_param = requests.Session() exporter = ZipkinExporter( constructor_param_version, @@ -113,9 +118,11 @@ def test_constructor_all_params_and_env_vars(self): local_node_port, max_tag_value_length, timeout_param, + session_param, ) self.assertIsInstance(exporter.encoder, JsonV2Encoder) + self.assertIsInstance(exporter.session, requests.Session) self.assertEqual(exporter.endpoint, constructor_param_endpoint) self.assertEqual(exporter.local_node.service_name, TEST_SERVICE_NAME) self.assertEqual( diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/__init__.py b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/__init__.py index bd98a1ff06c..5856cd7e4ea 100644 --- a/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/__init__.py +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/__init__.py @@ -31,6 +31,8 @@ .. code:: python + import requests + from opentelemetry import trace from opentelemetry.exporter.zipkin.proto.http import ZipkinExporter from opentelemetry.sdk.trace import TracerProvider @@ -46,8 +48,9 @@ # local_node_ipv4="192.168.0.1", # local_node_ipv6="2001:db8::c001", # local_node_port=31313, - # max_tag_value_length=256 - # timeout=5 (in seconds) + # max_tag_value_length=256, + # timeout=5 (in seconds), + # session=requests.Session() ) # Create a BatchSpanProcessor and add the exporter to it @@ -99,6 +102,7 @@ def __init__( local_node_port: Optional[int] = None, max_tag_value_length: Optional[int] = None, timeout: Optional[int] = None, + session: Optional[requests.Session] = None, ): """Zipkin exporter. @@ -112,6 +116,7 @@ def __init__( max_tag_value_length: Max length string attribute values can have. timeout: Maximum time the Zipkin exporter will wait for each batch export. The default value is 10s. + session: Connection session to the Zipkin collector endpoint. The tuple (local_node_ipv4, local_node_ipv6, local_node_port) is used to represent the network context of a node in the service graph. @@ -128,7 +133,7 @@ def __init__( self.encoder = ProtobufEncoder(max_tag_value_length) - self.session = requests.Session() + self.session = session or requests.Session() self.session.headers.update( {"Content-Type": self.encoder.content_type()} ) diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/tests/test_zipkin_exporter.py b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/test_zipkin_exporter.py index 8b8b01438e2..8a3c055437a 100644 --- a/exporter/opentelemetry-exporter-zipkin-proto-http/tests/test_zipkin_exporter.py +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/tests/test_zipkin_exporter.py @@ -17,6 +17,8 @@ import unittest from unittest.mock import patch +import requests + from opentelemetry import trace from opentelemetry.exporter.zipkin.node_endpoint import NodeEndpoint from opentelemetry.exporter.zipkin.proto.http import ( @@ -57,6 +59,7 @@ def tearDown(self): def test_constructor_default(self): exporter = ZipkinExporter() self.assertIsInstance(exporter.encoder, ProtobufEncoder) + self.assertIsInstance(exporter.session, requests.Session) self.assertEqual(exporter.endpoint, DEFAULT_ENDPOINT) self.assertEqual(exporter.local_node.service_name, TEST_SERVICE_NAME) self.assertEqual(exporter.local_node.ipv4, None) @@ -85,6 +88,7 @@ def test_constructor_protocol_endpoint(self): exporter = ZipkinExporter(endpoint) self.assertIsInstance(exporter.encoder, ProtobufEncoder) + self.assertIsInstance(exporter.session, requests.Session) self.assertEqual(exporter.endpoint, endpoint) self.assertEqual(exporter.local_node.service_name, TEST_SERVICE_NAME) self.assertEqual(exporter.local_node.ipv4, None) @@ -105,6 +109,7 @@ def test_constructor_all_params_and_env_vars(self): local_node_port = 30301 max_tag_value_length = 56 timeout_param = 20 + session_param = requests.Session() exporter = ZipkinExporter( constructor_param_endpoint, @@ -113,9 +118,11 @@ def test_constructor_all_params_and_env_vars(self): local_node_port, max_tag_value_length, timeout_param, + session_param, ) self.assertIsInstance(exporter.encoder, ProtobufEncoder) + self.assertIsInstance(exporter.session, requests.Session) self.assertEqual(exporter.endpoint, constructor_param_endpoint) self.assertEqual(exporter.local_node.service_name, TEST_SERVICE_NAME) self.assertEqual( From e27f2b866bd840623bb128a1c04296c52b35b919 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Mon, 4 Jul 2022 12:23:09 +0200 Subject: [PATCH 32/37] Fix unit and name regexes (#2796) * Fix unit and name regexes Fixes #2793 * Add changelog entry --- CHANGELOG.md | 4 +++- .../src/opentelemetry/metrics/_internal/instrument.py | 5 ++--- opentelemetry-api/tests/metrics/test_instruments.py | 3 +++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51135293b7f..22b479fc49c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.12.0rc1-0.31b0...HEAD) +- Fix instrument name and unit regexes + ([#2796](https://github.com/open-telemetry/opentelemetry-python/pull/2796)) - Add optional sessions parameter to all Exporters leveraging requests.Session -([#2783](https://github.com/open-telemetry/opentelemetry-python/pull/2783)) + ([#2783](https://github.com/open-telemetry/opentelemetry-python/pull/2783)) - Add min/max fields to Histogram ([#2759](https://github.com/open-telemetry/opentelemetry-python/pull/2759)) - `opentelemetry-exporter-otlp-proto-http` Add support for OTLP/HTTP log exporter diff --git a/opentelemetry-api/src/opentelemetry/metrics/_internal/instrument.py b/opentelemetry-api/src/opentelemetry/metrics/_internal/instrument.py index f98cbd72430..b203b8ffe7c 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/_internal/instrument.py +++ b/opentelemetry-api/src/opentelemetry/metrics/_internal/instrument.py @@ -18,7 +18,6 @@ from abc import ABC, abstractmethod from dataclasses import dataclass from logging import getLogger -from re import ASCII from re import compile as re_compile from typing import ( Callable, @@ -39,8 +38,8 @@ _logger = getLogger(__name__) -_name_regex = re_compile(r"[a-zA-Z][-.\w]{0,62}", ASCII) -_unit_regex = re_compile(r"\w{0,63}", ASCII) +_name_regex = re_compile(r"[a-zA-Z][-_.a-zA-Z0-9]{0,62}") +_unit_regex = re_compile(r"[\x00-\x7F]{0,63}") @dataclass(frozen=True) diff --git a/opentelemetry-api/tests/metrics/test_instruments.py b/opentelemetry-api/tests/metrics/test_instruments.py index ff2ab2b3e52..3e1e3fe745d 100644 --- a/opentelemetry-api/tests/metrics/test_instruments.py +++ b/opentelemetry-api/tests/metrics/test_instruments.py @@ -571,6 +571,7 @@ def test_name_regex(self): self.assertTrue(instrument._check_name_and_unit("a.", "unit")[0]) self.assertTrue(instrument._check_name_and_unit("a-", "unit")[0]) self.assertTrue(instrument._check_name_and_unit("a_", "unit")[0]) + self.assertFalse(instrument._check_name_and_unit("a" * 64, "unit")[0]) self.assertFalse(instrument._check_name_and_unit("Ñ", "unit")[0]) self.assertFalse(instrument._check_name_and_unit("_a", "unit")[0]) @@ -582,5 +583,7 @@ def test_unit_regex(self): instrument = ChildInstrument("name") self.assertTrue(instrument._check_name_and_unit("name", "a" * 63)[1]) + self.assertTrue(instrument._check_name_and_unit("name", "{a}")[1]) + self.assertFalse(instrument._check_name_and_unit("name", "a" * 64)[1]) self.assertFalse(instrument._check_name_and_unit("name", "Ñ")[1]) From b9a6358583c0676482a043279c01b252dc6c308e Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Mon, 4 Jul 2022 15:14:46 +0200 Subject: [PATCH 33/37] Release 1.12.0rc2-0.32b0 (#2795) * Release 1.12.0rc2-0.32b0 * Update CHANGELOG.md Co-authored-by: Srikanth Chekuri Co-authored-by: Srikanth Chekuri --- .github/workflows/test.yml | 2 +- CHANGELOG.md | 6 +++++- eachdist.ini | 4 ++-- .../src/opentelemetry/exporter/jaeger/proto/grpc/version.py | 2 +- .../src/opentelemetry/exporter/jaeger/thrift/version.py | 2 +- exporter/opentelemetry-exporter-jaeger/setup.cfg | 4 ++-- .../src/opentelemetry/exporter/jaeger/version.py | 2 +- .../src/opentelemetry/exporter/opencensus/version.py | 2 +- exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg | 2 +- .../src/opentelemetry/exporter/otlp/proto/grpc/version.py | 2 +- exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg | 2 +- .../src/opentelemetry/exporter/otlp/proto/http/version.py | 2 +- exporter/opentelemetry-exporter-otlp/setup.cfg | 4 ++-- .../src/opentelemetry/exporter/otlp/version.py | 2 +- .../src/opentelemetry/exporter/prometheus/version.py | 2 +- .../src/opentelemetry/exporter/zipkin/json/version.py | 2 +- exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg | 2 +- .../src/opentelemetry/exporter/zipkin/proto/http/version.py | 2 +- exporter/opentelemetry-exporter-zipkin/setup.cfg | 4 ++-- .../src/opentelemetry/exporter/zipkin/version.py | 2 +- opentelemetry-api/src/opentelemetry/version.py | 2 +- opentelemetry-proto/src/opentelemetry/proto/version.py | 2 +- opentelemetry-sdk/setup.cfg | 4 ++-- opentelemetry-sdk/src/opentelemetry/sdk/version.py | 2 +- .../src/opentelemetry/semconv/version.py | 2 +- .../src/opentelemetry/propagators/b3/version.py | 2 +- .../src/opentelemetry/propagators/jaeger/version.py | 2 +- shim/opentelemetry-opentracing-shim/setup.cfg | 2 +- .../src/opentelemetry/shim/opentracing_shim/version.py | 2 +- tests/opentelemetry-test-utils/setup.cfg | 4 ++-- .../src/opentelemetry/test/version.py | 2 +- 31 files changed, 41 insertions(+), 37 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 78619460a22..3f2566451b8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ env: # Otherwise, set variable to the commit of your branch on # opentelemetry-python-contrib which is compatible with these Core repo # changes. - CONTRIB_REPO_SHA: ac84e9968fc5bfb16016a4e0ca82059bf1e86511 + CONTRIB_REPO_SHA: 42ff80bef8a03ff214a54323a2631da06e6dc5e4 # This is needed because we do not clone the core repo in contrib builds anymore. # When running contrib builds as part of core builds, we use actions/checkout@v2 which # does not set an environment variable (simply just runs tox), which is different when diff --git a/CHANGELOG.md b/CHANGELOG.md index 22b479fc49c..2bb41eee5ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.12.0rc1-0.31b0...HEAD) +## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.12.0rc2-0.32b0...HEAD) + +## [1.12.0rc2-0.32b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc2-0.32b0) - 2022-07-04 + + - Fix instrument name and unit regexes ([#2796](https://github.com/open-telemetry/opentelemetry-python/pull/2796)) diff --git a/eachdist.ini b/eachdist.ini index ebf00880423..dca92043e9c 100644 --- a/eachdist.ini +++ b/eachdist.ini @@ -11,7 +11,7 @@ sortfirst= exporter/* [stable] -version=1.12.0rc1 +version=1.12.0rc2 packages= opentelemetry-sdk @@ -30,7 +30,7 @@ packages= opentelemetry-api [prerelease] -version=0.31b0 +version=0.32b0 packages= opentelemetry-opentracing-shim diff --git a/exporter/opentelemetry-exporter-jaeger-proto-grpc/src/opentelemetry/exporter/jaeger/proto/grpc/version.py b/exporter/opentelemetry-exporter-jaeger-proto-grpc/src/opentelemetry/exporter/jaeger/proto/grpc/version.py index 83073121526..414738e76e9 100644 --- a/exporter/opentelemetry-exporter-jaeger-proto-grpc/src/opentelemetry/exporter/jaeger/proto/grpc/version.py +++ b/exporter/opentelemetry-exporter-jaeger-proto-grpc/src/opentelemetry/exporter/jaeger/proto/grpc/version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "1.12.0rc2" diff --git a/exporter/opentelemetry-exporter-jaeger-thrift/src/opentelemetry/exporter/jaeger/thrift/version.py b/exporter/opentelemetry-exporter-jaeger-thrift/src/opentelemetry/exporter/jaeger/thrift/version.py index 83073121526..414738e76e9 100644 --- a/exporter/opentelemetry-exporter-jaeger-thrift/src/opentelemetry/exporter/jaeger/thrift/version.py +++ b/exporter/opentelemetry-exporter-jaeger-thrift/src/opentelemetry/exporter/jaeger/thrift/version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "1.12.0rc2" diff --git a/exporter/opentelemetry-exporter-jaeger/setup.cfg b/exporter/opentelemetry-exporter-jaeger/setup.cfg index 09629011636..14818cc3663 100644 --- a/exporter/opentelemetry-exporter-jaeger/setup.cfg +++ b/exporter/opentelemetry-exporter-jaeger/setup.cfg @@ -41,8 +41,8 @@ package_dir= =src packages=find_namespace: install_requires = - opentelemetry-exporter-jaeger-proto-grpc == 1.12.0rc1 - opentelemetry-exporter-jaeger-thrift == 1.12.0rc1 + opentelemetry-exporter-jaeger-proto-grpc == 1.12.0rc2 + opentelemetry-exporter-jaeger-thrift == 1.12.0rc2 [options.packages.find] where = src diff --git a/exporter/opentelemetry-exporter-jaeger/src/opentelemetry/exporter/jaeger/version.py b/exporter/opentelemetry-exporter-jaeger/src/opentelemetry/exporter/jaeger/version.py index 83073121526..414738e76e9 100644 --- a/exporter/opentelemetry-exporter-jaeger/src/opentelemetry/exporter/jaeger/version.py +++ b/exporter/opentelemetry-exporter-jaeger/src/opentelemetry/exporter/jaeger/version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "1.12.0rc2" diff --git a/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/version.py b/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/version.py index d8dc1e1ed7a..268a7953448 100644 --- a/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/version.py +++ b/exporter/opentelemetry-exporter-opencensus/src/opentelemetry/exporter/opencensus/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.31b0" +__version__ = "0.32b0" diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg b/exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg index 3c35fef7716..a98639cf686 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/setup.cfg @@ -44,7 +44,7 @@ install_requires = googleapis-common-protos ~= 1.52 opentelemetry-api ~= 1.3 opentelemetry-sdk ~= 1.11 - opentelemetry-proto == 1.12.0rc1 + opentelemetry-proto == 1.12.0rc2 backoff >= 1.10.0, < 2.0.0; python_version<'3.7' backoff >= 1.10.0, < 3.0.0; python_version>='3.7' diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/version.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/version.py index 54a13456e7c..5744f723da7 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/version.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "1.12.0rc2" diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg b/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg index e434fb794eb..61d29f78b77 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg +++ b/exporter/opentelemetry-exporter-otlp-proto-http/setup.cfg @@ -44,7 +44,7 @@ install_requires = googleapis-common-protos ~= 1.52 opentelemetry-api ~= 1.3 opentelemetry-sdk ~= 1.11 - opentelemetry-proto == 1.12.0rc1 + opentelemetry-proto == 1.12.0rc2 backoff >= 1.10.0, < 2.0.0; python_version<'3.7' backoff >= 1.10.0, < 3.0.0; python_version>='3.7' diff --git a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py index 54a13456e7c..5744f723da7 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py +++ b/exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "1.12.0rc2" diff --git a/exporter/opentelemetry-exporter-otlp/setup.cfg b/exporter/opentelemetry-exporter-otlp/setup.cfg index 61573650016..0dcd327d607 100644 --- a/exporter/opentelemetry-exporter-otlp/setup.cfg +++ b/exporter/opentelemetry-exporter-otlp/setup.cfg @@ -41,8 +41,8 @@ package_dir= =src packages=find_namespace: install_requires = - opentelemetry-exporter-otlp-proto-grpc == 1.12.0rc1 - opentelemetry-exporter-otlp-proto-http == 1.12.0rc1 + opentelemetry-exporter-otlp-proto-grpc == 1.12.0rc2 + opentelemetry-exporter-otlp-proto-http == 1.12.0rc2 [options.packages.find] where = src diff --git a/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/version.py b/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/version.py index 54a13456e7c..5744f723da7 100644 --- a/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/version.py +++ b/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "1.12.0rc2" diff --git a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/version.py b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/version.py index 54a13456e7c..268a7953448 100644 --- a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/version.py +++ b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "0.32b0" diff --git a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/version.py b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/version.py index 54a13456e7c..5744f723da7 100644 --- a/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/version.py +++ b/exporter/opentelemetry-exporter-zipkin-json/src/opentelemetry/exporter/zipkin/json/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "1.12.0rc2" diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg b/exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg index 70cbd52b139..fcd9d37c96f 100644 --- a/exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/setup.cfg @@ -45,7 +45,7 @@ install_requires = requests ~= 2.7 opentelemetry-api ~= 1.3 opentelemetry-sdk ~= 1.11 - opentelemetry-exporter-zipkin-json == 1.12.0rc1 + opentelemetry-exporter-zipkin-json == 1.12.0rc2 [options.packages.find] where = src diff --git a/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/version.py b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/version.py index 54a13456e7c..5744f723da7 100644 --- a/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/version.py +++ b/exporter/opentelemetry-exporter-zipkin-proto-http/src/opentelemetry/exporter/zipkin/proto/http/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "1.12.0rc2" diff --git a/exporter/opentelemetry-exporter-zipkin/setup.cfg b/exporter/opentelemetry-exporter-zipkin/setup.cfg index 98589854ee9..949b0dfb3a3 100644 --- a/exporter/opentelemetry-exporter-zipkin/setup.cfg +++ b/exporter/opentelemetry-exporter-zipkin/setup.cfg @@ -41,8 +41,8 @@ package_dir= =src packages=find_namespace: install_requires = - opentelemetry-exporter-zipkin-json == 1.12.0rc1 - opentelemetry-exporter-zipkin-proto-http == 1.12.0rc1 + opentelemetry-exporter-zipkin-json == 1.12.0rc2 + opentelemetry-exporter-zipkin-proto-http == 1.12.0rc2 [options.packages.find] where = src diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/version.py b/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/version.py index 54a13456e7c..5744f723da7 100644 --- a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/version.py +++ b/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "1.12.0rc2" diff --git a/opentelemetry-api/src/opentelemetry/version.py b/opentelemetry-api/src/opentelemetry/version.py index 54a13456e7c..5744f723da7 100644 --- a/opentelemetry-api/src/opentelemetry/version.py +++ b/opentelemetry-api/src/opentelemetry/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "1.12.0rc2" diff --git a/opentelemetry-proto/src/opentelemetry/proto/version.py b/opentelemetry-proto/src/opentelemetry/proto/version.py index 54a13456e7c..5744f723da7 100644 --- a/opentelemetry-proto/src/opentelemetry/proto/version.py +++ b/opentelemetry-proto/src/opentelemetry/proto/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "1.12.0rc2" diff --git a/opentelemetry-sdk/setup.cfg b/opentelemetry-sdk/setup.cfg index 7ee57a00a3f..b1c9ed9c1bf 100644 --- a/opentelemetry-sdk/setup.cfg +++ b/opentelemetry-sdk/setup.cfg @@ -43,8 +43,8 @@ packages=find_namespace: zip_safe = False include_package_data = True install_requires = - opentelemetry-api == 1.12.0rc1 - opentelemetry-semantic-conventions == 0.31b0 + opentelemetry-api == 1.12.0rc2 + opentelemetry-semantic-conventions == 0.32b0 setuptools >= 16.0 dataclasses == 0.8; python_version < '3.7' typing-extensions >= 3.7.4 diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/version.py b/opentelemetry-sdk/src/opentelemetry/sdk/version.py index 54a13456e7c..5744f723da7 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/version.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "1.12.0rc2" diff --git a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/version.py b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/version.py index d8dc1e1ed7a..268a7953448 100644 --- a/opentelemetry-semantic-conventions/src/opentelemetry/semconv/version.py +++ b/opentelemetry-semantic-conventions/src/opentelemetry/semconv/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.31b0" +__version__ = "0.32b0" diff --git a/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/version.py b/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/version.py index 54a13456e7c..5744f723da7 100644 --- a/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/version.py +++ b/propagator/opentelemetry-propagator-b3/src/opentelemetry/propagators/b3/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "1.12.0rc2" diff --git a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/version.py b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/version.py index 54a13456e7c..5744f723da7 100644 --- a/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/version.py +++ b/propagator/opentelemetry-propagator-jaeger/src/opentelemetry/propagators/jaeger/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.12.0rc1" +__version__ = "1.12.0rc2" diff --git a/shim/opentelemetry-opentracing-shim/setup.cfg b/shim/opentelemetry-opentracing-shim/setup.cfg index 367586ed83e..14f45de7f0c 100644 --- a/shim/opentelemetry-opentracing-shim/setup.cfg +++ b/shim/opentelemetry-opentracing-shim/setup.cfg @@ -47,7 +47,7 @@ install_requires = [options.extras_require] test = - opentelemetry-test-utils == 0.31b0 + opentelemetry-test-utils == 0.32b0 opentracing ~= 2.2.0 [options.packages.find] diff --git a/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/version.py b/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/version.py index d8dc1e1ed7a..268a7953448 100644 --- a/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/version.py +++ b/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.31b0" +__version__ = "0.32b0" diff --git a/tests/opentelemetry-test-utils/setup.cfg b/tests/opentelemetry-test-utils/setup.cfg index 4b8f20e1185..659778feba4 100644 --- a/tests/opentelemetry-test-utils/setup.cfg +++ b/tests/opentelemetry-test-utils/setup.cfg @@ -38,8 +38,8 @@ package_dir= =src packages=find_namespace: install_requires = - opentelemetry-api == 1.12.0rc1 - opentelemetry-sdk == 1.12.0rc1 + opentelemetry-api == 1.12.0rc2 + opentelemetry-sdk == 1.12.0rc2 asgiref ~= 3.0 [options.extras_require] diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/version.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/version.py index 8ae7aaafbe2..6a7b29001aa 100644 --- a/tests/opentelemetry-test-utils/src/opentelemetry/test/version.py +++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/version.py @@ -1 +1 @@ -__version__ = "0.31b0" +__version__ = "0.32b0" From dbcec9f3b143e4de424c16be9ecd19a16be3e630 Mon Sep 17 00:00:00 2001 From: oceyral Date: Wed, 6 Jul 2022 18:45:57 +0200 Subject: [PATCH 34/37] Fix tracing decorator with late configuration (#2754) --- CHANGELOG.md | 7 ++++- .../src/opentelemetry/trace/__init__.py | 6 ++-- opentelemetry-api/tests/trace/test_proxy.py | 30 ++++++++++++++++++- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bb41eee5ec..2d874d0b4f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.12.0rc2-0.32b0...HEAD) + +- Fix tracing decorator with late configuration + ([#2754](https://github.com/open-telemetry/opentelemetry-python/pull/2754)) + + ## [1.12.0rc2-0.32b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc2-0.32b0) - 2022-07-04 @@ -14,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix instrument name and unit regexes ([#2796](https://github.com/open-telemetry/opentelemetry-python/pull/2796)) - Add optional sessions parameter to all Exporters leveraging requests.Session - ([#2783](https://github.com/open-telemetry/opentelemetry-python/pull/2783)) + ([#2783](https://github.com/open-telemetry/opentelemetry-python/pull/2783) - Add min/max fields to Histogram ([#2759](https://github.com/open-telemetry/opentelemetry-python/pull/2759)) - `opentelemetry-exporter-otlp-proto-http` Add support for OTLP/HTTP log exporter diff --git a/opentelemetry-api/src/opentelemetry/trace/__init__.py b/opentelemetry-api/src/opentelemetry/trace/__init__.py index be0d8933b7e..53bb40f0e2d 100644 --- a/opentelemetry-api/src/opentelemetry/trace/__init__.py +++ b/opentelemetry-api/src/opentelemetry/trace/__init__.py @@ -434,8 +434,10 @@ def _tracer(self) -> Tracer: def start_span(self, *args, **kwargs) -> Span: # type: ignore return self._tracer.start_span(*args, **kwargs) # type: ignore - def start_as_current_span(self, *args, **kwargs) -> Span: # type: ignore - return self._tracer.start_as_current_span(*args, **kwargs) # type: ignore + @contextmanager # type: ignore + def start_as_current_span(self, *args, **kwargs) -> Iterator[Span]: # type: ignore + with self._tracer.start_as_current_span(*args, **kwargs) as span: # type: ignore + yield span class NoOpTracer(Tracer): diff --git a/opentelemetry-api/tests/trace/test_proxy.py b/opentelemetry-api/tests/trace/test_proxy.py index b361540b9d3..e48a2157aec 100644 --- a/opentelemetry-api/tests/trace/test_proxy.py +++ b/opentelemetry-api/tests/trace/test_proxy.py @@ -15,10 +15,15 @@ # pylint: disable=W0212,W0222,W0221 import typing import unittest +from contextlib import contextmanager from opentelemetry import trace from opentelemetry.test.globals_test import TraceGlobalsTest -from opentelemetry.trace.span import INVALID_SPAN_CONTEXT, NonRecordingSpan +from opentelemetry.trace.span import ( + INVALID_SPAN_CONTEXT, + NonRecordingSpan, + Span, +) class TestProvider(trace.NoOpTracerProvider): @@ -35,6 +40,11 @@ class TestTracer(trace.NoOpTracer): def start_span(self, *args, **kwargs): return TestSpan(INVALID_SPAN_CONTEXT) + @contextmanager + def start_as_current_span(self, *args, **kwargs): # type: ignore + with trace.use_span(self.start_span(*args, **kwargs)) as span: # type: ignore + yield span + class TestSpan(NonRecordingSpan): pass @@ -73,3 +83,21 @@ def test_proxy_tracer(self): # creates real spans with tracer.start_span("") as span: self.assertIsInstance(span, TestSpan) + + def test_late_config(self): + # get a tracer and instrument a function as we would at the + # root of a module + tracer = trace.get_tracer("test") + + @tracer.start_as_current_span("span") + def my_function() -> Span: + return trace.get_current_span() + + # call function before configuring tracing provider, should + # return INVALID_SPAN from the NoOpTracer + self.assertEqual(my_function(), trace.INVALID_SPAN) + + # configure tracing provider + trace.set_tracer_provider(TestProvider()) + # call function again, we should now be getting a TestSpan + self.assertIsInstance(my_function(), TestSpan) From fe26adaa07f88324c933a75f1a4dd199c9c8b2d7 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 7 Jul 2022 16:16:57 +0200 Subject: [PATCH 35/37] Fix warning message for OTLP gRPC exporter mixin (#2781) * Fix warning message for OTLP gRPC exporter mixin Fixes #2780 * Refactor export parameter type * Add changelog entry * Use fixed warning messages for traces and metrics * Use subclass-specific error messages * Fix test cases * Fix lint --- CHANGELOG.md | 6 +- .../otlp/proto/grpc/_log_exporter/__init__.py | 4 ++ .../exporter/otlp/proto/grpc/exporter.py | 39 +++++++++-- .../proto/grpc/metric_exporter/__init__.py | 4 ++ .../proto/grpc/trace_exporter/__init__.py | 4 ++ .../tests/logs/test_otlp_logs_exporter.py | 4 ++ .../metrics/test_otlp_metrics_exporter.py | 4 ++ .../tests/test_otlp_exporter_mixin.py | 68 ++++++++++++++++++- .../tests/test_otlp_trace_exporter.py | 4 ++ 9 files changed, 126 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d874d0b4f6..50261843496 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,15 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.12.0rc2-0.32b0...HEAD) - +- Fix OTLP gRPC exporter warning message + ([#2781](https://github.com/open-telemetry/opentelemetry-python/pull/2781)) - Fix tracing decorator with late configuration ([#2754](https://github.com/open-telemetry/opentelemetry-python/pull/2754)) - ## [1.12.0rc2-0.32b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc2-0.32b0) - 2022-07-04 - - - Fix instrument name and unit regexes ([#2796](https://github.com/open-telemetry/opentelemetry-python/pull/2796)) - Add optional sessions parameter to all Exporters leveraging requests.Session diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py index 51433d57409..489cf35c372 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_log_exporter/__init__.py @@ -154,3 +154,7 @@ def export(self, batch: Sequence[LogData]) -> LogExportResult: def shutdown(self) -> None: pass + + @property + def _exporting(self) -> str: + return "logs" diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py index b965061c5cb..4405bcad68b 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py @@ -14,7 +14,7 @@ """OTLP Exporter""" -import logging +from logging import getLogger from abc import ABC, abstractmethod from collections.abc import Sequence from os import environ @@ -23,6 +23,7 @@ from typing import Sequence as TypingSequence from typing import TypeVar from urllib.parse import urlparse +from opentelemetry.sdk.trace import ReadableSpan from backoff import expo from google.rpc.error_details_pb2 import RetryInfo @@ -52,8 +53,9 @@ ) from opentelemetry.sdk.resources import Resource as SDKResource from opentelemetry.util.re import parse_headers +from opentelemetry.sdk.metrics.export import MetricsData -logger = logging.getLogger(__name__) +logger = getLogger(__name__) SDKDataT = TypeVar("SDKDataT") ResourceDataT = TypeVar("ResourceDataT") TypingResourceT = TypeVar("TypingResourceT") @@ -277,8 +279,19 @@ def _translate_attributes(self, attributes) -> TypingSequence[KeyValue]: logger.exception(error) return output - def _export(self, data: TypingSequence[SDKDataT]) -> ExportResultT: - + def _export( + self, data: Union[TypingSequence[ReadableSpan], MetricsData] + ) -> ExportResultT: + + # FIXME remove this check if the export type for traces + # gets updated to a class that represents the proto + # TracesData and use the code below instead. + # logger.warning( + # "Transient error %s encountered while exporting %s, retrying in %ss.", + # error.code(), + # data.__class__.__name__, + # delay, + # ) max_value = 64 # expo returns a generator that yields delay values which grow # exponentially. Once delay is greater than max_value, the yielded @@ -321,15 +334,20 @@ def _export(self, data: TypingSequence[SDKDataT]) -> ExportResultT: ) logger.warning( - "Transient error %s encountered while exporting span batch, retrying in %ss.", + ( + "Transient error %s encountered while exporting " + "%s, retrying in %ss." + ), error.code(), + self._exporting, delay, ) sleep(delay) continue else: logger.error( - "Failed to export span batch, error code: %s", + "Failed to export %s, error code: %s", + self._exporting, error.code(), ) @@ -342,3 +360,12 @@ def _export(self, data: TypingSequence[SDKDataT]) -> ExportResultT: def shutdown(self) -> None: pass + + @property + @abstractmethod + def _exporting(self) -> str: + """ + Returns a string that describes the overall exporter, to be used in + warning messages. + """ + pass diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py index c5f4acad06c..fb316ab2e8f 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py @@ -206,3 +206,7 @@ def export( def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None: pass + + @property + def _exporting(self) -> str: + return "metrics" diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py index 084a5d93b14..5626012536f 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py @@ -289,3 +289,7 @@ def _translate_data( def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: return self._export(spans) + + @property + def _exporting(self): + return "traces" diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/logs/test_otlp_logs_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/logs/test_otlp_logs_exporter.py index 4ee8f6a0b37..a9c63eaa0a0 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/logs/test_otlp_logs_exporter.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/logs/test_otlp_logs_exporter.py @@ -161,6 +161,10 @@ def setUp(self): def tearDown(self): self.server.stop(None) + def test_exporting(self): + # pylint: disable=protected-access + self.assertEqual(self.exporter._exporting, "logs") + @patch( "opentelemetry.exporter.otlp.proto.grpc.exporter.ssl_channel_credentials" ) diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/metrics/test_otlp_metrics_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/metrics/test_otlp_metrics_exporter.py index 8936272bef0..c25ab06263c 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/metrics/test_otlp_metrics_exporter.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/metrics/test_otlp_metrics_exporter.py @@ -298,6 +298,10 @@ def setUp(self): def tearDown(self): self.server.stop(None) + def test_exporting(self): + # pylint: disable=protected-access + self.assertEqual(self.exporter._exporting, "metrics") + @patch( "opentelemetry.exporter.otlp.proto.grpc.exporter.ssl_channel_credentials" ) diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_exporter_mixin.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_exporter_mixin.py index a7627b237c9..3f44ef228ee 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_exporter_mixin.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_exporter_mixin.py @@ -12,13 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. +from logging import WARNING +from types import MethodType +from typing import Sequence from unittest import TestCase -from unittest.mock import patch +from unittest.mock import Mock, patch from grpc import Compression from opentelemetry.exporter.otlp.proto.grpc.exporter import ( + ExportServiceRequestT, InvalidCompressionValueException, + OTLPExporterMixin, + RpcError, + SDKDataT, + StatusCode, environ_to_compression, ) @@ -47,3 +55,61 @@ def test_environ_to_compression(self): ) with self.assertRaises(InvalidCompressionValueException): environ_to_compression("test_invalid") + + @patch("opentelemetry.exporter.otlp.proto.grpc.exporter.expo") + def test_export_warning(self, mock_expo): + + mock_expo.configure_mock(**{"return_value": [0]}) + + rpc_error = RpcError() + + def code(self): + return None + + rpc_error.code = MethodType(code, rpc_error) + + class OTLPMockExporter(OTLPExporterMixin): + + _result = Mock() + _stub = Mock( + **{"return_value": Mock(**{"Export.side_effect": rpc_error})} + ) + + def _translate_data( + self, data: Sequence[SDKDataT] + ) -> ExportServiceRequestT: + pass + + @property + def _exporting(self) -> str: + return "mock" + + otlp_mock_exporter = OTLPMockExporter() + + with self.assertLogs(level=WARNING) as warning: + # pylint: disable=protected-access + otlp_mock_exporter._export(Mock()) + self.assertEqual( + warning.records[0].message, + "Failed to export mock, error code: None", + ) + + def code(self): # pylint: disable=function-redefined + return StatusCode.CANCELLED + + def trailing_metadata(self): + return {} + + rpc_error.code = MethodType(code, rpc_error) + rpc_error.trailing_metadata = MethodType(trailing_metadata, rpc_error) + + with self.assertLogs(level=WARNING) as warning: + # pylint: disable=protected-access + otlp_mock_exporter._export([]) + self.assertEqual( + warning.records[0].message, + ( + "Transient error StatusCode.CANCELLED encountered " + "while exporting mock, retrying in 0s." + ), + ) diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py index 3d836729011..a5cb4e699a6 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py +++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py @@ -217,6 +217,10 @@ def setUp(self): def tearDown(self): self.server.stop(None) + def test_exporting(self): + # pylint: disable=protected-access + self.assertEqual(self.exporter._exporting, "traces") + @patch.dict( "os.environ", { From 12c9b0f731d3b80f72bcc402b57c203fcc9ba77f Mon Sep 17 00:00:00 2001 From: Aaron Ai Date: Tue, 12 Jul 2022 00:39:48 +0800 Subject: [PATCH 36/37] Add markdown links check (#2812) --- .github/workflows/check-links.yml | 42 +++++++++++++++++++++++ .github/workflows/check_links_config.json | 14 ++++++++ 2 files changed, 56 insertions(+) create mode 100644 .github/workflows/check-links.yml create mode 100644 .github/workflows/check_links_config.json diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml new file mode 100644 index 00000000000..cbf945b682e --- /dev/null +++ b/.github/workflows/check-links.yml @@ -0,0 +1,42 @@ +name: check-links +on: + push: + branches: [ main ] + pull_request: + +jobs: + changedfiles: + name: changed files + runs-on: ubuntu-latest + if: ${{ github.actor != 'dependabot[bot]' }} + outputs: + md: ${{ steps.changes.outputs.md }} + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Get changed files + id: changes + run: | + echo "::set-output name=md::$(git diff --name-only --diff-filter=ACMRTUXB $(git merge-base origin/main ${{ github.event.pull_request.head.sha }}) ${{ github.event.pull_request.head.sha }} | grep .md$ | xargs)" + check-links: + runs-on: ubuntu-latest + needs: changedfiles + if: ${{needs.changedfiles.outputs.md}} + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Install markdown-link-check + run: npm install -g markdown-link-check + + - name: Run markdown-link-check + run: | + markdown-link-check \ + --verbose \ + --config .github/workflows/check_links_config.json \ + ${{needs.changedfiles.outputs.md}} \ + || { echo "Check that anchor links are lowercase"; exit 1; } \ No newline at end of file diff --git a/.github/workflows/check_links_config.json b/.github/workflows/check_links_config.json new file mode 100644 index 00000000000..4f17e90626f --- /dev/null +++ b/.github/workflows/check_links_config.json @@ -0,0 +1,14 @@ +{ + "ignorePatterns": [ + { + "pattern": "http(s)?://\\d+\\.\\d+\\.\\d+\\.\\d+" + }, + { + "pattern": "http(s)?://localhost" + }, + { + "pattern": "http(s)?://example.com" + } + ], + "aliveStatusCodes": [429, 200] +} From b86a9a5e3ddc8f8299f8d07715785aabbe2f9e08 Mon Sep 17 00:00:00 2001 From: Alex Boten Date: Mon, 11 Jul 2022 12:15:17 -0700 Subject: [PATCH 37/37] [chore] update approvers list (#2814) --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f5620db91c8..9a060129db2 100644 --- a/README.md +++ b/README.md @@ -136,10 +136,17 @@ Meeting notes are available as a public [Google doc](https://docs.google.com/doc Approvers ([@open-telemetry/python-approvers](https://github.com/orgs/open-telemetry/teams/python-approvers)): - [Aaron Abbott](https://github.com/aabmass), Google -- [Alex Boten](https://github.com/codeboten), Lightstep - [Owais Lone](https://github.com/owais), Splunk - [Nathaniel Ruiz Nowell](https://github.com/NathanielRN), AWS +Emeritus Approvers + +- [Carlos Alberto Cortez](https://github.com/carlosalberto), Lightstep +- [Christian Neumüller](https://github.com/Oberon00), Dynatrace +- [Hector Hernandez](https://github.com/hectorhdzg), Microsoft +- [Mauricio Vásquez](https://github.com/mauriciovasquezbernal), Kinvolk +- [Tahir H. Butt](https://github.com/majorgreys) DataDog + *For more information about the approver role, see the [community repository](https://github.com/open-telemetry/community/blob/main/community-membership.md#approver).* Maintainers ([@open-telemetry/python-maintainers](https://github.com/orgs/open-telemetry/teams/python-maintainers)): @@ -148,6 +155,13 @@ Maintainers ([@open-telemetry/python-maintainers](https://github.com/orgs/open-t - [Leighton Chen](https://github.com/lzchen), Microsoft - [Srikanth Chekuri](https://github.com/srikanthccv) +Emeritus Maintainers: + +- [Alex Boten](https://github.com/codeboten), Lightstep +- [Chris Kleinknecht](https://github.com/c24t), Google +- [Reiley Yang](https://github.com/reyang), Microsoft +- [Yusuke Tsutsumi](https://github.com/toumorokoshi), Google + *For more information about the maintainer role, see the [community repository](https://github.com/open-telemetry/community/blob/main/community-membership.md#maintainer).* ### Thanks to all the people who already contributed!