diff --git a/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_options.h b/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_options.h index 5d8b4932fc..da9d124029 100644 --- a/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_options.h +++ b/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_options.h @@ -28,6 +28,12 @@ struct PrometheusExporterOptions // Populating otel_scope_name/otel_scope_labels attributes bool without_otel_scope = false; + + // Option to export metrics without the unit suffix + bool without_units = false; + + // Option to export metrics without the type suffix + bool without_type_suffix = false; }; } // namespace metrics diff --git a/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h b/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h index ccf3d03ff3..141eca011c 100644 --- a/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h +++ b/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h @@ -30,12 +30,18 @@ class PrometheusExporterUtils * @param populate_target_info whether to populate target_info * @param without_otel_scope whether to populate otel_scope_name and otel_scope_version * attributes + * @param without_units exporter configuration controlling whether to append unit suffix in + * the exported metrics. + * @param without_type_suffix exporter configuration controlling whether to append type suffix in + * the exported metrics. * @return a collection of translated metrics that is acceptable by Prometheus */ static std::vector<::prometheus::MetricFamily> TranslateToPrometheus( const sdk::metrics::ResourceMetrics &data, bool populate_target_info = true, - bool without_otel_scope = false); + bool without_otel_scope = false, + bool without_units = false, + bool without_type_suffix = false); private: /** @@ -61,7 +67,9 @@ class PrometheusExporterUtils static std::string MapToPrometheusName(const std::string &name, const std::string &unit, - ::prometheus::MetricType prometheus_type); + ::prometheus::MetricType prometheus_type, + bool without_units, + bool without_type_suffix); /** * A utility function that returns the equivalent Prometheus name for the provided OTLP metric diff --git a/exporters/prometheus/src/exporter_options.cc b/exporters/prometheus/src/exporter_options.cc index f2c49f7a57..4d75f4928f 100644 --- a/exporters/prometheus/src/exporter_options.cc +++ b/exporters/prometheus/src/exporter_options.cc @@ -48,10 +48,34 @@ inline bool GetPrometheusPopulateTargetInfo() return exists ? setting : true; } +inline bool GetPrometheusWithoutUnits() +{ + constexpr char kPrometheusWithoutUnits[] = + "OTEL_CPP_PROMETHEUS_EXPORTER_WITHOUT_UNITS"; + bool setting; + const auto exists = + opentelemetry::sdk::common::GetBoolEnvironmentVariable(kPrometheusWithoutUnits, setting); + + return exists ? setting : false; +} + +inline bool GetPrometheusWithoutTypeSuffix() +{ + constexpr char kPrometheusWithoutTypeSuffix[] = + "OTEL_CPP_PROMETHEUS_EXPORTER_WITHOUT_TYPE_SUFFIX"; + bool setting; + const auto exists = + opentelemetry::sdk::common::GetBoolEnvironmentVariable(kPrometheusWithoutTypeSuffix, setting); + + return exists ? setting : false; +} + PrometheusExporterOptions::PrometheusExporterOptions() : url(GetPrometheusDefaultHttpEndpoint()), populate_target_info(GetPrometheusPopulateTargetInfo()), - without_otel_scope(GetPrometheusWithoutOtelScope()) + without_otel_scope(GetPrometheusWithoutOtelScope()), + without_units(GetPrometheusWithoutUnits()), + without_type_suffix(GetPrometheusWithoutTypeSuffix()) {} } // namespace metrics diff --git a/exporters/prometheus/src/exporter_utils.cc b/exporters/prometheus/src/exporter_utils.cc index 77fcd56ad2..cfbce8cd79 100644 --- a/exporters/prometheus/src/exporter_utils.cc +++ b/exporters/prometheus/src/exporter_utils.cc @@ -102,13 +102,15 @@ std::string SanitizeLabel(std::string label_key) * Helper function to convert OpenTelemetry metrics data collection * to Prometheus metrics data collection * - * @param records a collection of metrics in OpenTelemetry + * @param data a collection of metrics in OpenTelemetry * @return a collection of translated metrics that is acceptable by Prometheus */ std::vector PrometheusExporterUtils::TranslateToPrometheus( const sdk::metrics::ResourceMetrics &data, bool populate_target_info, - bool without_otel_scope) + bool without_otel_scope, + bool without_units, + bool without_type_suffix) { // initialize output vector @@ -150,7 +152,9 @@ std::vector PrometheusExporterUtils::TranslateT } const prometheus_client::MetricType type = TranslateType(kind, is_monotonic); metric_family.name = MapToPrometheusName(metric_data.instrument_descriptor.name_, - metric_data.instrument_descriptor.unit_, type); + metric_data.instrument_descriptor.unit_, type, + without_units, without_type_suffix); + // TODO (psx95): Add tests to check compliance metric_family.type = type; const opentelemetry::sdk::instrumentationscope::InstrumentationScope *scope = without_otel_scope ? nullptr : instrumentation_info.scope_; @@ -492,10 +496,19 @@ std::string PrometheusExporterUtils::CleanUpString(const std::string &str) std::string PrometheusExporterUtils::MapToPrometheusName( const std::string &name, const std::string &unit, - prometheus_client::MetricType prometheus_type) + prometheus_client::MetricType prometheus_type, + bool without_units, + bool without_type_suffix) { auto sanitized_name = SanitizeNames(name); - std::string prometheus_equivalent_unit = GetEquivalentPrometheusUnit(unit); + std::string prometheus_equivalent_unit; + if (without_units) + { + prometheus_equivalent_unit = ""; + } else + { + prometheus_equivalent_unit = GetEquivalentPrometheusUnit(unit); + } // Append prometheus unit if not null or empty. if (!prometheus_equivalent_unit.empty() && @@ -505,7 +518,7 @@ std::string PrometheusExporterUtils::MapToPrometheusName( } // Special case - counter - if (prometheus_type == prometheus_client::MetricType::Counter) + if (prometheus_type == prometheus_client::MetricType::Counter && !without_type_suffix) { auto t_pos = sanitized_name.rfind("_total"); bool ends_with_total = t_pos == sanitized_name.size() - 6; @@ -517,7 +530,7 @@ std::string PrometheusExporterUtils::MapToPrometheusName( // Special case - gauge if (unit == "1" && prometheus_type == prometheus_client::MetricType::Gauge && - sanitized_name.find("ratio") == std::string::npos) + sanitized_name.find("ratio") == std::string::npos && !without_type_suffix) { sanitized_name += "_ratio"; } diff --git a/exporters/prometheus/test/exporter_utils_test.cc b/exporters/prometheus/test/exporter_utils_test.cc index 7ad5eee1fd..801909f1b4 100644 --- a/exporters/prometheus/test/exporter_utils_test.cc +++ b/exporters/prometheus/test/exporter_utils_test.cc @@ -56,7 +56,7 @@ class SanitizeNameTester const std::string &unit, prometheus_client::MetricType prometheus_type) { - return PrometheusExporterUtils::MapToPrometheusName(name, unit, prometheus_type); + return PrometheusExporterUtils::MapToPrometheusName(name, unit, prometheus_type, false, false); } }; } // namespace metrics