From 39f0f822a1bcdbac9f9d766ed16de18f5a4b6f73 Mon Sep 17 00:00:00 2001 From: Clemens Korner Date: Fri, 9 Feb 2024 02:24:33 +0100 Subject: [PATCH 1/4] Prometheus Exporter string representation for target_info labels (#3659) * Prometheus Exporter fix test_target_info_enabled_by_default test_target_info_enabled_by_default contained some dead code due to a wrong list slicling * Prometheus exporter string representation for target_info labels --------- Co-authored-by: Srikanth Chekuri Co-authored-by: Diego Hurtado --- CHANGELOG.md | 2 + .../exporter/prometheus/__init__.py | 3 +- .../tests/test_prometheus_exporter.py | 45 ++++++++++++------- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6106b889b17..7121b281cc4 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 +- Prometheus Exporter string representation for target_info labels + ([#3659](https://github.com/open-telemetry/opentelemetry-python/pull/3659)) - Logs: ObservedTimestamp field is missing in console exporter output ([#3564](https://github.com/open-telemetry/opentelemetry-python/pull/3564)) - Fix explicit bucket histogram aggregation 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 4c903297788..b1a8d668c46 100644 --- a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py +++ b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py @@ -380,7 +380,8 @@ def _create_info_metric( """Create an Info Metric Family with list of attributes""" # sanitize the attribute names according to Prometheus rule attributes = { - self._sanitize(key): value for key, value in attributes.items() + self._sanitize(key): self._check_value(value) + for key, value in attributes.items() } info = InfoMetricFamily(name, description, labels=attributes) info.add_metric(labels=list(attributes.keys()), value=attributes) diff --git a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py index db920a5c734..6d9126c815f 100644 --- a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py +++ b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py @@ -351,30 +351,33 @@ def test_target_info_enabled_by_default(self): metric_reader = PrometheusMetricReader() provider = MeterProvider( metric_readers=[metric_reader], - resource=Resource({"os": "Unix", "histo": 1}), + resource=Resource({"os": "Unix", "version": "1.2.3"}), ) meter = provider.get_meter("getting-started", "0.1.2") counter = meter.create_counter("counter") counter.add(1) result = list(metric_reader._collector.collect()) - for prometheus_metric in result[:0]: - self.assertEqual(type(prometheus_metric), InfoMetricFamily) - self.assertEqual(prometheus_metric.name, "target") - self.assertEqual( - prometheus_metric.documentation, "Target metadata" - ) - self.assertTrue(len(prometheus_metric.samples) == 1) - self.assertEqual(prometheus_metric.samples[0].value, 1) - self.assertTrue(len(prometheus_metric.samples[0].labels) == 2) - self.assertEqual(prometheus_metric.samples[0].labels["os"], "Unix") - self.assertEqual(prometheus_metric.samples[0].labels["histo"], "1") + self.assertEqual(len(result), 2) + + prometheus_metric = result[0] + + self.assertEqual(type(prometheus_metric), InfoMetricFamily) + self.assertEqual(prometheus_metric.name, "target") + self.assertEqual(prometheus_metric.documentation, "Target metadata") + self.assertTrue(len(prometheus_metric.samples) == 1) + self.assertEqual(prometheus_metric.samples[0].value, 1) + self.assertTrue(len(prometheus_metric.samples[0].labels) == 2) + self.assertEqual(prometheus_metric.samples[0].labels["os"], "Unix") + self.assertEqual( + prometheus_metric.samples[0].labels["version"], "1.2.3" + ) def test_target_info_disabled(self): metric_reader = PrometheusMetricReader(disable_target_info=True) provider = MeterProvider( metric_readers=[metric_reader], - resource=Resource({"os": "Unix", "histo": 1}), + resource=Resource({"os": "Unix", "version": "1.2.3"}), ) meter = provider.get_meter("getting-started", "0.1.2") counter = meter.create_counter("counter") @@ -388,7 +391,7 @@ def test_target_info_disabled(self): prometheus_metric.documentation, "Target metadata" ) self.assertNotIn("os", prometheus_metric.samples[0].labels) - self.assertNotIn("histo", prometheus_metric.samples[0].labels) + self.assertNotIn("version", prometheus_metric.samples[0].labels) def test_target_info_sanitize(self): metric_reader = PrometheusMetricReader() @@ -398,6 +401,8 @@ def test_target_info_sanitize(self): { "system.os": "Unix", "system.name": "Prometheus Target Sanitize", + "histo": 1, + "ratio": 0.1, } ), ) @@ -411,7 +416,7 @@ def test_target_info_sanitize(self): self.assertEqual(prometheus_metric.documentation, "Target metadata") self.assertTrue(len(prometheus_metric.samples) == 1) self.assertEqual(prometheus_metric.samples[0].value, 1) - self.assertTrue(len(prometheus_metric.samples[0].labels) == 2) + self.assertTrue(len(prometheus_metric.samples[0].labels) == 4) self.assertTrue("system_os" in prometheus_metric.samples[0].labels) self.assertEqual( prometheus_metric.samples[0].labels["system_os"], "Unix" @@ -421,3 +426,13 @@ def test_target_info_sanitize(self): prometheus_metric.samples[0].labels["system_name"], "Prometheus Target Sanitize", ) + self.assertTrue("histo" in prometheus_metric.samples[0].labels) + self.assertEqual( + prometheus_metric.samples[0].labels["histo"], + "1", + ) + self.assertTrue("ratio" in prometheus_metric.samples[0].labels) + self.assertEqual( + prometheus_metric.samples[0].labels["ratio"], + "0.1", + ) From 3500f5c03c019d4208ef9821453a1def38e406a0 Mon Sep 17 00:00:00 2001 From: Garry Cairns <2401853+garry-cairns@users.noreply.github.com> Date: Fri, 9 Feb 2024 01:41:59 +0000 Subject: [PATCH 2/4] Add ProxyLoggerProvider, ProxyLogger and associated tests (#3575) * 3060 adds ProxyLoggerProvider, ProxyLogger and associated tests * Updates changelog for 3060 (add proxy classes for logging) * Removes proxy classes from module level log import following review * Moves changelog entry to unreleased * Fixes build failures * Fix mypy and lint --------- --- CHANGELOG.md | 2 + .../opentelemetry/_logs/_internal/__init__.py | 61 +++++++++++++++--- .../tests/logs/test_logger_provider.py | 2 +- opentelemetry-api/tests/logs/test_proxy.py | 63 +++++++++++++++++++ .../src/opentelemetry/test/globals_test.py | 17 ++++- 5 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 opentelemetry-api/tests/logs/test_proxy.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 7121b281cc4..2278dcbbacd 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 ([#3623](https://github.com/open-telemetry/opentelemetry-python/pull/3623)) - Improve Resource Detector timeout messaging ([#3645](https://github.com/open-telemetry/opentelemetry-python/pull/3645)) +- Add Proxy classes for logging + ([#3575](https://github.com/open-telemetry/opentelemetry-python/pull/3575)) ## Version 1.22.0/0.43b0 (2023-12-15) diff --git a/opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py b/opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py index e67f28439b6..156b2598e47 100644 --- a/opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py +++ b/opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py @@ -112,6 +112,37 @@ def emit(self, record: "LogRecord") -> None: pass +class ProxyLogger(Logger): + def __init__( # pylint: disable=super-init-not-called + self, + name: str, + version: Optional[str] = None, + schema_url: Optional[str] = None, + ): + self._name = name + self._version = version + self._schema_url = schema_url + self._real_logger: Optional[Logger] = None + self._noop_logger = NoOpLogger(name) + + @property + def _logger(self) -> Logger: + if self._real_logger: + return self._real_logger + + if _LOGGER_PROVIDER: + self._real_logger = _LOGGER_PROVIDER.get_logger( + self._name, + self._version, + self._schema_url, + ) + return self._real_logger + return self._noop_logger + + def emit(self, record: LogRecord) -> None: + self._logger.emit(record) + + class LoggerProvider(ABC): """ LoggerProvider is the entry point of the API. It provides access to Logger instances. @@ -166,21 +197,37 @@ def get_logger( return NoOpLogger(name, version=version, schema_url=schema_url) -# TODO: ProxyLoggerProvider +class ProxyLoggerProvider(LoggerProvider): + def get_logger( + self, + name: str, + version: Optional[str] = None, + schema_url: Optional[str] = None, + ) -> Logger: + if _LOGGER_PROVIDER: + return _LOGGER_PROVIDER.get_logger( + name, + version=version, + schema_url=schema_url, + ) + return ProxyLogger( + name, + version=version, + schema_url=schema_url, + ) _LOGGER_PROVIDER_SET_ONCE = Once() -_LOGGER_PROVIDER = None +_LOGGER_PROVIDER: Optional[LoggerProvider] = None +_PROXY_LOGGER_PROVIDER = ProxyLoggerProvider() def get_logger_provider() -> LoggerProvider: """Gets the current global :class:`~.LoggerProvider` object.""" - global _LOGGER_PROVIDER # pylint: disable=global-statement + global _LOGGER_PROVIDER # pylint: disable=global-variable-not-assigned if _LOGGER_PROVIDER is None: if _OTEL_PYTHON_LOGGER_PROVIDER not in environ: - # TODO: return proxy - _LOGGER_PROVIDER = NoOpLoggerProvider() - return _LOGGER_PROVIDER + return _PROXY_LOGGER_PROVIDER logger_provider: LoggerProvider = _load_provider( # type: ignore _OTEL_PYTHON_LOGGER_PROVIDER, "logger_provider" @@ -194,7 +241,7 @@ def get_logger_provider() -> LoggerProvider: def _set_logger_provider(logger_provider: LoggerProvider, log: bool) -> None: def set_lp() -> None: global _LOGGER_PROVIDER # pylint: disable=global-statement - _LOGGER_PROVIDER = logger_provider # type: ignore + _LOGGER_PROVIDER = logger_provider did_set = _LOGGER_PROVIDER_SET_ONCE.do_once(set_lp) diff --git a/opentelemetry-api/tests/logs/test_logger_provider.py b/opentelemetry-api/tests/logs/test_logger_provider.py index 5943924bd8e..84f3f13e9c3 100644 --- a/opentelemetry-api/tests/logs/test_logger_provider.py +++ b/opentelemetry-api/tests/logs/test_logger_provider.py @@ -44,7 +44,7 @@ def test_get_logger_provider(self): assert logs_internal._LOGGER_PROVIDER is None assert isinstance( - get_logger_provider(), logs_internal.NoOpLoggerProvider + get_logger_provider(), logs_internal.ProxyLoggerProvider ) logs_internal._LOGGER_PROVIDER = None diff --git a/opentelemetry-api/tests/logs/test_proxy.py b/opentelemetry-api/tests/logs/test_proxy.py new file mode 100644 index 00000000000..d8fa0a9743c --- /dev/null +++ b/opentelemetry-api/tests/logs/test_proxy.py @@ -0,0 +1,63 @@ +# 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=W0212,W0222,W0221 +import typing +import unittest + +import opentelemetry._logs._internal as _logs_internal +from opentelemetry import _logs +from opentelemetry.sdk._logs import LogRecord # type: ignore +from opentelemetry.test.globals_test import LoggingGlobalsTest + + +class TestProvider(_logs.NoOpLoggerProvider): + def get_logger( + self, + name: str, + version: typing.Optional[str] = None, + schema_url: typing.Optional[str] = None, + ) -> _logs.Logger: + return TestLogger(name) + + +class TestLogger(_logs.NoOpLogger): + def emit(self, *args, **kwargs): + return LogRecord(timestamp=0) + + +class TestProxy(LoggingGlobalsTest, unittest.TestCase): + def test_proxy_logger(self): + provider = _logs.get_logger_provider() + # proxy provider + self.assertIsInstance(provider, _logs_internal.ProxyLoggerProvider) + + # provider returns proxy logger + logger = provider.get_logger("proxy-test") + self.assertIsInstance(logger, _logs_internal.ProxyLogger) + + # set a real provider + _logs.set_logger_provider(TestProvider()) + + # get_logger_provider() now returns the real provider + self.assertIsInstance(_logs.get_logger_provider(), TestProvider) + + # logger provider now returns real instance + self.assertIsInstance( + _logs.get_logger_provider().get_logger("fresh"), TestLogger + ) + + # references to the old provider still work but return real logger now + real_logger = provider.get_logger("proxy-test") + self.assertIsInstance(real_logger, TestLogger) diff --git a/tests/opentelemetry-test-utils/src/opentelemetry/test/globals_test.py b/tests/opentelemetry-test-utils/src/opentelemetry/test/globals_test.py index 23b3112430d..ed210f27973 100644 --- a/tests/opentelemetry-test-utils/src/opentelemetry/test/globals_test.py +++ b/tests/opentelemetry-test-utils/src/opentelemetry/test/globals_test.py @@ -42,7 +42,7 @@ def reset_logging_globals() -> None: """WARNING: only use this for tests.""" logging_api._LOGGER_PROVIDER_SET_ONCE = Once() # type: ignore[attr-defined] logging_api._LOGGER_PROVIDER = None # type: ignore[attr-defined] - # logging_api._PROXY_LOGGER_PROVIDER = _ProxyLoggerProvider() # type: ignore[attr-defined] + logging_api._PROXY_LOGGER_PROVIDER = logging_api.ProxyLoggerProvider() # type: ignore[attr-defined] class TraceGlobalsTest(unittest.TestCase): @@ -73,3 +73,18 @@ def setUp(self) -> None: def tearDown(self) -> None: super().tearDown() reset_metrics_globals() + + +class LoggingGlobalsTest(unittest.TestCase): + """Resets logging API globals in setUp/tearDown + + Use as a base class or mixin for your test that modifies logging API globals. + """ + + def setUp(self) -> None: + super().setUp() + reset_logging_globals() + + def tearDown(self) -> None: + super().tearDown() + reset_logging_globals() From d829e375202d0106d45d7a2441409be82086b423 Mon Sep 17 00:00:00 2001 From: John Huang Date: Sat, 10 Feb 2024 11:18:08 +0800 Subject: [PATCH 3/4] Set Severity Text = WARN as defined (#3566) * Set Severity Text = WARN as defined * Fix warning text * Add CHANGELOG * Fix lint --------- Co-authored-by: Diego Hurtado --- CHANGELOG.md | 2 ++ .../opentelemetry/sdk/_logs/_internal/__init__.py | 9 ++++++++- opentelemetry-sdk/tests/logs/test_export.py | 12 ++++++------ .../tests/logs/test_multi_log_processor.py | 4 ++-- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2278dcbbacd..62fb27fbfce 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 +- Fix loglevel warning text + ([#3566](https://github.com/open-telemetry/opentelemetry-python/pull/3566)) - Prometheus Exporter string representation for target_info labels ([#3659](https://github.com/open-telemetry/opentelemetry-python/pull/3659)) - Logs: ObservedTimestamp field is missing in console exporter output diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py index 046025754b3..ab9e69ba6d6 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py @@ -521,12 +521,19 @@ def _translate(self, record: logging.LogRecord) -> LogRecord: body = record.msg % record.args else: body = record.msg + + # related to https://github.com/open-telemetry/opentelemetry-python/issues/3548 + # Severity Text = WARN as defined in https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#displaying-severity. + level_name = ( + "WARN" if record.levelname == "WARNING" else record.levelname + ) + return LogRecord( timestamp=timestamp, trace_id=span_context.trace_id, span_id=span_context.span_id, trace_flags=span_context.trace_flags, - severity_text=record.levelname, + severity_text=level_name, severity_number=severity_number, body=body, resource=self._logger.resource, diff --git a/opentelemetry-sdk/tests/logs/test_export.py b/opentelemetry-sdk/tests/logs/test_export.py index 2828504eaa9..d48dcf8e242 100644 --- a/opentelemetry-sdk/tests/logs/test_export.py +++ b/opentelemetry-sdk/tests/logs/test_export.py @@ -67,7 +67,7 @@ def test_simple_log_record_processor_default_level(self): self.assertEqual(len(finished_logs), 1) warning_log_record = finished_logs[0].log_record self.assertEqual(warning_log_record.body, "Something is wrong") - self.assertEqual(warning_log_record.severity_text, "WARNING") + self.assertEqual(warning_log_record.severity_text, "WARN") self.assertEqual( warning_log_record.severity_number, SeverityNumber.WARN ) @@ -122,7 +122,7 @@ def test_simple_log_record_processor_trace_correlation(self): self.assertEqual(len(finished_logs), 1) log_record = finished_logs[0].log_record self.assertEqual(log_record.body, "Warning message") - self.assertEqual(log_record.severity_text, "WARNING") + self.assertEqual(log_record.severity_text, "WARN") self.assertEqual(log_record.severity_number, SeverityNumber.WARN) self.assertEqual(log_record.trace_id, INVALID_SPAN_CONTEXT.trace_id) self.assertEqual(log_record.span_id, INVALID_SPAN_CONTEXT.span_id) @@ -162,7 +162,7 @@ def test_simple_log_record_processor_shutdown(self): self.assertEqual(len(finished_logs), 1) warning_log_record = finished_logs[0].log_record self.assertEqual(warning_log_record.body, "Something is wrong") - self.assertEqual(warning_log_record.severity_text, "WARNING") + self.assertEqual(warning_log_record.severity_text, "WARN") self.assertEqual( warning_log_record.severity_number, SeverityNumber.WARN ) @@ -192,13 +192,13 @@ def test_simple_log_record_processor_different_msg_types(self): finished_logs = exporter.get_finished_logs() expected = [ - ("warning message: possible upcoming heatwave", "WARNING"), + ("warning message: possible upcoming heatwave", "WARN"), ("Very high rise in temperatures across the globe", "ERROR"), ( "Temperature hits high 420 C in Hyderabad", "CRITICAL", ), - (["list", "of", "strings"], "WARNING"), + (["list", "of", "strings"], "WARN"), ({"key": "value"}, "ERROR"), ] emitted = [ @@ -367,7 +367,7 @@ def test_shutdown(self): finished_logs = exporter.get_finished_logs() expected = [ - ("warning message: possible upcoming heatwave", "WARNING"), + ("warning message: possible upcoming heatwave", "WARN"), ("Very high rise in temperatures across the globe", "ERROR"), ( "Temperature hits high 420 C in Hyderabad", diff --git a/opentelemetry-sdk/tests/logs/test_multi_log_processor.py b/opentelemetry-sdk/tests/logs/test_multi_log_processor.py index 7f4bbc32c15..110fedb9578 100644 --- a/opentelemetry-sdk/tests/logs/test_multi_log_processor.py +++ b/opentelemetry-sdk/tests/logs/test_multi_log_processor.py @@ -82,7 +82,7 @@ def test_log_record_processor(self): logger.error("Some error message") expected_list_1 = [ - ("Brace yourself", "WARNING"), + ("Brace yourself", "WARN"), ("Some error message", "ERROR"), ] self.assertEqual(logs_list_1, expected_list_1) @@ -107,7 +107,7 @@ def _get_multi_log_record_processor(self): def make_record(self): return LogRecord( timestamp=1622300111608942000, - severity_text="WARNING", + severity_text="WARN", severity_number=SeverityNumber.WARN, body="Warning message", ) From e98af82ff0ebe7e687fda265093aa576cd9ba80f Mon Sep 17 00:00:00 2001 From: Maksim Zayakin Date: Sat, 10 Feb 2024 10:07:36 +0500 Subject: [PATCH 4/4] Fix: Add missing Resource SchemaURL in otlp exporters (#3652) * fix(otlp-exporter): add missing resource schema url * test(otlp-exporter): add resource schema url to tests * chore: update changelog --- CHANGELOG.md | 2 ++ .../proto/common/_internal/_log_encoder/__init__.py | 1 + .../common/_internal/metrics_encoder/__init__.py | 1 + .../proto/common/_internal/trace_encoder/__init__.py | 1 + .../tests/test_log_encoder.py | 11 +++++++++-- .../tests/test_metrics_encoder.py | 7 +++++++ .../tests/test_trace_encoder.py | 5 +++-- 7 files changed, 24 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62fb27fbfce..af84693970d 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 +- Add missing Resource SchemaURL in OTLP exporters + ([#3652](https://github.com/open-telemetry/opentelemetry-python/pull/3652)) - Fix loglevel warning text ([#3566](https://github.com/open-telemetry/opentelemetry-python/pull/3566)) - Prometheus Exporter string representation for target_info labels diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py index 47c254033bc..c664e3ba883 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/_log_encoder/__init__.py @@ -77,6 +77,7 @@ def _encode_resource_logs(batch: Sequence[LogData]) -> List[ResourceLogs]: ResourceLogs( resource=_encode_resource(sdk_resource), scope_logs=scope_logs, + schema_url=sdk_resource.schema_url, ) ) diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py index d604786108c..ecd20b8145a 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/metrics_encoder/__init__.py @@ -312,6 +312,7 @@ def encode_metrics(data: MetricsData) -> ExportMetricsServiceRequest: attributes=_encode_attributes(sdk_resource.attributes) ), scope_metrics=scope_data.values(), + schema_url=sdk_resource.schema_url, ) ) resource_metrics = resource_data diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py index 46cf628dd13..99498c93ef7 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/trace_encoder/__init__.py @@ -97,6 +97,7 @@ def _encode_resource_spans( PB2ResourceSpans( resource=_encode_resource(sdk_resource), scope_spans=scope_spans, + schema_url=sdk_resource.schema_url, ) ) diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_log_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_log_encoder.py index 1fdb1977bab..0731bc5125d 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_log_encoder.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_log_encoder.py @@ -75,7 +75,10 @@ def _get_sdk_log_data() -> List[LogData]: 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"}), + resource=SDKResource( + {"first_resource": "value"}, + "resource_schema_url", + ), attributes={"a": 1, "b": "c"}, ), instrumentation_scope=InstrumentationScope( @@ -124,7 +127,10 @@ def _get_sdk_log_data() -> List[LogData]: severity_text="INFO", severity_number=SeverityNumber.INFO, body="Love is the one thing that transcends time and space", - resource=SDKResource({"first_resource": "value"}), + resource=SDKResource( + {"first_resource": "value"}, + "resource_schema_url", + ), attributes={"filename": "model.py", "func_name": "run_method"}, ), instrumentation_scope=InstrumentationScope( @@ -206,6 +212,7 @@ def get_test_logs( ], ), ], + schema_url="resource_schema_url", ), PB2ResourceLogs( resource=PB2Resource( diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_metrics_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_metrics_encoder.py index 69e7cda39fb..70863bb44a7 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_metrics_encoder.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_metrics_encoder.py @@ -99,6 +99,7 @@ def test_encode_sum_int(self): expected = ExportMetricsServiceRequest( resource_metrics=[ pb2.ResourceMetrics( + schema_url="resource_schema_url", resource=OTLPResource( attributes=[ KeyValue(key="a", value=AnyValue(int_value=1)), @@ -178,6 +179,7 @@ def test_encode_sum_double(self): expected = ExportMetricsServiceRequest( resource_metrics=[ pb2.ResourceMetrics( + schema_url="resource_schema_url", resource=OTLPResource( attributes=[ KeyValue(key="a", value=AnyValue(int_value=1)), @@ -257,6 +259,7 @@ def test_encode_gauge_int(self): expected = ExportMetricsServiceRequest( resource_metrics=[ pb2.ResourceMetrics( + schema_url="resource_schema_url", resource=OTLPResource( attributes=[ KeyValue(key="a", value=AnyValue(int_value=1)), @@ -333,6 +336,7 @@ def test_encode_gauge_double(self): expected = ExportMetricsServiceRequest( resource_metrics=[ pb2.ResourceMetrics( + schema_url="resource_schema_url", resource=OTLPResource( attributes=[ KeyValue(key="a", value=AnyValue(int_value=1)), @@ -409,6 +413,7 @@ def test_encode_histogram(self): expected = ExportMetricsServiceRequest( resource_metrics=[ pb2.ResourceMetrics( + schema_url="resource_schema_url", resource=OTLPResource( attributes=[ KeyValue(key="a", value=AnyValue(int_value=1)), @@ -511,6 +516,7 @@ def test_encode_multiple_scope_histogram(self): expected = ExportMetricsServiceRequest( resource_metrics=[ pb2.ResourceMetrics( + schema_url="resource_schema_url", resource=OTLPResource( attributes=[ KeyValue(key="a", value=AnyValue(int_value=1)), @@ -739,6 +745,7 @@ def test_encode_exponential_histogram(self): expected = ExportMetricsServiceRequest( resource_metrics=[ pb2.ResourceMetrics( + schema_url="resource_schema_url", resource=OTLPResource( attributes=[ KeyValue(key="a", value=AnyValue(int_value=1)), diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py index c0a05483f10..19e3adb98ea 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py @@ -111,7 +111,7 @@ def get_exhaustive_otel_span_list() -> List[SDKSpan]: links=( SDKLink(context=other_context, attributes={"key_bool": True}), ), - resource=SDKResource({}), + resource=SDKResource({}, "resource_schema_url"), ) span1.start(start_time=start_times[0]) span1.set_attribute("key_bool", False) @@ -143,7 +143,7 @@ def get_exhaustive_otel_span_list() -> List[SDKSpan]: name="test-span-4", context=other_context, parent=None, - resource=SDKResource({}), + resource=SDKResource({}, "resource_schema_url"), instrumentation_scope=SDKInstrumentationScope( name="name", version="version" ), @@ -163,6 +163,7 @@ def get_exhaustive_test_spans( pb2_service_request = PB2ExportTraceServiceRequest( resource_spans=[ PB2ResourceSpans( + schema_url="resource_schema_url", resource=PB2Resource(), scope_spans=[ PB2ScopeSpans(