Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-opentelemetry-exporter-otlp-proto-common for openSUSE:Factory checked in at 2025-01-09 15:09:51 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-opentelemetry-exporter-otlp-proto-common (Old) and /work/SRC/openSUSE:Factory/.python-opentelemetry-exporter-otlp-proto-common.new.1881 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-opentelemetry-exporter-otlp-proto-common" Thu Jan 9 15:09:51 2025 rev:8 rq:1235917 version:1.29.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-opentelemetry-exporter-otlp-proto-common/python-opentelemetry-exporter-otlp-proto-common.changes 2024-09-05 15:48:18.470862227 +0200 +++ /work/SRC/openSUSE:Factory/.python-opentelemetry-exporter-otlp-proto-common.new.1881/python-opentelemetry-exporter-otlp-proto-common.changes 2025-01-09 15:11:30.070415085 +0100 @@ -1,0 +2,31 @@ +Wed Jan 8 10:15:49 UTC 2025 - John Paul Adrian Glaubitz <adrian.glaub...@suse.com> + +- Update to 1.29.0 + * Fix crash exporting a log record with None body + * Fix metrics export with exemplar and no context and filtering observable instruments + * Fix recursion error with sdk disabled and handler added to root logger + * sdk: setup EventLogger when OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED is set + * api: fix logging of duplicate EventLogger setup warning + * sdk: fix setting of process owner in ProcessResourceDetector + * sdk: fix serialization of logs severity_number field to int + * Remove `TestBase.assertEqualSpanInstrumentationInfo` method, + use `assertEqualSpanInstrumentationScope` instead + * sdk: instantiate lazily `ExemplarBucket`s in `ExemplarReservoir`s + * semantic-conventions: Bump to 1.29.0 +- from version 1.28.0 + * Removed superfluous py.typed markers and added them where they were missing + * Include metric info in encoding exceptions + * sdk: Add support for log formatting + * sdk: Add Host resource detector + * sdk: Implementation of exemplars + * Implement events sdk + * Update semantic conventions to version 1.28.0 + * Add support to protobuf 5+ and drop support to protobuf 3 and 4 + * Update environment variable descriptions to match signal + * Record logger name as the instrumentation scope name + * Fix memory leak in exporter and reader + * Drop `OTEL_PYTHON_EXPERIMENTAL_DISABLE_PROMETHEUS_UNIT_NORMALIZATION` environment variable + * Improve compatibility with other logging libraries that override + `LogRecord.getMessage()` in order to customize message formatting + +------------------------------------------------------------------- Old: ---- opentelemetry_exporter_otlp_proto_common-1.27.0.tar.gz New: ---- opentelemetry_exporter_otlp_proto_common-1.29.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-opentelemetry-exporter-otlp-proto-common.spec ++++++ --- /var/tmp/diff_new_pack.Fycx1L/_old 2025-01-09 15:11:30.530434141 +0100 +++ /var/tmp/diff_new_pack.Fycx1L/_new 2025-01-09 15:11:30.530434141 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-opentelemetry-exporter-otlp-proto-common # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) 2025 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-opentelemetry-exporter-otlp-proto-common -Version: 1.27.0 +Version: 1.29.0 Release: 0 Summary: OpenTelemetry Protobuf encoding License: Apache-2.0 ++++++ opentelemetry_exporter_otlp_proto_common-1.27.0.tar.gz -> opentelemetry_exporter_otlp_proto_common-1.29.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentelemetry_exporter_otlp_proto_common-1.27.0/PKG-INFO new/opentelemetry_exporter_otlp_proto_common-1.29.0/PKG-INFO --- old/opentelemetry_exporter_otlp_proto_common-1.27.0/PKG-INFO 2020-02-02 01:00:00.000000000 +0100 +++ new/opentelemetry_exporter_otlp_proto_common-1.29.0/PKG-INFO 2020-02-02 01:00:00.000000000 +0100 @@ -1,11 +1,10 @@ Metadata-Version: 2.3 Name: opentelemetry-exporter-otlp-proto-common -Version: 1.27.0 +Version: 1.29.0 Summary: OpenTelemetry Protobuf encoding Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python/tree/main/exporter/opentelemetry-exporter-otlp-proto-common Author-email: OpenTelemetry Authors <cncf-opentelemetry-contribut...@lists.cncf.io> License: Apache-2.0 -License-File: LICENSE Classifier: Development Status :: 5 - Production/Stable Classifier: Framework :: OpenTelemetry Classifier: Framework :: OpenTelemetry :: Exporters @@ -19,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Requires-Python: >=3.8 -Requires-Dist: opentelemetry-proto==1.27.0 +Requires-Dist: opentelemetry-proto==1.29.0 Description-Content-Type: text/x-rst OpenTelemetry Protobuf Encoding diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentelemetry_exporter_otlp_proto_common-1.27.0/pyproject.toml new/opentelemetry_exporter_otlp_proto_common-1.29.0/pyproject.toml --- old/opentelemetry_exporter_otlp_proto_common-1.27.0/pyproject.toml 2020-02-02 01:00:00.000000000 +0100 +++ new/opentelemetry_exporter_otlp_proto_common-1.29.0/pyproject.toml 2020-02-02 01:00:00.000000000 +0100 @@ -27,14 +27,14 @@ "Programming Language :: Python :: 3.12", ] dependencies = [ - "opentelemetry-proto == 1.27.0", + "opentelemetry-proto == 1.29.0", ] [project.urls] Homepage = "https://github.com/open-telemetry/opentelemetry-python/tree/main/exporter/opentelemetry-exporter-otlp-proto-common" [tool.hatch.version] -path = "src/opentelemetry/exporter/otlp/proto/common/version.py" +path = "src/opentelemetry/exporter/otlp/proto/common/version/__init__.py" [tool.hatch.build.targets.sdist] include = [ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentelemetry_exporter_otlp_proto_common-1.27.0/src/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py new/opentelemetry_exporter_otlp_proto_common-1.29.0/src/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py --- old/opentelemetry_exporter_otlp_proto_common-1.27.0/src/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py 2020-02-02 01:00:00.000000000 +0100 +++ new/opentelemetry_exporter_otlp_proto_common-1.29.0/src/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py 2020-02-02 01:00:00.000000000 +0100 @@ -18,31 +18,31 @@ from itertools import count from typing import ( Any, - Mapping, - Optional, - List, Callable, - TypeVar, Dict, Iterator, + List, + Mapping, + Optional, + TypeVar, ) -from opentelemetry.sdk.util.instrumentation import InstrumentationScope +from opentelemetry.proto.common.v1.common_pb2 import AnyValue as PB2AnyValue from opentelemetry.proto.common.v1.common_pb2 import ( - InstrumentationScope as PB2InstrumentationScope, + ArrayValue as PB2ArrayValue, ) -from opentelemetry.proto.resource.v1.resource_pb2 import ( - Resource as PB2Resource, +from opentelemetry.proto.common.v1.common_pb2 import ( + InstrumentationScope as PB2InstrumentationScope, ) -from opentelemetry.proto.common.v1.common_pb2 import AnyValue as PB2AnyValue from opentelemetry.proto.common.v1.common_pb2 import KeyValue as PB2KeyValue from opentelemetry.proto.common.v1.common_pb2 import ( KeyValueList as PB2KeyValueList, ) -from opentelemetry.proto.common.v1.common_pb2 import ( - ArrayValue as PB2ArrayValue, +from opentelemetry.proto.resource.v1.resource_pb2 import ( + Resource as PB2Resource, ) from opentelemetry.sdk.trace import Resource +from opentelemetry.sdk.util.instrumentation import InstrumentationScope from opentelemetry.util.types import Attributes _logger = logging.getLogger(__name__) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentelemetry_exporter_otlp_proto_common-1.27.0/src/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py new/opentelemetry_exporter_otlp_proto_common-1.29.0/src/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py --- old/opentelemetry_exporter_otlp_proto_common-1.27.0/src/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py 2020-02-02 01:00:00.000000000 +0100 +++ new/opentelemetry_exporter_otlp_proto_common-1.29.0/src/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py 2020-02-02 01:00:00.000000000 +0100 @@ -12,25 +12,24 @@ # See the License for the specific language governing permissions and # limitations under the License. from collections import defaultdict -from typing import Sequence, List +from typing import List, Sequence from opentelemetry.exporter.otlp.proto.common._internal import ( + _encode_attributes, _encode_instrumentation_scope, _encode_resource, _encode_span_id, _encode_trace_id, _encode_value, - _encode_attributes, ) from opentelemetry.proto.collector.logs.v1.logs_service_pb2 import ( ExportLogsServiceRequest, ) +from opentelemetry.proto.logs.v1.logs_pb2 import LogRecord as PB2LogRecord from opentelemetry.proto.logs.v1.logs_pb2 import ( - ScopeLogs, ResourceLogs, + ScopeLogs, ) -from opentelemetry.proto.logs.v1.logs_pb2 import LogRecord as PB2LogRecord - from opentelemetry.sdk._logs import LogData @@ -49,13 +48,14 @@ if log_data.log_record.trace_id == 0 else _encode_trace_id(log_data.log_record.trace_id) ) + body = log_data.log_record.body return PB2LogRecord( time_unix_nano=log_data.log_record.timestamp, observed_time_unix_nano=log_data.log_record.observed_timestamp, span_id=span_id, trace_id=trace_id, flags=int(log_data.log_record.trace_flags), - body=_encode_value(log_data.log_record.body), + body=_encode_value(body) if body is not None else None, severity_text=log_data.log_record.severity_text, attributes=_encode_attributes(log_data.log_record.attributes), dropped_attributes_count=log_data.log_record.dropped_attributes, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentelemetry_exporter_otlp_proto_common-1.27.0/src/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py new/opentelemetry_exporter_otlp_proto_common-1.29.0/src/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py --- old/opentelemetry_exporter_otlp_proto_common-1.27.0/src/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py 2020-02-02 01:00:00.000000000 +0100 +++ new/opentelemetry_exporter_otlp_proto_common-1.29.0/src/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py 2020-02-02 01:00:00.000000000 +0100 @@ -12,51 +12,52 @@ # See the License for the specific language governing permissions and # limitations under the License. import logging +from os import environ +from typing import Dict, List -from opentelemetry.sdk.metrics.export import ( - MetricExporter, +from opentelemetry.exporter.otlp.proto.common._internal import ( + _encode_attributes, + _encode_span_id, + _encode_trace_id, +) +from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import ( + ExportMetricsServiceRequest, +) +from opentelemetry.proto.common.v1.common_pb2 import InstrumentationScope +from opentelemetry.proto.metrics.v1 import metrics_pb2 as pb2 +from opentelemetry.proto.resource.v1.resource_pb2 import ( + Resource as PB2Resource, +) +from opentelemetry.sdk.environment_variables import ( + OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION, + OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE, ) -from opentelemetry.sdk.metrics.view import Aggregation -from os import environ from opentelemetry.sdk.metrics import ( Counter, + Exemplar, Histogram, ObservableCounter, ObservableGauge, ObservableUpDownCounter, UpDownCounter, ) -from opentelemetry.exporter.otlp.proto.common._internal import ( - _encode_attributes, -) -from opentelemetry.sdk.environment_variables import ( - OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE, -) from opentelemetry.sdk.metrics.export import ( AggregationTemporality, -) -from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import ( - ExportMetricsServiceRequest, -) -from opentelemetry.proto.common.v1.common_pb2 import InstrumentationScope -from opentelemetry.proto.metrics.v1 import metrics_pb2 as pb2 -from opentelemetry.sdk.metrics.export import ( - MetricsData, Gauge, - Histogram as HistogramType, + MetricExporter, + MetricsData, Sum, - ExponentialHistogram as ExponentialHistogramType, ) -from typing import Dict -from opentelemetry.proto.resource.v1.resource_pb2 import ( - Resource as PB2Resource, +from opentelemetry.sdk.metrics.export import ( + ExponentialHistogram as ExponentialHistogramType, ) -from opentelemetry.sdk.environment_variables import ( - OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION, +from opentelemetry.sdk.metrics.export import ( + Histogram as HistogramType, ) from opentelemetry.sdk.metrics.view import ( - ExponentialBucketHistogramAggregation, + Aggregation, ExplicitBucketHistogramAggregation, + ExponentialBucketHistogramAggregation, ) _logger = logging.getLogger(__name__) @@ -68,7 +69,6 @@ preferred_temporality: Dict[type, AggregationTemporality] = None, preferred_aggregation: Dict[type, Aggregation] = None, ) -> None: - MetricExporter.__init__( self, preferred_temporality=self._get_temporality(preferred_temporality), @@ -78,7 +78,6 @@ def _get_temporality( self, preferred_temporality: Dict[type, AggregationTemporality] ) -> Dict[type, AggregationTemporality]: - otel_exporter_otlp_metrics_temporality_preference = ( environ.get( OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE, @@ -135,7 +134,6 @@ self, preferred_aggregation: Dict[type, Aggregation], ) -> Dict[type, Aggregation]: - otel_exporter_otlp_metrics_default_histogram_aggregation = environ.get( OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION, "explicit_bucket_histogram", @@ -144,17 +142,14 @@ if otel_exporter_otlp_metrics_default_histogram_aggregation == ( "base2_exponential_bucket_histogram" ): - instrument_class_aggregation = { Histogram: ExponentialBucketHistogramAggregation(), } else: - if otel_exporter_otlp_metrics_default_histogram_aggregation != ( "explicit_bucket_histogram" ): - _logger.warning( ( "Invalid value for %s: %s, using explicit bucket " @@ -173,152 +168,26 @@ return instrument_class_aggregation -def encode_metrics(data: MetricsData) -> ExportMetricsServiceRequest: - resource_metrics_dict = {} - - for resource_metrics in data.resource_metrics: - - resource = resource_metrics.resource - - # It is safe to assume that each entry in data.resource_metrics is - # associated with an unique resource. - scope_metrics_dict = {} - - resource_metrics_dict[resource] = scope_metrics_dict - - for scope_metrics in resource_metrics.scope_metrics: - - instrumentation_scope = scope_metrics.scope - - # The SDK groups metrics in instrumentation scopes already so - # there is no need to check for existing instrumentation scopes - # here. - pb2_scope_metrics = pb2.ScopeMetrics( - scope=InstrumentationScope( - name=instrumentation_scope.name, - version=instrumentation_scope.version, - ) - ) +class EncodingException(Exception): + """ + Raised by encode_metrics() when an exception is caught during encoding. Contains the problematic metric so + the misbehaving metric name and details can be logged during exception handling. + """ + + def __init__(self, original_exception, metric): + super().__init__() + self.original_exception = original_exception + self.metric = metric - scope_metrics_dict[instrumentation_scope] = pb2_scope_metrics + def __str__(self): + return f"{self.metric}\n{self.original_exception}" - for metric in scope_metrics.metrics: - pb2_metric = pb2.Metric( - name=metric.name, - description=metric.description, - unit=metric.unit, - ) - if isinstance(metric.data, Gauge): - for data_point in metric.data.data_points: - pt = pb2.NumberDataPoint( - attributes=_encode_attributes( - data_point.attributes - ), - time_unix_nano=data_point.time_unix_nano, - ) - if isinstance(data_point.value, int): - pt.as_int = data_point.value - else: - pt.as_double = data_point.value - pb2_metric.gauge.data_points.append(pt) - - elif isinstance(metric.data, HistogramType): - for data_point in metric.data.data_points: - pt = pb2.HistogramDataPoint( - attributes=_encode_attributes( - data_point.attributes - ), - time_unix_nano=data_point.time_unix_nano, - start_time_unix_nano=( - data_point.start_time_unix_nano - ), - count=data_point.count, - 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 - ) - pb2_metric.histogram.data_points.append(pt) - - elif isinstance(metric.data, Sum): - for data_point in metric.data.data_points: - pt = pb2.NumberDataPoint( - attributes=_encode_attributes( - data_point.attributes - ), - start_time_unix_nano=( - data_point.start_time_unix_nano - ), - time_unix_nano=data_point.time_unix_nano, - ) - if isinstance(data_point.value, int): - pt.as_int = data_point.value - else: - pt.as_double = data_point.value - # note that because sum is a message type, the - # fields must be set individually rather than - # instantiating a pb2.Sum and setting it once - pb2_metric.sum.aggregation_temporality = ( - metric.data.aggregation_temporality - ) - pb2_metric.sum.is_monotonic = metric.data.is_monotonic - pb2_metric.sum.data_points.append(pt) - - elif isinstance(metric.data, ExponentialHistogramType): - for data_point in metric.data.data_points: - - if data_point.positive.bucket_counts: - positive = pb2.ExponentialHistogramDataPoint.Buckets( - offset=data_point.positive.offset, - bucket_counts=data_point.positive.bucket_counts, - ) - else: - positive = None - - if data_point.negative.bucket_counts: - negative = pb2.ExponentialHistogramDataPoint.Buckets( - offset=data_point.negative.offset, - bucket_counts=data_point.negative.bucket_counts, - ) - else: - negative = None - - pt = pb2.ExponentialHistogramDataPoint( - attributes=_encode_attributes( - data_point.attributes - ), - time_unix_nano=data_point.time_unix_nano, - start_time_unix_nano=( - data_point.start_time_unix_nano - ), - count=data_point.count, - sum=data_point.sum, - scale=data_point.scale, - zero_count=data_point.zero_count, - positive=positive, - negative=negative, - flags=data_point.flags, - max=data_point.max, - min=data_point.min, - ) - pb2_metric.exponential_histogram.aggregation_temporality = ( - metric.data.aggregation_temporality - ) - pb2_metric.exponential_histogram.data_points.append(pt) - - else: - _logger.warning( - "unsupported data type %s", - metric.data.__class__.__name__, - ) - continue +def encode_metrics(data: MetricsData) -> ExportMetricsServiceRequest: + resource_metrics_dict = {} - pb2_scope_metrics.metrics.append(pb2_metric) + for resource_metrics in data.resource_metrics: + _encode_resource_metrics(resource_metrics, resource_metrics_dict) resource_data = [] for ( @@ -334,5 +203,184 @@ schema_url=sdk_resource.schema_url, ) ) - resource_metrics = resource_data - return ExportMetricsServiceRequest(resource_metrics=resource_metrics) + return ExportMetricsServiceRequest(resource_metrics=resource_data) + + +def _encode_resource_metrics(resource_metrics, resource_metrics_dict): + resource = resource_metrics.resource + # It is safe to assume that each entry in data.resource_metrics is + # associated with an unique resource. + scope_metrics_dict = {} + resource_metrics_dict[resource] = scope_metrics_dict + for scope_metrics in resource_metrics.scope_metrics: + instrumentation_scope = scope_metrics.scope + + # The SDK groups metrics in instrumentation scopes already so + # there is no need to check for existing instrumentation scopes + # here. + pb2_scope_metrics = pb2.ScopeMetrics( + scope=InstrumentationScope( + name=instrumentation_scope.name, + version=instrumentation_scope.version, + ) + ) + + scope_metrics_dict[instrumentation_scope] = pb2_scope_metrics + + for metric in scope_metrics.metrics: + pb2_metric = pb2.Metric( + name=metric.name, + description=metric.description, + unit=metric.unit, + ) + + try: + _encode_metric(metric, pb2_metric) + except Exception as ex: + # `from None` so we don't get "During handling of the above exception, another exception occurred:" + raise EncodingException(ex, metric) from None + + pb2_scope_metrics.metrics.append(pb2_metric) + + +def _encode_metric(metric, pb2_metric): + if isinstance(metric.data, Gauge): + for data_point in metric.data.data_points: + pt = pb2.NumberDataPoint( + attributes=_encode_attributes(data_point.attributes), + time_unix_nano=data_point.time_unix_nano, + exemplars=_encode_exemplars(data_point.exemplars), + ) + if isinstance(data_point.value, int): + pt.as_int = data_point.value + else: + pt.as_double = data_point.value + pb2_metric.gauge.data_points.append(pt) + + elif isinstance(metric.data, HistogramType): + for data_point in metric.data.data_points: + pt = pb2.HistogramDataPoint( + attributes=_encode_attributes(data_point.attributes), + time_unix_nano=data_point.time_unix_nano, + start_time_unix_nano=data_point.start_time_unix_nano, + exemplars=_encode_exemplars(data_point.exemplars), + count=data_point.count, + 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 + ) + pb2_metric.histogram.data_points.append(pt) + + elif isinstance(metric.data, Sum): + for data_point in metric.data.data_points: + pt = pb2.NumberDataPoint( + attributes=_encode_attributes(data_point.attributes), + start_time_unix_nano=data_point.start_time_unix_nano, + time_unix_nano=data_point.time_unix_nano, + exemplars=_encode_exemplars(data_point.exemplars), + ) + if isinstance(data_point.value, int): + pt.as_int = data_point.value + else: + pt.as_double = data_point.value + # note that because sum is a message type, the + # fields must be set individually rather than + # instantiating a pb2.Sum and setting it once + pb2_metric.sum.aggregation_temporality = ( + metric.data.aggregation_temporality + ) + pb2_metric.sum.is_monotonic = metric.data.is_monotonic + pb2_metric.sum.data_points.append(pt) + + elif isinstance(metric.data, ExponentialHistogramType): + for data_point in metric.data.data_points: + if data_point.positive.bucket_counts: + positive = pb2.ExponentialHistogramDataPoint.Buckets( + offset=data_point.positive.offset, + bucket_counts=data_point.positive.bucket_counts, + ) + else: + positive = None + + if data_point.negative.bucket_counts: + negative = pb2.ExponentialHistogramDataPoint.Buckets( + offset=data_point.negative.offset, + bucket_counts=data_point.negative.bucket_counts, + ) + else: + negative = None + + pt = pb2.ExponentialHistogramDataPoint( + attributes=_encode_attributes(data_point.attributes), + time_unix_nano=data_point.time_unix_nano, + start_time_unix_nano=data_point.start_time_unix_nano, + exemplars=_encode_exemplars(data_point.exemplars), + count=data_point.count, + sum=data_point.sum, + scale=data_point.scale, + zero_count=data_point.zero_count, + positive=positive, + negative=negative, + flags=data_point.flags, + max=data_point.max, + min=data_point.min, + ) + pb2_metric.exponential_histogram.aggregation_temporality = ( + metric.data.aggregation_temporality + ) + pb2_metric.exponential_histogram.data_points.append(pt) + + else: + _logger.warning( + "unsupported data type %s", + metric.data.__class__.__name__, + ) + + +def _encode_exemplars(sdk_exemplars: List[Exemplar]) -> List[pb2.Exemplar]: + """ + Converts a list of SDK Exemplars into a list of protobuf Exemplars. + + Args: + sdk_exemplars (list): The list of exemplars from the OpenTelemetry SDK. + + Returns: + list: A list of protobuf exemplars. + """ + pb_exemplars = [] + for sdk_exemplar in sdk_exemplars: + if ( + sdk_exemplar.span_id is not None + and sdk_exemplar.trace_id is not None + ): + pb_exemplar = pb2.Exemplar( + time_unix_nano=sdk_exemplar.time_unix_nano, + span_id=_encode_span_id(sdk_exemplar.span_id), + trace_id=_encode_trace_id(sdk_exemplar.trace_id), + filtered_attributes=_encode_attributes( + sdk_exemplar.filtered_attributes + ), + ) + else: + pb_exemplar = pb2.Exemplar( + time_unix_nano=sdk_exemplar.time_unix_nano, + filtered_attributes=_encode_attributes( + sdk_exemplar.filtered_attributes + ), + ) + + # Assign the value based on its type in the SDK exemplar + if isinstance(sdk_exemplar.value, float): + pb_exemplar.as_double = sdk_exemplar.value + elif isinstance(sdk_exemplar.value, int): + pb_exemplar.as_int = sdk_exemplar.value + else: + raise ValueError("Exemplar value must be an int or float") + pb_exemplars.append(pb_exemplar) + + return pb_exemplars diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentelemetry_exporter_otlp_proto_common-1.27.0/src/opentelemetry/exporter/otlp/proto/common/version/__init__.py new/opentelemetry_exporter_otlp_proto_common-1.29.0/src/opentelemetry/exporter/otlp/proto/common/version/__init__.py --- old/opentelemetry_exporter_otlp_proto_common-1.27.0/src/opentelemetry/exporter/otlp/proto/common/version/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/opentelemetry_exporter_otlp_proto_common-1.29.0/src/opentelemetry/exporter/otlp/proto/common/version/__init__.py 2020-02-02 01:00:00.000000000 +0100 @@ -0,0 +1,15 @@ +# 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. + +__version__ = "1.29.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentelemetry_exporter_otlp_proto_common-1.27.0/src/opentelemetry/exporter/otlp/proto/common/version.py new/opentelemetry_exporter_otlp_proto_common-1.29.0/src/opentelemetry/exporter/otlp/proto/common/version.py --- old/opentelemetry_exporter_otlp_proto_common-1.27.0/src/opentelemetry/exporter/otlp/proto/common/version.py 2020-02-02 01:00:00.000000000 +0100 +++ new/opentelemetry_exporter_otlp_proto_common-1.29.0/src/opentelemetry/exporter/otlp/proto/common/version.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,15 +0,0 @@ -# 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. - -__version__ = "1.27.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentelemetry_exporter_otlp_proto_common-1.27.0/tests/test_log_encoder.py new/opentelemetry_exporter_otlp_proto_common-1.29.0/tests/test_log_encoder.py --- old/opentelemetry_exporter_otlp_proto_common-1.27.0/tests/test_log_encoder.py 2020-02-02 01:00:00.000000000 +0100 +++ new/opentelemetry_exporter_otlp_proto_common-1.29.0/tests/test_log_encoder.py 2020-02-02 01:00:00.000000000 +0100 @@ -51,6 +51,18 @@ sdk_logs, expected_encoding = self.get_test_logs() self.assertEqual(encode_logs(sdk_logs), expected_encoding) + def test_encode_no_body(self): + sdk_logs, expected_encoding = self.get_test_logs() + for log in sdk_logs: + log.log_record.body = None + + for resource_log in expected_encoding.resource_logs: + for scope_log in resource_log.scope_logs: + for log_record in scope_log.log_records: + log_record.ClearField("body") + + self.assertEqual(encode_logs(sdk_logs), expected_encoding) + def test_dropped_attributes_count(self): sdk_logs = self._get_test_logs_dropped_attributes() encoded_logs = encode_logs(sdk_logs) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentelemetry_exporter_otlp_proto_common-1.27.0/tests/test_metrics_encoder.py new/opentelemetry_exporter_otlp_proto_common-1.29.0/tests/test_metrics_encoder.py --- old/opentelemetry_exporter_otlp_proto_common-1.27.0/tests/test_metrics_encoder.py 2020-02-02 01:00:00.000000000 +0100 +++ new/opentelemetry_exporter_otlp_proto_common-1.29.0/tests/test_metrics_encoder.py 2020-02-02 01:00:00.000000000 +0100 @@ -12,9 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -# pylint: disable=protected-access +# pylint: disable=protected-access,too-many-lines import unittest +from opentelemetry.exporter.otlp.proto.common._internal.metrics_encoder import ( + EncodingException, +) from opentelemetry.exporter.otlp.proto.common.metrics_encoder import ( encode_metrics, ) @@ -30,19 +33,21 @@ from opentelemetry.proto.resource.v1.resource_pb2 import ( Resource as OTLPResource, ) -from opentelemetry.sdk.metrics.export import AggregationTemporality, Buckets -from opentelemetry.sdk.metrics.export import ( - ExponentialHistogram as ExponentialHistogramType, -) -from opentelemetry.sdk.metrics.export import ExponentialHistogramDataPoint -from opentelemetry.sdk.metrics.export import Histogram as HistogramType +from opentelemetry.sdk.metrics import Exemplar from opentelemetry.sdk.metrics.export import ( + AggregationTemporality, + Buckets, + ExponentialHistogramDataPoint, HistogramDataPoint, Metric, MetricsData, ResourceMetrics, ScopeMetrics, ) +from opentelemetry.sdk.metrics.export import ( + ExponentialHistogram as ExponentialHistogramType, +) +from opentelemetry.sdk.metrics.export import Histogram as HistogramType from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.util.instrumentation import ( InstrumentationScope as SDKInstrumentationScope, @@ -51,6 +56,9 @@ class TestOTLPMetricsEncoder(unittest.TestCase): + span_id = int("6e0c63257de34c92", 16) + trace_id = int("d4cda95b652f4a1592b449d5929fda1b", 16) + histogram = Metric( name="histogram", description="foo", @@ -61,6 +69,22 @@ attributes={"a": 1, "b": True}, start_time_unix_nano=1641946016139533244, time_unix_nano=1641946016139533244, + exemplars=[ + Exemplar( + {"filtered": "banana"}, + 298.0, + 1641946016139533400, + span_id, + trace_id, + ), + Exemplar( + {"filtered": "banana"}, + 298.0, + 1641946016139533400, + None, + None, + ), + ], count=5, sum=67, bucket_counts=[1, 4], @@ -456,7 +480,34 @@ sum=67, bucket_counts=[1, 4], explicit_bounds=[10.0, 20.0], - exemplars=[], + exemplars=[ + pb2.Exemplar( + time_unix_nano=1641946016139533400, + as_double=298, + span_id=b"n\x0cc%}\xe3L\x92", + trace_id=b"\xd4\xcd\xa9[e/J\x15\x92\xb4I\xd5\x92\x9f\xda\x1b", + filtered_attributes=[ + KeyValue( + key="filtered", + value=AnyValue( + string_value="banana" + ), + ) + ], + ), + pb2.Exemplar( + time_unix_nano=1641946016139533400, + as_double=298, + filtered_attributes=[ + KeyValue( + key="filtered", + value=AnyValue( + string_value="banana" + ), + ) + ], + ), + ], max=18.0, min=8.0, ) @@ -559,7 +610,34 @@ sum=67, bucket_counts=[1, 4], explicit_bounds=[10.0, 20.0], - exemplars=[], + exemplars=[ + pb2.Exemplar( + time_unix_nano=1641946016139533400, + as_double=298, + span_id=b"n\x0cc%}\xe3L\x92", + trace_id=b"\xd4\xcd\xa9[e/J\x15\x92\xb4I\xd5\x92\x9f\xda\x1b", + filtered_attributes=[ + KeyValue( + key="filtered", + value=AnyValue( + string_value="banana" + ), + ) + ], + ), + pb2.Exemplar( + time_unix_nano=1641946016139533400, + as_double=298, + filtered_attributes=[ + KeyValue( + key="filtered", + value=AnyValue( + string_value="banana" + ), + ) + ], + ), + ], max=18.0, min=8.0, ) @@ -594,7 +672,34 @@ sum=67, bucket_counts=[1, 4], explicit_bounds=[10.0, 20.0], - exemplars=[], + exemplars=[ + pb2.Exemplar( + time_unix_nano=1641946016139533400, + as_double=298, + span_id=b"n\x0cc%}\xe3L\x92", + trace_id=b"\xd4\xcd\xa9[e/J\x15\x92\xb4I\xd5\x92\x9f\xda\x1b", + filtered_attributes=[ + KeyValue( + key="filtered", + value=AnyValue( + string_value="banana" + ), + ) + ], + ), + pb2.Exemplar( + time_unix_nano=1641946016139533400, + as_double=298, + filtered_attributes=[ + KeyValue( + key="filtered", + value=AnyValue( + string_value="banana" + ), + ) + ], + ), + ], max=18.0, min=8.0, ) @@ -636,7 +741,34 @@ sum=67, bucket_counts=[1, 4], explicit_bounds=[10.0, 20.0], - exemplars=[], + exemplars=[ + pb2.Exemplar( + time_unix_nano=1641946016139533400, + as_double=298, + span_id=b"n\x0cc%}\xe3L\x92", + trace_id=b"\xd4\xcd\xa9[e/J\x15\x92\xb4I\xd5\x92\x9f\xda\x1b", + filtered_attributes=[ + KeyValue( + key="filtered", + value=AnyValue( + string_value="banana" + ), + ) + ], + ), + pb2.Exemplar( + time_unix_nano=1641946016139533400, + as_double=298, + filtered_attributes=[ + KeyValue( + key="filtered", + value=AnyValue( + string_value="banana" + ), + ) + ], + ), + ], max=18.0, min=8.0, ) @@ -678,7 +810,34 @@ sum=67, bucket_counts=[1, 4], explicit_bounds=[10.0, 20.0], - exemplars=[], + exemplars=[ + pb2.Exemplar( + time_unix_nano=1641946016139533400, + as_double=298, + span_id=b"n\x0cc%}\xe3L\x92", + trace_id=b"\xd4\xcd\xa9[e/J\x15\x92\xb4I\xd5\x92\x9f\xda\x1b", + filtered_attributes=[ + KeyValue( + key="filtered", + value=AnyValue( + string_value="banana" + ), + ) + ], + ), + pb2.Exemplar( + time_unix_nano=1641946016139533400, + as_double=298, + filtered_attributes=[ + KeyValue( + key="filtered", + value=AnyValue( + string_value="banana" + ), + ) + ], + ), + ], max=18.0, min=8.0, ) @@ -814,3 +973,35 @@ # pylint: disable=protected-access actual = encode_metrics(metrics_data) self.assertEqual(expected, actual) + + def test_encoding_exception_reraise(self): + # this number is too big to fit in a signed 64-bit proto field and causes a ValueError + big_number = 2**63 + metrics_data = MetricsData( + resource_metrics=[ + ResourceMetrics( + resource=Resource( + attributes={}, + schema_url="resource_schema_url", + ), + scope_metrics=[ + ScopeMetrics( + scope=SDKInstrumentationScope( + name="first_name", + version="first_version", + schema_url="insrumentation_scope_schema_url", + ), + metrics=[_generate_sum("sum_double", big_number)], + schema_url="instrumentation_scope_schema_url", + ) + ], + schema_url="resource_schema_url", + ) + ] + ) + with self.assertRaises(EncodingException) as context: + encode_metrics(metrics_data) + + # assert that the EncodingException wraps the metric and original exception + assert isinstance(context.exception.metric, Metric) + assert isinstance(context.exception.original_exception, ValueError)