Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EXPORTER] Export resource for prometheus #2301

Merged
merged 34 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
dfefb44
Export resource for prometheus
owent Sep 8, 2023
ca3286a
Fix unit test in prometheus, fix resouce exporting for prometheus exp…
owent Sep 9, 2023
1436368
Fix constructor
owent Sep 9, 2023
22b8c94
Fix compiling problem
owent Sep 9, 2023
3b15502
Fix invalid resource pointer in unit test of prometheus
owent Sep 9, 2023
1ffba54
Fix "default member initializer" problem
owent Sep 12, 2023
401a8ac
Merge remote-tracking branch 'github/main' into emits_resource_for_pr…
owent Sep 14, 2023
d2e144a
Add `target_info` metric.
owent Sep 14, 2023
a30ddf7
Merge remote-tracking branch 'github/main' into emits_resource_for_pr…
owent Sep 14, 2023
02f4d04
Fix instance and job exporting
owent Sep 14, 2023
004a4d4
Merge remote-tracking branch 'github/main' into emits_resource_for_pr…
owent Sep 19, 2023
72ed751
Add options to let user to decide whether to populate target_info
owent Sep 19, 2023
00b0b66
Fix warnings
owent Sep 19, 2023
9f01fd4
Fix priority of conflict labels.
owent Sep 21, 2023
5d24d88
Fix unit test
owent Sep 21, 2023
e50a3d6
Allow some attributes in resource attributes but not in metric attrib…
owent Sep 21, 2023
88e9857
Change the info-typed target metric.
owent Sep 21, 2023
a0b322b
Do not ignore metric attributes.
owent Sep 21, 2023
33d3a47
Merge remote-tracking branch 'github/main' into emits_resource_for_pr…
owent Sep 23, 2023
0ab87c0
Merge remote-tracking branch 'github/main' into emits_resource_for_pr…
owent Sep 27, 2023
fcc397f
Fix unit tests after merged.
owent Sep 27, 2023
23f29cd
Merge remote-tracking branch 'github/main' into emits_resource_for_pr…
owent Sep 27, 2023
765eb99
Merge SanitizeName changes
owent Sep 27, 2023
c476939
Remove ignores in resource attributes
owent Sep 28, 2023
3ebe160
Merge remote-tracking branch 'opentelemetry/main'
owent Sep 29, 2023
422bca2
Remove labels conversations that should only be in target info.
owent Sep 29, 2023
d0f15f2
Remove unused variables.
owent Sep 29, 2023
bb62f0a
Populate target_info with every scrape
owent Oct 1, 2023
9826f67
Merge remote-tracking branch 'opentelemetry/main'
owent Oct 1, 2023
db783e2
Fix style
owent Oct 1, 2023
3517444
Merge remote-tracking branch 'opentelemetry/main'
owent Oct 3, 2023
dd737e3
Fix some legacy issues to remove `job` and `instance` labels.
owent Oct 3, 2023
b6ca2f2
Add scope for target-info
owent Oct 3, 2023
0149d5c
Restore `+2` when reserve space for vector.
owent Oct 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Increment the:

* [DEPRECATION] Deprecate ZPAGES
[#2291](https://github.com/open-telemetry/opentelemetry-cpp/pull/2291)
* [EXPORTER] Prometheus exporter emit resource attributes
[#2301](https://github.com/open-telemetry/opentelemetry-cpp/pull/2301)

## [1.11.0] 2023-08-21

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,24 @@ class PrometheusExporterUtils
*/
static std::string SanitizeNames(std::string name);

/**
* Some attributes should be ignored when converting resource attributes to
* prometheus labels.
*
* @param name resource attribnuet name
owent marked this conversation as resolved.
Show resolved Hide resolved
* @return true if the attribute should be ignored, false otherwise.
owent marked this conversation as resolved.
Show resolved Hide resolved
*/
static bool ShouldIgnoreResourceAttribute(const std::string &name);

/**
* Some attributes should be renamed when converting resource attributes to
* prometheus labels.
*
* @param name resource attribnuet name
owent marked this conversation as resolved.
Show resolved Hide resolved
* @return const std::string&
*/
static const std::string &GetPrometheusAttributeName(const std::string &name);

static opentelemetry::sdk::metrics::AggregationType getAggregationType(
const opentelemetry::sdk::metrics::PointType &point_type);

Expand All @@ -59,7 +77,8 @@ class PrometheusExporterUtils
const opentelemetry::sdk::metrics::PointAttributes &labels,
::prometheus::MetricType type,
std::chrono::nanoseconds time,
::prometheus::MetricFamily *metric_family);
::prometheus::MetricFamily *metric_family,
const opentelemetry::sdk::resource::Resource *resource);

