diff --git a/CHANGELOG.md b/CHANGELOG.md index a7788e32f2..545e52c90f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Increment the: ## [Unreleased] +* [EXPORTER] Prometheus: Adding ability to disable timestamps from metric points + [#3894](https://github.com/open-telemetry/opentelemetry-cpp/pull/3894) + * [TEST] Add multi-threaded metrics benchmarks for shared vs per-thread counter [#3865](https://github.com/open-telemetry/opentelemetry-cpp/pull/3865) diff --git a/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_options.h b/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_options.h index 9d41d268e8..82dbfa79ac 100644 --- a/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_options.h +++ b/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_options.h @@ -38,6 +38,9 @@ struct PrometheusExporterOptions // Option to export metrics without the type suffix bool without_type_suffix = false; + + // Option to export metrics without a timestamp + bool without_timestamps = 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 496ec9bd34..3fe2305107 100644 --- a/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h +++ b/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h @@ -41,7 +41,8 @@ class PrometheusExporterUtils bool populate_target_info = true, bool without_otel_scope = false, bool without_units = false, - bool without_type_suffix = false); + bool without_type_suffix = false, + bool without_timestamps = false); private: /** @@ -154,6 +155,7 @@ class PrometheusExporterUtils */ static void SetTarget(const sdk::metrics::ResourceMetrics &data, std::chrono::nanoseconds time, + bool without_timestamps, const opentelemetry::sdk::instrumentationscope::InstrumentationScope *scope, std::vector<::prometheus::MetricFamily> *output); @@ -167,6 +169,7 @@ class PrometheusExporterUtils const opentelemetry::sdk::instrumentationscope::InstrumentationScope *scope, ::prometheus::MetricType type, std::chrono::nanoseconds time, + bool without_timestamps, ::prometheus::MetricFamily *metric_family, const opentelemetry::sdk::resource::Resource *resource); @@ -181,6 +184,7 @@ class PrometheusExporterUtils const opentelemetry::sdk::metrics::PointAttributes &labels, const opentelemetry::sdk::instrumentationscope::InstrumentationScope *scope, std::chrono::nanoseconds time, + bool without_timestamps, ::prometheus::MetricFamily *metric_family, const opentelemetry::sdk::resource::Resource *resource); @@ -191,6 +195,7 @@ class PrometheusExporterUtils ::prometheus::ClientMetric &metric, const opentelemetry::sdk::metrics::PointAttributes &labels, std::chrono::nanoseconds time, + bool without_timestamps, const opentelemetry::sdk::instrumentationscope::InstrumentationScope *scope, const opentelemetry::sdk::resource::Resource *resource); diff --git a/exporters/prometheus/src/exporter_options.cc b/exporters/prometheus/src/exporter_options.cc index a713cf3c2c..e4c113b194 100644 --- a/exporters/prometheus/src/exporter_options.cc +++ b/exporters/prometheus/src/exporter_options.cc @@ -69,12 +69,23 @@ static inline bool GetPrometheusWithoutTypeSuffix() return exists ? setting : false; } +inline bool GetPrometheusWithoutTimestamps() +{ + constexpr char kPrometheusWithoutTypeSuffix[] = "OTEL_CPP_PROMETHEUS_EXPORTER_WITHOUT_TIMESTAMPS"; + 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_units(GetPrometheusWithoutUnits()), - without_type_suffix(GetPrometheusWithoutTypeSuffix()) + without_type_suffix(GetPrometheusWithoutTypeSuffix()), + without_timestamps(GetPrometheusWithoutTimestamps()) {} PrometheusExporterOptions::PrometheusExporterOptions(void *) : url("") {} diff --git a/exporters/prometheus/src/exporter_utils.cc b/exporters/prometheus/src/exporter_utils.cc index 7055e0d86c..924a63dbbc 100644 --- a/exporters/prometheus/src/exporter_utils.cc +++ b/exporters/prometheus/src/exporter_utils.cc @@ -119,7 +119,8 @@ std::vector PrometheusExporterUtils::TranslateT bool populate_target_info, bool without_otel_scope, bool without_units, - bool without_type_suffix) + bool without_type_suffix, + bool without_timestamps) { // initialize output vector @@ -140,6 +141,7 @@ std::vector PrometheusExporterUtils::TranslateT { SetTarget(data, data.scope_metric_data_.begin()->metric_data_.begin()->end_ts.time_since_epoch(), + without_timestamps, without_otel_scope ? nullptr : (*data.scope_metric_data_.begin()).scope_, &output); } @@ -185,8 +187,8 @@ std::vector PrometheusExporterUtils::TranslateT sum = static_cast(nostd::get(histogram_point_data.sum_)); } SetData(std::vector{sum, static_cast(histogram_point_data.count_)}, - boundaries, counts, point_data_attr.attributes, scope, time, &metric_family, - data.resource_); + boundaries, counts, point_data_attr.attributes, scope, time, without_timestamps, + &metric_family, data.resource_); } else if (type == prometheus_client::MetricType::Gauge) { @@ -196,16 +198,16 @@ std::vector PrometheusExporterUtils::TranslateT auto last_value_point_data = nostd::get(point_data_attr.point_data); std::vector values{last_value_point_data.value_}; - SetData(values, point_data_attr.attributes, scope, type, time, &metric_family, - data.resource_); + SetData(values, point_data_attr.attributes, scope, type, time, without_timestamps, + &metric_family, data.resource_); } else if (nostd::holds_alternative(point_data_attr.point_data)) { auto sum_point_data = nostd::get(point_data_attr.point_data); std::vector values{sum_point_data.value_}; - SetData(values, point_data_attr.attributes, scope, type, time, &metric_family, - data.resource_); + SetData(values, point_data_attr.attributes, scope, type, time, without_timestamps, + &metric_family, data.resource_); } else { @@ -221,8 +223,8 @@ std::vector PrometheusExporterUtils::TranslateT auto sum_point_data = nostd::get(point_data_attr.point_data); std::vector values{sum_point_data.value_}; - SetData(values, point_data_attr.attributes, scope, type, time, &metric_family, - data.resource_); + SetData(values, point_data_attr.attributes, scope, type, time, without_timestamps, + &metric_family, data.resource_); } else { @@ -606,6 +608,7 @@ prometheus_client::MetricType PrometheusExporterUtils::TranslateType( void PrometheusExporterUtils::SetTarget( const sdk::metrics::ResourceMetrics &data, std::chrono::nanoseconds time, + bool without_timestamps, const opentelemetry::sdk::instrumentationscope::InstrumentationScope *scope, std::vector<::prometheus::MetricFamily> *output) { @@ -624,7 +627,7 @@ void PrometheusExporterUtils::SetTarget( metric.info.value = 1.0; metric_sdk::PointAttributes empty_attributes; - SetMetricBasic(metric, empty_attributes, time, scope, data.resource_); + SetMetricBasic(metric, empty_attributes, time, without_timestamps, scope, data.resource_); for (auto &label : data.resource_->GetAttributes()) { @@ -646,12 +649,13 @@ void PrometheusExporterUtils::SetData( const opentelemetry::sdk::instrumentationscope::InstrumentationScope *scope, prometheus_client::MetricType type, std::chrono::nanoseconds time, + bool without_timestamps, prometheus_client::MetricFamily *metric_family, const opentelemetry::sdk::resource::Resource *resource) { metric_family->metric.emplace_back(); prometheus_client::ClientMetric &metric = metric_family->metric.back(); - SetMetricBasic(metric, labels, time, scope, resource); + SetMetricBasic(metric, labels, time, without_timestamps, scope, resource); SetValue(values, type, &metric); } @@ -667,12 +671,13 @@ void PrometheusExporterUtils::SetData( const metric_sdk::PointAttributes &labels, const opentelemetry::sdk::instrumentationscope::InstrumentationScope *scope, std::chrono::nanoseconds time, + bool without_timestamps, prometheus_client::MetricFamily *metric_family, const opentelemetry::sdk::resource::Resource *resource) { metric_family->metric.emplace_back(); prometheus_client::ClientMetric &metric = metric_family->metric.back(); - SetMetricBasic(metric, labels, time, scope, resource); + SetMetricBasic(metric, labels, time, without_timestamps, scope, resource); SetValue(values, boundaries, counts, &metric); } @@ -683,10 +688,15 @@ void PrometheusExporterUtils::SetMetricBasic( prometheus_client::ClientMetric &metric, const metric_sdk::PointAttributes &labels, std::chrono::nanoseconds time, + bool without_timestamps, const opentelemetry::sdk::instrumentationscope::InstrumentationScope *scope, const opentelemetry::sdk::resource::Resource *resource) { - metric.timestamp_ms = time.count() / 1000000; + if (!without_timestamps) + { + metric.timestamp_ms = time.count() / 1000000; + } + if (labels.empty() && nullptr == resource) { return; diff --git a/exporters/prometheus/test/exporter_utils_test.cc b/exporters/prometheus/test/exporter_utils_test.cc index b5dcd554fe..4ca0bec0d0 100644 --- a/exporters/prometheus/test/exporter_utils_test.cc +++ b/exporters/prometheus/test/exporter_utils_test.cc @@ -90,10 +90,10 @@ static void assert_basic(prometheus_client::MetricFamily &metric, ASSERT_EQ(metric.help, description); // description not changed ASSERT_EQ(metric.type, type); // type translated - // Prometheus metric data points should not have explicit timestamps for (const prometheus::ClientMetric &cm : metric.metric) { - ASSERT_EQ(cm.timestamp_ms, 0); + // end_ts is set as 1766662560000 + ASSERT_EQ(cm.timestamp_ms, 1766662560); } auto metric_data = metric.metric[0]; @@ -286,6 +286,48 @@ TEST(PrometheusExporterUtils, TranslateToPrometheusHistogramNormal) ASSERT_EQ(checked_label_num, 3); } +class TimestampTest : public ::testing::Test +{ + opentelemetry::sdk::resource::Resource resource_ = + opentelemetry::sdk::resource::Resource::Create({{"service.name", "test_service"}}); + +protected: + void CheckTimestamp(bool without_timestamps, metric_sdk::ResourceMetrics metrics_data) + { + metrics_data.resource_ = &resource_; + auto translated = PrometheusExporterUtils::TranslateToPrometheus( + metrics_data, false, false, false, false, without_timestamps); + + auto metric = translated[0]; + for (const prometheus::ClientMetric &cm : metric.metric) + { + if (without_timestamps) + { + // Prometheus metric data points should not have explicit timestamps + ASSERT_EQ(cm.timestamp_ms, 0); + } + else + { + // end_ts is set as 1766662560000 + ASSERT_EQ(cm.timestamp_ms, 1766662560); + } + } + } +}; + +TEST_F(TimestampTest, Timestamp) +{ + TestDataPoints dp; + // Without timestamps + CheckTimestamp(true, dp.CreateHistogramPointData()); + CheckTimestamp(true, dp.CreateSumPointData()); + CheckTimestamp(true, dp.CreateLastValuePointData()); + // With timestamps + CheckTimestamp(false, dp.CreateHistogramPointData()); + CheckTimestamp(false, dp.CreateSumPointData()); + CheckTimestamp(false, dp.CreateLastValuePointData()); +} + class SanitizeTest : public ::testing::Test { Resource resource_ = Resource::Create({}); diff --git a/exporters/prometheus/test/prometheus_test_helper.h b/exporters/prometheus/test/prometheus_test_helper.h index 1bf1f17657..852bd6a405 100644 --- a/exporters/prometheus/test/prometheus_test_helper.h +++ b/exporters/prometheus/test/prometheus_test_helper.h @@ -23,6 +23,10 @@ struct TestDataPoints Resource resource = Resource::Create(ResourceAttributes{}); nostd::unique_ptr instrumentation_scope = InstrumentationScope::Create("library_name", "1.2.0"); + opentelemetry::common::SystemTimestamp start_ts = + opentelemetry::common::SystemTimestamp{std::chrono::microseconds{1766662500000}}; + opentelemetry::common::SystemTimestamp end_ts = + opentelemetry::common::SystemTimestamp{std::chrono::microseconds{1766662560000}}; /** * Helper function to create ResourceMetrics @@ -39,8 +43,7 @@ struct TestDataPoints metric_sdk::InstrumentDescriptor{"library_name", "description", "unit", metric_sdk::InstrumentType::kCounter, metric_sdk::InstrumentValueType::kDouble}, - metric_sdk::AggregationTemporality::kDelta, opentelemetry::common::SystemTimestamp{}, - opentelemetry::common::SystemTimestamp{}, + metric_sdk::AggregationTemporality::kDelta, start_ts, end_ts, std::vector{ {metric_sdk::PointAttributes{{"a1", "b1"}}, sum_point_data}, {metric_sdk::PointAttributes{{"a2", "b2"}}, sum_point_data2}}}; @@ -67,13 +70,13 @@ struct TestDataPoints metric_sdk::InstrumentDescriptor{"library_name", "description", "unit", metric_sdk::InstrumentType::kHistogram, metric_sdk::InstrumentValueType::kDouble}, - metric_sdk::AggregationTemporality::kDelta, opentelemetry::common::SystemTimestamp{}, - opentelemetry::common::SystemTimestamp{}, + metric_sdk::AggregationTemporality::kDelta, start_ts, end_ts, std::vector{ {metric_sdk::PointAttributes{{"a1", "b1"}}, histogram_point_data}, {metric_sdk::PointAttributes{{"a2", "b2"}}, histogram_point_data2}}}; data.scope_metric_data_ = std::vector{ {instrumentation_scope.get(), std::vector{metric_data}}}; + return data; } @@ -93,8 +96,7 @@ struct TestDataPoints metric_sdk::InstrumentDescriptor{"library_name", "description", "unit", metric_sdk::InstrumentType::kCounter, metric_sdk::InstrumentValueType::kDouble}, - metric_sdk::AggregationTemporality::kDelta, opentelemetry::common::SystemTimestamp{}, - opentelemetry::common::SystemTimestamp{}, + metric_sdk::AggregationTemporality::kDelta, start_ts, end_ts, std::vector{ {metric_sdk::PointAttributes{{"a1", "b1"}}, last_value_point_data}, {metric_sdk::PointAttributes{{"a2", "b2"}}, last_value_point_data2}}}; @@ -113,8 +115,7 @@ struct TestDataPoints metric_sdk::InstrumentDescriptor{"library_name", "description", "unit", metric_sdk::InstrumentType::kCounter, metric_sdk::InstrumentValueType::kDouble}, - metric_sdk::AggregationTemporality::kDelta, opentelemetry::common::SystemTimestamp{}, - opentelemetry::common::SystemTimestamp{}, + metric_sdk::AggregationTemporality::kDelta, start_ts, end_ts, std::vector{ {metric_sdk::PointAttributes{{"a1", "b1"}}, drop_point_data}, {metric_sdk::PointAttributes{{"a2", "b2"}}, drop_point_data2}}};