Skip to content
This repository was archived by the owner on Jul 31, 2023. It is now read-only.

Commit c6bfe2e

Browse files
author
Ian Sturdy
authored
Add a Prometheus exporter. (#50)
1 parent 1d1a0d7 commit c6bfe2e

File tree

8 files changed

+620
-0
lines changed

8 files changed

+620
-0
lines changed

WORKSPACE

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,16 @@ cc_library(
8080
)
8181
"""
8282
)
83+
84+
http_archive(
85+
name = "prometheus_cpp",
86+
strip_prefix = "prometheus-cpp-master",
87+
urls = ["https://github.com/jupp0r/prometheus-cpp/archive/master.zip"],
88+
)
89+
90+
load("@prometheus_cpp//:repositories.bzl", "load_prometheus_client_model",
91+
"load_civetweb")
92+
93+
# Load dependencies individually since we load some of them above.
94+
load_prometheus_client_model()
95+
load_civetweb()
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Copyright 2018, OpenCensus Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
load("//opencensus:copts.bzl", "DEFAULT_COPTS", "TEST_COPTS")
16+
17+
licenses(["notice"]) # Apache License 2.0
18+
19+
package(default_visibility = ["//visibility:private"])
20+
21+
cc_library(
22+
name = "prometheus_exporter",
23+
srcs = ["internal/prometheus_exporter.cc"],
24+
hdrs = ["prometheus_exporter.h"],
25+
copts = DEFAULT_COPTS,
26+
visibility = ["//visibility:public"],
27+
deps = [
28+
":prometheus_utils",
29+
"//opencensus/stats",
30+
"@prometheus_client_model",
31+
"@prometheus_cpp",
32+
],
33+
)
34+
35+
# Internal libraries.
36+
# ========================================================================= #
37+
38+
cc_library(
39+
name = "prometheus_utils",
40+
srcs = ["internal/prometheus_utils.cc"],
41+
hdrs = ["internal/prometheus_utils.h"],
42+
copts = DEFAULT_COPTS,
43+
deps = [
44+
"//opencensus/stats",
45+
"@com_google_absl//absl/strings",
46+
"@com_google_absl//absl/time",
47+
"@prometheus_client_model",
48+
],
49+
)
50+
51+
# Tests.
52+
# ========================================================================= #
53+
54+
cc_test(
55+
name = "prometheus_utils_test",
56+
srcs = ["internal/prometheus_utils_test.cc"],
57+
copts = TEST_COPTS,
58+
deps = [
59+
":prometheus_utils",
60+
"//opencensus/stats",
61+
"//opencensus/stats:test_utils",
62+
"@com_google_absl//absl/strings",
63+
"@com_google_googletest//:gtest_main",
64+
"@com_google_protobuf//:protobuf",
65+
"@prometheus_client_model",
66+
],
67+
)
68+
69+
cc_binary(
70+
name = "prometheus_test_server",
71+
srcs = ["internal/prometheus_test_server.cc"],
72+
deps = [
73+
":prometheus_exporter",
74+
"//opencensus/stats",
75+
"@com_google_absl//absl/time",
76+
"@prometheus_cpp",
77+
],
78+
)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2018, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "opencensus/exporters/stats/prometheus/prometheus_exporter.h"
16+
17+
#include <utility>
18+
#include <vector>
19+
20+
#include "metrics.pb.h"
21+
#include "opencensus/exporters/stats/prometheus/internal/prometheus_utils.h"
22+
#include "opencensus/stats/stats.h"
23+
24+
namespace opencensus {
25+
namespace exporters {
26+
namespace stats {
27+
28+
std::vector<io::prometheus::client::MetricFamily>
29+
PrometheusExporter::Collect() {
30+
const auto data = opencensus::stats::StatsExporter::GetViewData();
31+
std::vector<io::prometheus::client::MetricFamily> output(data.size());
32+
for (int i = 0; i < data.size(); ++i) {
33+
SetMetricFamily(data[i].first, data[i].second, &output[i]);
34+
}
35+
return output;
36+
}
37+
38+
} // namespace stats
39+
} // namespace exporters
40+
} // namespace opencensus
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2018, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <iostream>
16+
#include <memory>
17+
18+
#include "absl/time/clock.h"
19+
#include "absl/time/time.h"
20+
#include "opencensus/exporters/stats/prometheus/prometheus_exporter.h"
21+
#include "opencensus/stats/stats.h"
22+
#include "prometheus/exposer.h"
23+
24+
int main(int argc, char** argv) {
25+
// Expose a Prometheus endpoint and register the Opencensus exporter with it.
26+
prometheus::Exposer exposer("127.0.0.1:8080");
27+
auto exporter =
28+
std::make_shared<opencensus::exporters::stats::PrometheusExporter>();
29+
exposer.RegisterCollectable(exporter);
30+
31+
// Create a view and register it with the exporter.
32+
const std::string foo_usage_measure_name = "example.com/Foo/FooUsage";
33+
const opencensus::stats::MeasureDouble foo_usage =
34+
opencensus::stats::MeasureRegistry::RegisterDouble(
35+
foo_usage_measure_name, "foos", "Usage of foos.");
36+
const auto view_descriptor =
37+
opencensus::stats::ViewDescriptor()
38+
.set_name("example.com/Bar/FooUsage-sum-cumulative-key1-key2")
39+
.set_measure(foo_usage_measure_name)
40+
.set_aggregation(opencensus::stats::Aggregation::Distribution(
41+
opencensus::stats::BucketBoundaries::Explicit({0, 10})))
42+
.add_column("key1")
43+
.add_column("key2")
44+
.set_description(
45+
"Cumulative distribution of example.com/Foo/FooUsage broken down "
46+
"by 'key1' and 'key2'.");
47+
opencensus::stats::StatsExporter::AddView(view_descriptor);
48+
49+
std::cout << "Access metrics on http://127.0.0.1:8080/metrics\n";
50+
while (true) {
51+
opencensus::stats::Record({{foo_usage, 1.0}}, {{"key1", "v1"}});
52+
opencensus::stats::Record({{foo_usage, 7.0}}, {{"key1", "v1"}});
53+
opencensus::stats::Record({{foo_usage, 12.0}}, {{"key1", "v1"}});
54+
opencensus::stats::Record({{foo_usage, 5.0}},
55+
{{"key1", "v1"}, {"key2", "v2"}});
56+
absl::SleepFor(absl::Seconds(10));
57+
}
58+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright 2018, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "opencensus/exporters/stats/prometheus/internal/prometheus_utils.h"
16+
17+
#include <cctype>
18+
#include <cstdint>
19+
#include <string>
20+
#include <utility>
21+
#include <vector>
22+
23+
#include "absl/strings/str_cat.h"
24+
#include "absl/strings/string_view.h"
25+
#include "absl/time/time.h"
26+
#include "metrics.pb.h"
27+
#include "opencensus/stats/stats.h"
28+
29+
namespace opencensus {
30+
namespace exporters {
31+
namespace stats {
32+
33+
namespace {
34+
35+
// Replaces non-alphanumeric characters with underscores to satisfy
36+
// Prometheus's name requirements.
37+
std::string SanitizeName(absl::string_view name) {
38+
std::string sanitized(name);
39+
std::replace_if(sanitized.begin(), sanitized.end(),
40+
[](char c) { return !::isalnum(c); }, '_');
41+
return sanitized;
42+
}
43+
44+
io::prometheus::client::MetricType MetricType(
45+
opencensus::stats::Aggregation::Type type) {
46+
switch (type) {
47+
case opencensus::stats::Aggregation::Type::kCount:
48+
return io::prometheus::client::MetricType::COUNTER;
49+
case opencensus::stats::Aggregation::Type::kSum:
50+
return io::prometheus::client::MetricType::UNTYPED;
51+
case opencensus::stats::Aggregation::Type::kDistribution:
52+
return io::prometheus::client::MetricType::HISTOGRAM;
53+
}
54+
}
55+
56+
void SetValue(double value, io::prometheus::client::Metric* metric) {
57+
metric->mutable_untyped()->set_value(value);
58+
}
59+
void SetValue(int64_t value, io::prometheus::client::Metric* metric) {
60+
metric->mutable_counter()->set_value(value);
61+
}
62+
void SetValue(const opencensus::stats::Distribution& value,
63+
io::prometheus::client::Metric* metric) {
64+
auto* histogram = metric->mutable_histogram();
65+
histogram->set_sample_count(value.count());
66+
histogram->set_sample_sum(value.count() * value.mean());
67+
68+
int64_t cumulative_count = 0;
69+
for (int i = 0; i < value.bucket_boundaries().num_buckets(); ++i) {
70+
cumulative_count += value.bucket_counts()[i];
71+
auto* bucket = histogram->add_bucket();
72+
bucket->set_cumulative_count(cumulative_count);
73+
// We use lower boundaries plus an underflow bucket; Prometheus uses upper
74+
// boundaries, including a +Inf boundary.
75+
bucket->set_upper_bound(
76+
i < value.bucket_boundaries().lower_boundaries().size()
77+
? value.bucket_boundaries().lower_boundaries()[i]
78+
: std::numeric_limits<double>::infinity());
79+
}
80+
}
81+
82+
template <typename T>
83+
void SetData(const opencensus::stats::ViewDescriptor& descriptor,
84+
const opencensus::stats::ViewData::DataMap<T>& data, int64_t time,
85+
io::prometheus::client::MetricFamily* metric_family) {
86+
for (const auto& row : data) {
87+
auto* metric = metric_family->add_metric();
88+
metric->set_timestamp_ms(time);
89+
for (int i = 0; i < descriptor.num_columns(); ++i) {
90+
auto* label = metric->add_label();
91+
label->set_name(SanitizeName(descriptor.columns()[i]));
92+
label->set_value(row.first[i]);
93+
}
94+
SetValue(row.second, metric);
95+
}
96+
}
97+
98+
} // namespace
99+
100+
void SetMetricFamily(const opencensus::stats::ViewDescriptor& descriptor,
101+
const opencensus::stats::ViewData& data,
102+
io::prometheus::client::MetricFamily* metric_family) {
103+
// TODO(sturdy): convert common units into base units (e.g. ms->s).
104+
metric_family->set_name(SanitizeName(absl::StrCat(
105+
descriptor.name(), "_", descriptor.measure_descriptor().units())));
106+
metric_family->set_help(descriptor.description());
107+
metric_family->set_type(MetricType(descriptor.aggregation().type()));
108+
109+
const int64_t time = absl::ToUnixMillis(data.end_time());
110+
switch (data.type()) {
111+
case opencensus::stats::ViewData::Type::kDouble: {
112+
SetData(descriptor, data.double_data(), time, metric_family);
113+
break;
114+
}
115+
case opencensus::stats::ViewData::Type::kInt64: {
116+
SetData(descriptor, data.int_data(), time, metric_family);
117+
break;
118+
}
119+
case opencensus::stats::ViewData::Type::kDistribution: {
120+
SetData(descriptor, data.distribution_data(), time, metric_family);
121+
break;
122+
}
123+
}
124+
}
125+
126+
} // namespace stats
127+
} // namespace exporters
128+
} // namespace opencensus
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2018, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef OPENCENSUS_EXPORTERS_STATS_PROMETHEUS_INTERNAL_PROMETHEUS_UTILS_H_
16+
#define OPENCENSUS_EXPORTERS_STATS_PROMETHEUS_INTERNAL_PROMETHEUS_UTILS_H_
17+
18+
#include <utility>
19+
#include <vector>
20+
21+
#include "metrics.pb.h"
22+
#include "opencensus/stats/stats.h"
23+
24+
namespace opencensus {
25+
namespace exporters {
26+
namespace stats {
27+
28+
// Populates metric_family based on view_descriptor and view_data.
29+
void SetMetricFamily(const opencensus::stats::ViewDescriptor& descriptor,
30+
const opencensus::stats::ViewData& data,
31+
io::prometheus::client::MetricFamily* metric_family);
32+
33+
} // namespace stats
34+
} // namespace exporters
35+
} // namespace opencensus
36+
37+
#endif // OPENCENSUS_EXPORTERS_STATS_PROMETHEUS_INTERNAL_PROMETHEUS_UTILS_H_

0 commit comments

Comments
 (0)