/**
* Set metric data for:
Expand All @@ -71,14 +90,16 @@ class PrometheusExporterUtils
const std::vector<uint64_t> &counts,
const opentelemetry::sdk::metrics::PointAttributes &labels,
std::chrono::nanoseconds time,
::prometheus::MetricFamily *metric_family);
::prometheus::MetricFamily *metric_family,
const opentelemetry::sdk::resource::Resource *resource);

/**
* Set time and labels to metric data
*/
static void SetMetricBasic(::prometheus::ClientMetric &metric,
std::chrono::nanoseconds time,
const opentelemetry::sdk::metrics::PointAttributes &labels);
const opentelemetry::sdk::metrics::PointAttributes &labels,
const opentelemetry::sdk::resource::Resource *resource);

/**
* Convert attribute value to string
Expand Down
125 changes: 109 additions & 16 deletions exporters/prometheus/src/exporter_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@
// SPDX-License-Identifier: Apache-2.0

#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#include "prometheus/metric_family.h"
#include "prometheus/metric_type.h"

#include <prometheus/metric_type.h>
#include "opentelemetry/exporters/prometheus/exporter_utils.h"
#include "opentelemetry/sdk/metrics/export/metric_producer.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/sdk/resource/semantic_conventions.h"
#include "opentelemetry/trace/semantic_conventions.h"

#include "opentelemetry/sdk/common/global_log_handler.h"

Expand All @@ -20,6 +27,7 @@ namespace exporter
{
namespace metrics
{

/**
* Helper function to convert OpenTelemetry metrics data collection
* to Prometheus metrics data collection
Expand Down Expand Up @@ -72,7 +80,7 @@ std::vector<prometheus_client::MetricFamily> PrometheusExporterUtils::TranslateT
sum = nostd::get<int64_t>(histogram_point_data.sum_);
}
SetData(std::vector<double>{sum, (double)histogram_point_data.count_}, boundaries, counts,
point_data_attr.attributes, time, &metric_family);
point_data_attr.attributes, time, &metric_family, data.resource_);
}
else if (type == prometheus_client::MetricType::Gauge)
{
Expand All @@ -82,14 +90,14 @@ std::vector<prometheus_client::MetricFamily> PrometheusExporterUtils::TranslateT
auto last_value_point_data =
nostd::get<sdk::metrics::LastValuePointData>(point_data_attr.point_data);
std::vector<metric_sdk::ValueType> values{last_value_point_data.value_};
SetData(values, point_data_attr.attributes, type, time, &metric_family);
SetData(values, point_data_attr.attributes, type, time, &metric_family, data.resource_);
}
else if (nostd::holds_alternative<sdk::metrics::SumPointData>(point_data_attr.point_data))
{
auto sum_point_data =
nostd::get<sdk::metrics::SumPointData>(point_data_attr.point_data);
std::vector<metric_sdk::ValueType> values{sum_point_data.value_};
SetData(values, point_data_attr.attributes, type, time, &metric_family);
SetData(values, point_data_attr.attributes, type, time, &metric_family, data.resource_);
}
else
{
Expand All @@ -105,7 +113,7 @@ std::vector<prometheus_client::MetricFamily> PrometheusExporterUtils::TranslateT
auto sum_point_data =
nostd::get<sdk::metrics::SumPointData>(point_data_attr.point_data);
std::vector<metric_sdk::ValueType> values{sum_point_data.value_};
SetData(values, point_data_attr.attributes, type, time, &metric_family);
SetData(values, point_data_attr.attributes, type, time, &metric_family, data.resource_);
}
else
{
Expand Down Expand Up @@ -167,6 +175,30 @@ std::string PrometheusExporterUtils::SanitizeNames(std::string name)
return name;
}

bool PrometheusExporterUtils::ShouldIgnoreResourceAttribute(const std::string &name)
{
static std::unordered_set<std::string> ignores{
owent marked this conversation as resolved.
Show resolved Hide resolved
opentelemetry::sdk::resource::SemanticConventions::kServiceName,
opentelemetry::sdk::resource::SemanticConventions::kServiceNamespace,
opentelemetry::trace::SemanticConventions::kServerAddress,
opentelemetry::trace::SemanticConventions::kServerPort,
opentelemetry::trace::SemanticConventions::kUrlScheme};
return ignores.end() != ignores.find(name);
}

const std::string &PrometheusExporterUtils::GetPrometheusAttributeName(const std::string &name)
{
static std::unordered_map<std::string, std::string> name_mappings{
{opentelemetry::sdk::resource::SemanticConventions::kServiceInstanceId, "instance"}};
std::unordered_map<std::string, std::string>::const_iterator it = name_mappings.find(name);
if (it == name_mappings.end())
{
return name;
}

return it->second;
}

metric_sdk::AggregationType PrometheusExporterUtils::getAggregationType(
const metric_sdk::PointType &point_type)
{
Expand Down Expand Up @@ -229,11 +261,12 @@ void PrometheusExporterUtils::SetData(std::vector<T> values,
const metric_sdk::PointAttributes &labels,
prometheus_client::MetricType type,
std::chrono::nanoseconds time,
prometheus_client::MetricFamily *metric_family)
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, time, labels);
SetMetricBasic(metric, time, labels, resource);
SetValue(values, type, &metric);
}

Expand All @@ -247,11 +280,12 @@ void PrometheusExporterUtils::SetData(std::vector<T> values,
const std::vector<uint64_t> &counts,
const metric_sdk::PointAttributes &labels,
std::chrono::nanoseconds time,
prometheus_client::MetricFamily *metric_family)
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, time, labels);
SetMetricBasic(metric, time, labels, resource);
SetValue(values, boundaries, counts, &metric);
}

Expand All @@ -260,20 +294,79 @@ void PrometheusExporterUtils::SetData(std::vector<T> values,
*/
void PrometheusExporterUtils::SetMetricBasic(prometheus_client::ClientMetric &metric,
std::chrono::nanoseconds time,
const metric_sdk::PointAttributes &labels)
const metric_sdk::PointAttributes &labels,
const opentelemetry::sdk::resource::Resource *resource)
{
metric.timestamp_ms = time.count() / 1000000;

std::size_t label_size = 0;
if (nullptr != resource)
{
label_size += resource->GetAttributes().size();
}
if (!labels.empty())
{
label_size += labels.size();
}
metric.label.reserve(label_size);

// Convert resource to prometheus labels
if (nullptr != resource)
{
opentelemetry::sdk::resource::ResourceAttributes::const_iterator service_name_it =
resource->GetAttributes().find(
opentelemetry::sdk::resource::SemanticConventions::kServiceName);
opentelemetry::sdk::resource::ResourceAttributes::const_iterator service_namespace_it =
resource->GetAttributes().find(
opentelemetry::sdk::resource::SemanticConventions::kServiceNamespace);

if (service_namespace_it != resource->GetAttributes().end() &&
service_name_it != resource->GetAttributes().end())
{
prometheus_client::ClientMetric::Label prometheus_label;
prometheus_label.name = "job";
prometheus_label.value = AttributeValueToString(service_namespace_it->second) + "/" +
AttributeValueToString(service_name_it->second);
metric.label.emplace_back(std::move(prometheus_label));
}
else if (service_name_it != resource->GetAttributes().end())
{
prometheus_client::ClientMetric::Label prometheus_label;
prometheus_label.name = "job";
prometheus_label.value = AttributeValueToString(service_name_it->second);
metric.label.emplace_back(std::move(prometheus_label));
}
else if (service_namespace_it != resource->GetAttributes().end())
{
prometheus_client::ClientMetric::Label prometheus_label;
prometheus_label.name = "job";
prometheus_label.value = AttributeValueToString(service_namespace_it->second);
metric.label.emplace_back(std::move(prometheus_label));
}

for (auto &label : resource->GetAttributes())
{
if (ShouldIgnoreResourceAttribute(label.first))
{
continue;
}

prometheus_client::ClientMetric::Label prometheus_label;
prometheus_label.name = SanitizeNames(GetPrometheusAttributeName(label.first));
prometheus_label.value = AttributeValueToString(label.second);
metric.label.emplace_back(std::move(prometheus_label));
owent marked this conversation as resolved.
Show resolved Hide resolved
}
}

// auto label_pairs = ParseLabel(labels);
if (!labels.empty())
{
metric.label.resize(labels.size());
size_t i = 0;
for (auto const &label : labels)
for (auto &label : labels)
{
auto sanitized = SanitizeNames(label.first);
metric.label[i].name = sanitized;
metric.label[i++].value = AttributeValueToString(label.second);
prometheus_client::ClientMetric::Label prometheus_label;
prometheus_label.name = SanitizeNames(label.first);
prometheus_label.value = AttributeValueToString(label.second);
metric.label.emplace_back(std::move(prometheus_label));
}
}
}
Expand Down
69 changes: 62 additions & 7 deletions exporters/prometheus/test/exporter_utils_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "prometheus/metric_type.h"

