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

Commit 042603a

Browse files
author
Ian Sturdy
authored
Add Last Value aggregation. (#144)
1 parent c7c7510 commit 042603a

File tree

12 files changed

+272
-12
lines changed

12 files changed

+272
-12
lines changed

opencensus/exporters/stats/prometheus/internal/prometheus_utils.cc

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <utility>
2121
#include <vector>
2222

23+
#include "absl/base/macros.h"
2324
#include "absl/strings/str_cat.h"
2425
#include "absl/strings/string_view.h"
2526
#include "absl/time/time.h"
@@ -48,21 +49,40 @@ io::prometheus::client::MetricType MetricType(
4849
return io::prometheus::client::MetricType::COUNTER;
4950
case opencensus::stats::Aggregation::Type::kSum:
5051
return io::prometheus::client::MetricType::UNTYPED;
52+
case opencensus::stats::Aggregation::Type::kLastValue:
53+
return io::prometheus::client::MetricType::GAUGE;
5154
case opencensus::stats::Aggregation::Type::kDistribution:
5255
return io::prometheus::client::MetricType::HISTOGRAM;
5356
}
5457
}
5558

5659
void SetValue(double value, io::prometheus::client::MetricType type,
5760
io::prometheus::client::Metric* metric) {
58-
metric->mutable_untyped()->set_value(value);
61+
if (type == io::prometheus::client::MetricType::UNTYPED) {
62+
metric->mutable_untyped()->set_value(value);
63+
} else {
64+
ABSL_ASSERT(type == io::prometheus::client::MetricType::GAUGE);
65+
metric->mutable_gauge()->set_value(value);
66+
}
5967
}
68+
6069
void SetValue(int64_t value, io::prometheus::client::MetricType type,
6170
io::prometheus::client::Metric* metric) {
62-
if (type == io::prometheus::client::MetricType::COUNTER) {
63-
metric->mutable_counter()->set_value(value);
64-
} else {
65-
metric->mutable_untyped()->set_value(value);
71+
switch (type) {
72+
case io::prometheus::client::MetricType::COUNTER: {
73+
metric->mutable_counter()->set_value(value);
74+
break;
75+
}
76+
case io::prometheus::client::MetricType::GAUGE: {
77+
metric->mutable_gauge()->set_value(value);
78+
break;
79+
}
80+
case io::prometheus::client::MetricType::UNTYPED: {
81+
metric->mutable_untyped()->set_value(value);
82+
break;
83+
}
84+
default:
85+
ABSL_ASSERT(false && "Invalid MetricType for int64 value.");
6686
}
6787
}
6888
void SetValue(const opencensus::stats::Distribution& value,

opencensus/exporters/stats/prometheus/internal/prometheus_utils_test.cc

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,78 @@ TEST(SetMetricFamilyTest, SumInt) {
157157
)");
158158
}
159159

160+
TEST(SetMetricFamilyTest, LastValueDouble) {
161+
const auto measure = opencensus::stats::MeasureDouble::Register(
162+
"measure_last_value_double", "", "units");
163+
const std::string task = "test_task";
164+
const std::string view_name = "test_descriptor";
165+
const auto tag_key_1 = opencensus::stats::TagKey::Register("foo");
166+
const auto tag_key_2 = opencensus::stats::TagKey::Register("bar");
167+
const auto view_descriptor =
168+
opencensus::stats::ViewDescriptor()
169+
.set_name(view_name)
170+
.set_measure(measure.GetDescriptor().name())
171+
.set_aggregation(opencensus::stats::Aggregation::LastValue())
172+
.add_column(tag_key_1)
173+
.add_column(tag_key_2);
174+
const opencensus::stats::ViewData data = TestUtils::MakeViewData(
175+
view_descriptor,
176+
{{{"v1", "v1"}, 1.0}, {{"v1", "v1"}, 3.0}, {{"v1", "v2"}, 2.0}});
177+
io::prometheus::client::MetricFamily actual;
178+
SetMetricFamily(view_descriptor, data, &actual);
179+
180+
CompareMetricFamilies(actual, R"(
181+
name: "test_descriptor_units"
182+
type: GAUGE
183+
metric {
184+
label { name: "foo" value: "v1" }
185+
label { name: "bar" value: "v1" }
186+
gauge { value: 3.0 }
187+
}
188+
metric {
189+
label { name: "foo" value: "v1" }
190+
label { name: "bar" value: "v2" }
191+
gauge { value: 2.0 }
192+
}
193+
)");
194+
}
195+
196+
TEST(SetMetricFamilyTest, LastValueInt64) {
197+
const auto measure = opencensus::stats::MeasureInt64::Register(
198+
"measure_last_value_int", "", "units");
199+
const std::string task = "test_task";
200+
const std::string view_name = "test_descriptor";
201+
const auto tag_key_1 = opencensus::stats::TagKey::Register("foo");
202+
const auto tag_key_2 = opencensus::stats::TagKey::Register("bar");
203+
const auto view_descriptor =
204+
opencensus::stats::ViewDescriptor()
205+
.set_name(view_name)
206+
.set_measure(measure.GetDescriptor().name())
207+
.set_aggregation(opencensus::stats::Aggregation::LastValue())
208+
.add_column(tag_key_1)
209+
.add_column(tag_key_2);
210+
const opencensus::stats::ViewData data = TestUtils::MakeViewData(
211+
view_descriptor,
212+
{{{"v1", "v1"}, 1}, {{"v1", "v1"}, 3}, {{"v1", "v2"}, 2}});
213+
io::prometheus::client::MetricFamily actual;
214+
SetMetricFamily(view_descriptor, data, &actual);
215+
216+
CompareMetricFamilies(actual, R"(
217+
name: "test_descriptor_units"
218+
type: GAUGE
219+
metric {
220+
label { name: "foo" value: "v1" }
221+
label { name: "bar" value: "v1" }
222+
gauge { value: 3 }
223+
}
224+
metric {
225+
label { name: "foo" value: "v1" }
226+
label { name: "bar" value: "v2" }
227+
gauge { value: 2 }
228+
}
229+
)");
230+
}
231+
160232
TEST(StackdriverUtilsTest, MakeTimeSeriesDistributionDouble) {
161233
const auto measure = opencensus::stats::MeasureDouble::Register(
162234
"measure_distribution_double", "", "units");

opencensus/exporters/stats/stackdriver/internal/stackdriver_utils.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ google::api::MetricDescriptor::ValueType GetValueType(
7373
case opencensus::stats::Aggregation::Type::kCount:
7474
return google::api::MetricDescriptor::INT64;
7575
case opencensus::stats::Aggregation::Type::kSum:
76+
case opencensus::stats::Aggregation::Type::kLastValue:
7677
switch (descriptor.measure_descriptor().type()) {
7778
case opencensus::stats::MeasureDescriptor::Type::kDouble:
7879
return google::api::MetricDescriptor::DOUBLE;
@@ -158,7 +159,11 @@ void SetMetricDescriptor(
158159
for (const auto& tag_key : view_descriptor.columns()) {
159160
SetLabelDescriptor(tag_key.name(), metric_descriptor->add_labels());
160161
}
161-
metric_descriptor->set_metric_kind(google::api::MetricDescriptor::CUMULATIVE);
162+
metric_descriptor->set_metric_kind(
163+
view_descriptor.aggregation().type() ==
164+
opencensus::stats::Aggregation::Type::kLastValue
165+
? google::api::MetricDescriptor::GAUGE
166+
: google::api::MetricDescriptor::CUMULATIVE);
162167
metric_descriptor->set_value_type(GetValueType(view_descriptor));
163168
metric_descriptor->set_unit(
164169
view_descriptor.aggregation() == opencensus::stats::Aggregation::Count()

opencensus/exporters/stats/stackdriver/internal/stackdriver_utils_test.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,23 @@ TEST(StackdriverUtilsTest, SetMetricDescriptorMetricKind) {
7777
auto view_descriptor = opencensus::stats::ViewDescriptor();
7878
google::api::MetricDescriptor metric_descriptor;
7979

80+
view_descriptor.set_aggregation(opencensus::stats::Aggregation::Count());
81+
SetMetricDescriptor("", view_descriptor, &metric_descriptor);
82+
EXPECT_EQ(google::api::MetricDescriptor::CUMULATIVE,
83+
metric_descriptor.metric_kind());
84+
85+
view_descriptor.set_aggregation(opencensus::stats::Aggregation::Sum());
86+
SetMetricDescriptor("", view_descriptor, &metric_descriptor);
87+
EXPECT_EQ(google::api::MetricDescriptor::CUMULATIVE,
88+
metric_descriptor.metric_kind());
89+
90+
view_descriptor.set_aggregation(opencensus::stats::Aggregation::LastValue());
91+
SetMetricDescriptor("", view_descriptor, &metric_descriptor);
92+
EXPECT_EQ(google::api::MetricDescriptor::GAUGE,
93+
metric_descriptor.metric_kind());
94+
95+
view_descriptor.set_aggregation(opencensus::stats::Aggregation::Distribution(
96+
opencensus::stats::BucketBoundaries::Explicit({})));
8097
SetMetricDescriptor("", view_descriptor, &metric_descriptor);
8198
EXPECT_EQ(google::api::MetricDescriptor::CUMULATIVE,
8299
metric_descriptor.metric_kind());

opencensus/stats/aggregation.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,16 @@ class Aggregation final {
4747
return Aggregation(Type::kDistribution, std::move(buckets));
4848
}
4949

50+
// LastValue aggregation returns the last value recorded.
51+
static Aggregation LastValue() {
52+
return Aggregation(Type::kLastValue, BucketBoundaries::Explicit({}));
53+
}
54+
5055
enum class Type {
5156
kCount,
5257
kSum,
5358
kDistribution,
59+
kLastValue,
5460
};
5561

5662
Type type() const { return type_; }

opencensus/stats/internal/aggregation.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,17 @@ std::string Aggregation::DebugString() const {
2323
switch (type_) {
2424
case Type::kCount: {
2525
return "Count";
26-
break;
2726
}
2827
case Type::kSum: {
2928
return "Sum";
30-
break;
3129
}
3230
case Type::kDistribution: {
3331
return absl::StrCat("Distribution with ",
3432
bucket_boundaries_.DebugString());
3533
}
34+
case Type::kLastValue: {
35+
return "Last Value";
36+
}
3637
}
3738
}
3839

opencensus/stats/internal/debug_string_test.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ namespace {
3131
TEST(DebugStringTest, Aggregation) {
3232
EXPECT_NE("", Aggregation::Count().DebugString());
3333
EXPECT_NE("", Aggregation::Sum().DebugString());
34+
EXPECT_NE("", Aggregation::LastValue().DebugString());
3435

3536
const BucketBoundaries buckets = BucketBoundaries::Explicit({0, 1});
3637
EXPECT_PRED_FORMAT2(::testing::IsSubstring, buckets.DebugString(),

opencensus/stats/internal/measure_data.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ MeasureData::MeasureData(absl::Span<const BucketBoundaries> boundaries)
3737
}
3838

3939
void MeasureData::Add(double value) {
40+
last_value_ = value;
4041
// Update using the method of provisional means.
4142
++count_;
4243
ABSL_ASSERT(count_ > 0 && "Histogram count overflow.");

opencensus/stats/internal/measure_data.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#define OPENCENSUS_STATS_INTERNAL_MEASURE_DATA_H_
1717

1818
#include <cstdint>
19+
#include <limits>
1920
#include <vector>
2021

2122
#include "absl/types/span.h"
@@ -35,6 +36,7 @@ class MeasureData final {
3536

3637
void Add(double value);
3738

39+
double last_value() const { return last_value_; }
3840
uint64_t count() const { return count_; }
3941
double sum() const { return count_ * mean_; }
4042

@@ -53,6 +55,7 @@ class MeasureData final {
5355
private:
5456
const absl::Span<const BucketBoundaries> boundaries_;
5557

58+
double last_value_ = std::numeric_limits<double>::quiet_NaN();
5659
uint64_t count_ = 0;
5760
double mean_ = 0;
5861
double sum_of_squared_deviation_ = 0;

opencensus/stats/internal/stats_manager_test.cc

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,64 @@ TEST_F(StatsManagerTest, SumInt) {
140140
::testing::Pair(::testing::ElementsAre("value1", "value2"), 4)));
141141
}
142142

143+
TEST_F(StatsManagerTest, LastValueDouble) {
144+
ViewDescriptor view_descriptor =
145+
ViewDescriptor()
146+
.set_measure(kFirstMeasureId)
147+
.set_name("last_value_double")
148+
.set_aggregation(Aggregation::LastValue())
149+
.add_column(key1_)
150+
.add_column(key2_);
151+
View view(view_descriptor);
152+
ASSERT_EQ(ViewData::Type::kDouble, view.GetData().type());
153+
EXPECT_TRUE(view.GetData().double_data().empty());
154+
155+
// Stats under a different measure should be ignored.
156+
Record({{SecondMeasure(), 1}});
157+
testing::TestUtils::Flush();
158+
EXPECT_TRUE(view.GetData().double_data().empty());
159+
160+
Record({{FirstMeasure(), 2.0}, {FirstMeasure(), 3.0}});
161+
Record({{FirstMeasure(), 4.0}},
162+
{{key1_, "value1"}, {key2_, "value2"}, {key3_, "value3"}});
163+
testing::TestUtils::Flush();
164+
const opencensus::stats::ViewData data = view.GetData();
165+
EXPECT_THAT(
166+
data.double_data(),
167+
::testing::UnorderedElementsAre(
168+
::testing::Pair(::testing::ElementsAre("", ""), 3.0),
169+
::testing::Pair(::testing::ElementsAre("value1", "value2"), 4.0)));
170+
}
171+
172+
TEST_F(StatsManagerTest, LastValueInt) {
173+
ViewDescriptor view_descriptor =
174+
ViewDescriptor()
175+
.set_measure(kSecondMeasureId)
176+
.set_name("last_value_int")
177+
.set_aggregation(Aggregation::LastValue())
178+
.add_column(key1_)
179+
.add_column(key2_);
180+
View view(view_descriptor);
181+
ASSERT_EQ(ViewData::Type::kInt64, view.GetData().type());
182+
EXPECT_TRUE(view.GetData().int_data().empty());
183+
184+
// Stats under a different measure should be ignored.
185+
Record({{FirstMeasure(), 1.0}});
186+
testing::TestUtils::Flush();
187+
EXPECT_TRUE(view.GetData().int_data().empty());
188+
189+
Record({{SecondMeasure(), 2}, {SecondMeasure(), 3}});
190+
Record({{SecondMeasure(), 4}},
191+
{{key1_, "value1"}, {key2_, "value2"}, {key3_, "value3"}});
192+
testing::TestUtils::Flush();
193+
const opencensus::stats::ViewData data = view.GetData();
194+
EXPECT_THAT(
195+
data.int_data(),
196+
::testing::UnorderedElementsAre(
197+
::testing::Pair(::testing::ElementsAre("", ""), 3),
198+
::testing::Pair(::testing::ElementsAre("value1", "value2"), 4)));
199+
}
200+
143201
TEST_F(StatsManagerTest, Distribution) {
144202
ViewDescriptor view_descriptor =
145203
ViewDescriptor()

0 commit comments

Comments
 (0)