diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e4507a662..36b3cc949c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,9 @@ Increment the: * [CI] Upgrade tools/vcpkg to 2025.09.17 [#3701](https://github.com/open-telemetry/opentelemetry-cpp/pull/3701) +* [CONFIGURATION] File configuration - prometheus translation + [#3715](https://github.com/open-telemetry/opentelemetry-cpp/pull/3715) + ## [1.23 2025-09-25] * [CodeHealth] Fix clang-tidy warnings part 6 diff --git a/exporters/prometheus/src/prometheus_pull_builder.cc b/exporters/prometheus/src/prometheus_pull_builder.cc index 18cf814051..245a30f38e 100644 --- a/exporters/prometheus/src/prometheus_pull_builder.cc +++ b/exporters/prometheus/src/prometheus_pull_builder.cc @@ -8,9 +8,11 @@ #include "opentelemetry/exporters/prometheus/exporter_factory.h" #include "opentelemetry/exporters/prometheus/exporter_options.h" #include "opentelemetry/exporters/prometheus/prometheus_pull_builder.h" +#include "opentelemetry/sdk/common/global_log_handler.h" #include "opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_builder.h" #include "opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_configuration.h" #include "opentelemetry/sdk/configuration/registry.h" +#include "opentelemetry/sdk/configuration/translation_strategy.h" #include "opentelemetry/sdk/metrics/metric_reader.h" #include "opentelemetry/version.h" @@ -38,8 +40,36 @@ std::unique_ptr PrometheusPullBuilder options.url = url; options.populate_target_info = true; options.without_otel_scope = model->without_scope_info; - options.without_units = model->without_units; - options.without_type_suffix = model->without_type_suffix; + + switch (model->translation_strategy) + { + case sdk::configuration::TranslationStrategy::UnderscoreEscapingWithSuffixes: + options.without_units = false; + options.without_type_suffix = false; + break; + case sdk::configuration::TranslationStrategy::UnderscoreEscapingWithoutSuffixes: + options.without_units = true; + options.without_type_suffix = true; + break; + case sdk::configuration::TranslationStrategy::NoUTF8EscapingWithSuffixes: + // FIXME: no flag to disable UnderscoreEscaping + OTEL_INTERNAL_LOG_WARN("[Prometheus Exporter] NoUTF8EscapingWithSuffixes not supported"); + options.without_units = false; + options.without_type_suffix = false; + break; + case sdk::configuration::TranslationStrategy::NoTranslation: + // FIXME: no flag to disable UnderscoreEscaping + OTEL_INTERNAL_LOG_WARN("[Prometheus Exporter] NoTranslation not supported"); + options.without_units = true; + options.without_type_suffix = true; + break; + } + + if (model->with_resource_constant_labels != nullptr) + { + // FIXME: with_resource_constant_labels + OTEL_INTERNAL_LOG_WARN("[Prometheus Exporter] with_resource_constant_labels not supported"); + } return PrometheusExporterFactory::Create(options); } diff --git a/functional/configuration/shelltests/kitchen-sink.test b/functional/configuration/shelltests/kitchen-sink.test index b4e3c1f66d..1b14dd2843 100644 --- a/functional/configuration/shelltests/kitchen-sink.test +++ b/functional/configuration/shelltests/kitchen-sink.test @@ -6,6 +6,7 @@ $ example_yaml --test --yaml shelltests/kitchen-sink.yaml | egrep -v "(observed_ MODEL PARSED [WARNING] attribute_limits not supported, ignoring [WARNING] IncludeExclude attribute processor not supported, ignoring +[WARNING] [Prometheus Exporter] with_resource_constant_labels not supported [WARNING] metric producer not supported, ignoring [WARNING] metric producer not supported, ignoring [WARNING] [Periodic Exporting Metric Reader] Invalid configuration: export_timeout_millis_ should be less than export_interval_millis_, using default values diff --git a/functional/configuration/shelltests/prometheus_translation_broken.test b/functional/configuration/shelltests/prometheus_translation_broken.test new file mode 100644 index 0000000000..29eaa94153 --- /dev/null +++ b/functional/configuration/shelltests/prometheus_translation_broken.test @@ -0,0 +1,8 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +$ example_yaml --test --yaml shelltests/prometheus_translation_broken.yaml +> +[ERROR] :9[10](164): Illegal TranslationStrategy: broken +FAILED TO PARSE MODEL +>= 1 diff --git a/functional/configuration/shelltests/prometheus_translation_broken.yaml b/functional/configuration/shelltests/prometheus_translation_broken.yaml new file mode 100644 index 0000000000..655d7b59cf --- /dev/null +++ b/functional/configuration/shelltests/prometheus_translation_broken.yaml @@ -0,0 +1,15 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +file_format: "1.0" + +meter_provider: + readers: + - pull: + exporter: + prometheus/development: + host: localhost + port: 9464 + without_scope_info: false + # Must fail, invalid + translation_strategy: broken diff --git a/functional/configuration/shelltests/prometheus_translation_no.test b/functional/configuration/shelltests/prometheus_translation_no.test new file mode 100644 index 0000000000..f263713471 --- /dev/null +++ b/functional/configuration/shelltests/prometheus_translation_no.test @@ -0,0 +1,10 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +$ example_yaml --test --yaml shelltests/prometheus_translation_no.yaml +> +MODEL PARSED +[WARNING] [Prometheus Exporter] NoTranslation not supported +[WARNING] [Prometheus Exporter] with_resource_constant_labels not supported +SDK CREATED +>= 0 diff --git a/functional/configuration/shelltests/prometheus_translation_no.yaml b/functional/configuration/shelltests/prometheus_translation_no.yaml new file mode 100644 index 0000000000..189b89c335 --- /dev/null +++ b/functional/configuration/shelltests/prometheus_translation_no.yaml @@ -0,0 +1,17 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +file_format: "1.0" + +meter_provider: + readers: + - pull: + exporter: + prometheus/development: + host: localhost + port: 9464 + without_scope_info: false + translation_strategy: NoTranslation + with_resource_constant_labels: + included: + - "not supported" diff --git a/functional/configuration/shelltests/prometheus_translation_no_utf8.test b/functional/configuration/shelltests/prometheus_translation_no_utf8.test new file mode 100644 index 0000000000..253a1e9aef --- /dev/null +++ b/functional/configuration/shelltests/prometheus_translation_no_utf8.test @@ -0,0 +1,9 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +$ example_yaml --test --yaml shelltests/prometheus_translation_no_utf8.yaml +> +MODEL PARSED +[WARNING] [Prometheus Exporter] NoUTF8EscapingWithSuffixes not supported +SDK CREATED +>= 0 diff --git a/functional/configuration/shelltests/prometheus_translation_no_utf8.yaml b/functional/configuration/shelltests/prometheus_translation_no_utf8.yaml new file mode 100644 index 0000000000..2fcd54f9ce --- /dev/null +++ b/functional/configuration/shelltests/prometheus_translation_no_utf8.yaml @@ -0,0 +1,14 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +file_format: "1.0" + +meter_provider: + readers: + - pull: + exporter: + prometheus/development: + host: localhost + port: 9464 + without_scope_info: false + translation_strategy: NoUTF8EscapingWithSuffixes diff --git a/functional/configuration/shelltests/prometheus_translation_with_suffixes.test b/functional/configuration/shelltests/prometheus_translation_with_suffixes.test new file mode 100644 index 0000000000..90d20d4258 --- /dev/null +++ b/functional/configuration/shelltests/prometheus_translation_with_suffixes.test @@ -0,0 +1,8 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +$ example_yaml --test --yaml shelltests/prometheus_translation_with_suffixes.yaml +> +MODEL PARSED +SDK CREATED +>= 0 diff --git a/functional/configuration/shelltests/prometheus_translation_with_suffixes.yaml b/functional/configuration/shelltests/prometheus_translation_with_suffixes.yaml new file mode 100644 index 0000000000..3403c89648 --- /dev/null +++ b/functional/configuration/shelltests/prometheus_translation_with_suffixes.yaml @@ -0,0 +1,14 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +file_format: "1.0" + +meter_provider: + readers: + - pull: + exporter: + prometheus/development: + host: localhost + port: 9464 + without_scope_info: false + translation_strategy: UnderscoreEscapingWithSuffixes diff --git a/functional/configuration/shelltests/prometheus_translation_without_suffixes.test b/functional/configuration/shelltests/prometheus_translation_without_suffixes.test new file mode 100644 index 0000000000..89391906fc --- /dev/null +++ b/functional/configuration/shelltests/prometheus_translation_without_suffixes.test @@ -0,0 +1,8 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +$ example_yaml --test --yaml shelltests/prometheus_translation_without_suffixes.yaml +> +MODEL PARSED +SDK CREATED +>= 0 diff --git a/functional/configuration/shelltests/prometheus_translation_without_suffixes.yaml b/functional/configuration/shelltests/prometheus_translation_without_suffixes.yaml new file mode 100644 index 0000000000..3d8e7b36f2 --- /dev/null +++ b/functional/configuration/shelltests/prometheus_translation_without_suffixes.yaml @@ -0,0 +1,14 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +file_format: "1.0" + +meter_provider: + readers: + - pull: + exporter: + prometheus/development: + host: localhost + port: 9464 + without_scope_info: false + translation_strategy: UnderscoreEscapingWithoutSuffixes diff --git a/install/test/src/test_exporters_prometheus_builder.cc b/install/test/src/test_exporters_prometheus_builder.cc index 231b526f7a..0f5781683b 100644 --- a/install/test/src/test_exporters_prometheus_builder.cc +++ b/install/test/src/test_exporters_prometheus_builder.cc @@ -11,11 +11,9 @@ TEST(ExportersPrometheusBuilderInstall, PrometheusPullBuilder) ASSERT_TRUE(builder != nullptr); opentelemetry::sdk::configuration::PrometheusPullMetricExporterConfiguration model; - model.host = "localhost"; - model.port = 1234; - model.without_scope_info = false; - model.without_units = false; - model.without_type_suffix = false; + model.host = "localhost"; + model.port = 1234; + model.without_scope_info = false; auto exporter = builder->Build(&model); ASSERT_TRUE(exporter != nullptr); diff --git a/sdk/include/opentelemetry/sdk/configuration/configuration_parser.h b/sdk/include/opentelemetry/sdk/configuration/configuration_parser.h index 3ed39aef69..eb2c7252a7 100644 --- a/sdk/include/opentelemetry/sdk/configuration/configuration_parser.h +++ b/sdk/include/opentelemetry/sdk/configuration/configuration_parser.h @@ -84,6 +84,7 @@ #include "opentelemetry/sdk/configuration/temporality_preference.h" #include "opentelemetry/sdk/configuration/trace_id_ratio_based_sampler_configuration.h" #include "opentelemetry/sdk/configuration/tracer_provider_configuration.h" +#include "opentelemetry/sdk/configuration/translation_strategy.h" #include "opentelemetry/sdk/configuration/view_configuration.h" #include "opentelemetry/sdk/configuration/view_selector_configuration.h" #include "opentelemetry/sdk/configuration/view_stream_configuration.h" @@ -171,6 +172,9 @@ class ConfigurationParser std::unique_ptr ParseConsolePushMetricExporterConfiguration(const std::unique_ptr &node) const; + TranslationStrategy ParseTranslationStrategy(const std::unique_ptr &node, + const std::string &name) const; + std::unique_ptr ParsePrometheusPullMetricExporterConfiguration(const std::unique_ptr &node) const; diff --git a/sdk/include/opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_configuration.h b/sdk/include/opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_configuration.h index 99b4fbcc85..296e55d36e 100644 --- a/sdk/include/opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_configuration.h +++ b/sdk/include/opentelemetry/sdk/configuration/prometheus_pull_metric_exporter_configuration.h @@ -6,8 +6,10 @@ #include #include "opentelemetry/sdk/configuration/headers_configuration.h" +#include "opentelemetry/sdk/configuration/include_exclude_configuration.h" #include "opentelemetry/sdk/configuration/pull_metric_exporter_configuration.h" #include "opentelemetry/sdk/configuration/pull_metric_exporter_configuration_visitor.h" +#include "opentelemetry/sdk/configuration/translation_strategy.h" #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -28,10 +30,9 @@ class PrometheusPullMetricExporterConfiguration : public PullMetricExporterConfi std::string host; std::size_t port{0}; - bool without_units{false}; - bool without_type_suffix{false}; bool without_scope_info{false}; - // FIXME: with_resource_constant_labels; + std::unique_ptr with_resource_constant_labels; + TranslationStrategy translation_strategy; }; } // namespace configuration diff --git a/sdk/include/opentelemetry/sdk/configuration/translation_strategy.h b/sdk/include/opentelemetry/sdk/configuration/translation_strategy.h new file mode 100644 index 0000000000..e6f1244b91 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/configuration/translation_strategy.h @@ -0,0 +1,28 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace configuration +{ + +// YAML-SCHEMA: schema/meter_provider.json +// YAML-NODE: ExperimentalPrometheusMetricExporter +enum class TranslationStrategy : std::uint8_t +{ + UnderscoreEscapingWithSuffixes, + UnderscoreEscapingWithoutSuffixes, + NoUTF8EscapingWithSuffixes, + NoTranslation +}; + +} // namespace configuration +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/configuration/configuration_parser.cc b/sdk/src/configuration/configuration_parser.cc index 014ce84886..1ff55fa262 100644 --- a/sdk/src/configuration/configuration_parser.cc +++ b/sdk/src/configuration/configuration_parser.cc @@ -91,6 +91,7 @@ #include "opentelemetry/sdk/configuration/temporality_preference.h" #include "opentelemetry/sdk/configuration/trace_id_ratio_based_sampler_configuration.h" #include "opentelemetry/sdk/configuration/tracer_provider_configuration.h" +#include "opentelemetry/sdk/configuration/translation_strategy.h" #include "opentelemetry/sdk/configuration/view_configuration.h" #include "opentelemetry/sdk/configuration/view_selector_configuration.h" #include "opentelemetry/sdk/configuration/view_stream_configuration.h" @@ -609,17 +610,55 @@ ConfigurationParser::ParseConsolePushMetricExporterConfiguration( return model; } +TranslationStrategy ConfigurationParser::ParseTranslationStrategy( + const std::unique_ptr &node, + const std::string &name) const +{ + if (name == "UnderscoreEscapingWithSuffixes") + { + return TranslationStrategy::UnderscoreEscapingWithSuffixes; + } + + if (name == "UnderscoreEscapingWithoutSuffixes") + { + return TranslationStrategy::UnderscoreEscapingWithoutSuffixes; + } + + if (name == "NoUTF8EscapingWithSuffixes") + { + return TranslationStrategy::NoUTF8EscapingWithSuffixes; + } + + if (name == "NoTranslation") + { + return TranslationStrategy::NoTranslation; + } + + std::string message("Illegal TranslationStrategy: "); + message.append(name); + throw InvalidSchemaException(node->Location(), message); +} + std::unique_ptr ConfigurationParser::ParsePrometheusPullMetricExporterConfiguration( const std::unique_ptr &node) const { auto model = std::make_unique(); + std::unique_ptr child; + + model->host = node->GetString("host", "localhost"); + model->port = node->GetInteger("port", 9464); + model->without_scope_info = node->GetBoolean("without_scope_info", false); + + child = node->GetChildNode("with_resource_constant_labels"); + if (child) + { + model->with_resource_constant_labels = ParseIncludeExcludeConfiguration(child); + } - model->host = node->GetString("host", "localhost"); - model->port = node->GetInteger("port", 9464); - model->without_units = node->GetBoolean("without_units", false); - model->without_type_suffix = node->GetBoolean("without_type_suffix", false); - model->without_scope_info = node->GetBoolean("without_scope_info", false); + std::string translation_strategy = + node->GetString("translation_strategy", "UnderscoreEscapingWithSuffixes"); + model->translation_strategy = ParseTranslationStrategy(node, translation_strategy); return model; } diff --git a/sdk/test/configuration/yaml_metrics_test.cc b/sdk/test/configuration/yaml_metrics_test.cc index f063c55145..296405fc89 100644 --- a/sdk/test/configuration/yaml_metrics_test.cc +++ b/sdk/test/configuration/yaml_metrics_test.cc @@ -25,6 +25,7 @@ #include "opentelemetry/sdk/configuration/pull_metric_reader_configuration.h" #include "opentelemetry/sdk/configuration/string_array_configuration.h" #include "opentelemetry/sdk/configuration/temporality_preference.h" +#include "opentelemetry/sdk/configuration/translation_strategy.h" #include "opentelemetry/sdk/configuration/view_configuration.h" #include "opentelemetry/sdk/configuration/view_selector_configuration.h" #include "opentelemetry/sdk/configuration/view_stream_configuration.h" @@ -537,9 +538,10 @@ file_format: "1.0-metrics" opentelemetry::sdk::configuration::PrometheusPullMetricExporterConfiguration *>(exporter); ASSERT_EQ(prometheus->host, "localhost"); ASSERT_EQ(prometheus->port, 9464); - ASSERT_EQ(prometheus->without_units, false); - ASSERT_EQ(prometheus->without_type_suffix, false); ASSERT_EQ(prometheus->without_scope_info, false); + ASSERT_EQ(prometheus->translation_strategy, + opentelemetry::sdk::configuration::TranslationStrategy::UnderscoreEscapingWithSuffixes); + ASSERT_EQ(prometheus->with_resource_constant_labels, nullptr); } TEST(YamlMetrics, prometheus) @@ -553,9 +555,14 @@ file_format: "1.0-metrics" prometheus/development: host: "prometheus" port: 1234 - without_units: true - without_type_suffix: true without_scope_info: true + translation_strategy: NoUTF8EscapingWithSuffixes + with_resource_constant_labels: + included: + - "foo.in" + - "bar.in" + excluded: + - "baz.ex" )"; auto config = DoParse(yaml); @@ -573,9 +580,17 @@ file_format: "1.0-metrics" opentelemetry::sdk::configuration::PrometheusPullMetricExporterConfiguration *>(exporter); ASSERT_EQ(prometheus->host, "prometheus"); ASSERT_EQ(prometheus->port, 1234); - ASSERT_EQ(prometheus->without_units, true); - ASSERT_EQ(prometheus->without_type_suffix, true); ASSERT_EQ(prometheus->without_scope_info, true); + ASSERT_EQ(prometheus->translation_strategy, + opentelemetry::sdk::configuration::TranslationStrategy::NoUTF8EscapingWithSuffixes); + ASSERT_NE(prometheus->with_resource_constant_labels, nullptr); + ASSERT_NE(prometheus->with_resource_constant_labels->included, nullptr); + ASSERT_EQ(prometheus->with_resource_constant_labels->included->string_array.size(), 2); + ASSERT_EQ(prometheus->with_resource_constant_labels->included->string_array[0], "foo.in"); + ASSERT_EQ(prometheus->with_resource_constant_labels->included->string_array[1], "bar.in"); + ASSERT_NE(prometheus->with_resource_constant_labels->excluded, nullptr); + ASSERT_EQ(prometheus->with_resource_constant_labels->excluded->string_array.size(), 1); + ASSERT_EQ(prometheus->with_resource_constant_labels->excluded->string_array[0], "baz.ex"); } TEST(YamlMetrics, empty_views)