#include "opentelemetry/exporters/prometheus/exporter_utils.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "prometheus_test_helper.h"

using opentelemetry::exporter::metrics::PrometheusExporterUtils;
Expand Down Expand Up @@ -35,7 +36,7 @@ void assert_basic(prometheus_client::MetricFamily &metric,
const std::string &sanitized_name,
const std::string &description,
prometheus_client::MetricType type,
int label_num,
size_t label_num,
std::vector<T> vals)
{
ASSERT_EQ(metric.name, sanitized_name + "_unit"); // name sanitized
Expand Down Expand Up @@ -107,28 +108,82 @@ TEST(PrometheusExporterUtils, TranslateToPrometheusEmptyInputReturnsEmptyCollect
TEST(PrometheusExporterUtils, TranslateToPrometheusIntegerCounter)
{

opentelemetry::sdk::resource::Resource resource = opentelemetry::sdk::resource::Resource::Create(
{{"service.name", "test_service"},
{"service.namespace", "test_namespace"},
{"service.instance.id", "localhost:8000"},
{"custom_resource_attr", "custom_resource_value"}});
metric_sdk::ResourceMetrics metrics_data = CreateSumPointData();
metrics_data.resource_ = &resource;

auto translated = PrometheusExporterUtils::TranslateToPrometheus(metrics_data);
ASSERT_EQ(translated.size(), 1);

auto metric1 = translated[0];
std::vector<int> vals = {10};
assert_basic(metric1, "library_name", "description", prometheus_client::MetricType::Counter, 1,
vals);

assert_basic(metric1, "library_name", "description", prometheus_client::MetricType::Counter,
resource.GetAttributes().size(), vals);

int checked_label_num = 0;
for (auto &label : metric1.metric[0].label)
{
if (label.name == "job")
{
ASSERT_EQ(label.value, "test_namespace/test_service");
checked_label_num++;
}
else if (label.name == "instance")
{
ASSERT_EQ(label.value, "localhost:8000");
checked_label_num++;
}
else if (label.name == "custom_resource_attr")
{
ASSERT_EQ(label.value, "custom_resource_value");
checked_label_num++;
}
}
ASSERT_EQ(checked_label_num, 3);
}

TEST(PrometheusExporterUtils, TranslateToPrometheusIntegerLastValue)
{
opentelemetry::sdk::resource::Resource resource = opentelemetry::sdk::resource::Resource::Create(
{{"service.name", "test_service"},
{"service.instance.id", "localhost:8000"},
{"custom_resource_attr", "custom_resource_value"}});
metric_sdk::ResourceMetrics metrics_data = CreateLastValuePointData();
metrics_data.resource_ = &resource;

auto translated = PrometheusExporterUtils::TranslateToPrometheus(metrics_data);
ASSERT_EQ(translated.size(), 1);

auto metric1 = translated[0];
std::vector<int> vals = {10};
assert_basic(metric1, "library_name", "description", prometheus_client::MetricType::Gauge, 1,
vals);
assert_basic(metric1, "library_name", "description", prometheus_client::MetricType::Gauge,
resource.GetAttributes().size() + 1, vals);

int checked_label_num = 0;
for (auto &label : metric1.metric[0].label)
{
if (label.name == "job")
{
ASSERT_EQ(label.value, "test_service");
checked_label_num++;
}
else if (label.name == "instance")
{
ASSERT_EQ(label.value, "localhost:8000");
checked_label_num++;
}
else if (label.name == "custom_resource_attr")
{
ASSERT_EQ(label.value, "custom_resource_value");
checked_label_num++;
}
}
ASSERT_EQ(checked_label_num, 3);
}

TEST(PrometheusExporterUtils, TranslateToPrometheusHistogramNormal)
Expand All @@ -140,8 +195,8 @@ TEST(PrometheusExporterUtils, TranslateToPrometheusHistogramNormal)

auto metric = translated[0];
std::vector<double> vals = {3, 900.5, 4};
assert_basic(metric, "library_name", "description", prometheus_client::MetricType::Histogram, 1,
vals);
assert_basic(metric, "library_name", "description", prometheus_client::MetricType::Histogram,
metrics_data.resource_->GetAttributes().size() + 1, vals);
assert_histogram(metric, std::list<double>{10.1, 20.2, 30.2}, {200, 300, 400, 500});
}

Expand Down
Loading