Skip to content
This repository was archived by the owner on Feb 12, 2022. It is now read-only.

Commit 2d8f586

Browse files
authored
Add support for Cloudwatch MetricDatum StatisticSet (#48)
* add support for cloudwatch metrics' StatisticSet * add test for metricObjectToDatum() * address PR comments * take into consideration that MetricDatum's Value and StatisticValues are mutually exclusive * add docstring to MetricObject constructors
1 parent 920dce2 commit 2d8f586

File tree

2 files changed

+141
-31
lines changed

2 files changed

+141
-31
lines changed

cloudwatch_metrics_common/include/cloudwatch_metrics_common/utils/metric_object.h

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
#include <map>
1818
#include <string>
1919

20-
#include <aws/monitoring/model/StandardUnit.h>
2120
#include <aws/monitoring/model/PutMetricDataRequest.h>
21+
#include <aws/monitoring/model/StandardUnit.h>
2222
#include <cloudwatch_metrics_common/definitions/definitions.h>
2323

2424
namespace Aws {
@@ -46,24 +46,71 @@ static std::unordered_map<std::string, Aws::CloudWatch::Model ::StandardUnit> un
4646
{"", Aws::CloudWatch::Model::StandardUnit::None},
4747
};
4848

49+
enum class StatisticValuesType
50+
{
51+
MINIMUM,
52+
MAXIMUM,
53+
SUM,
54+
SAMPLE_COUNT
55+
};
56+
4957
/**
5058
* Wrapper object for the AWS specific Aws::CloudWatch::Model::MetricDatum. This object is meant to be constructed from
51-
* userland provided metric data instead of using the AWS SKD specific object.
59+
* userland provided metric data instead of using the AWS SDK specific object.
5260
*/
5361
struct MetricObject {
54-
const std::string metric_name;
55-
const double value;
56-
const std::string unit;
57-
const int64_t timestamp;
58-
const std::map<std::string, std::string> dimensions;
59-
const int storage_resolution;
62+
/**
63+
* Construct a new instance of MetricObject that contains a scalar value.
64+
*
65+
* @param _name metric name
66+
* @param _value metric scalar value
67+
* @param _unit metric unit
68+
* @param _timestamp metric time stamp
69+
* @param _dimensions dimensions delineating extra details about the metric
70+
* @param _storage_resolution
71+
*/
72+
MetricObject(
73+
const std::string & _name,
74+
const double _value,
75+
const std::string & _unit,
76+
const int64_t _timestamp,
77+
const std::map<std::string, std::string> & _dimensions,
78+
const int _storage_resolution)
79+
: metric_name(_name), unit(_unit), timestamp(_timestamp), value(_value),
80+
dimensions(_dimensions), storage_resolution(_storage_resolution) {}
81+
82+
/**
83+
* Construct a new instance of MetricObject that contains a statistical values.
84+
*
85+
* @param _name metric name
86+
* @param _statistic_values statistical values of the metric
87+
* @param _unit metric unit
88+
* @param _timestamp metric time stamp
89+
* @param _dimensions dimensions delineating extra details about the metric
90+
* @param _storage_resolution
91+
*/
92+
MetricObject(
93+
const std::string & _name,
94+
const std::map<StatisticValuesType, double> & _statistic_values,
95+
const std::string & _unit,
96+
const int64_t _timestamp,
97+
const std::map<std::string, std::string> & _dimensions,
98+
const int _storage_resolution)
99+
: metric_name(_name), unit(_unit), timestamp(_timestamp), statistic_values(_statistic_values),
100+
dimensions(_dimensions), storage_resolution(_storage_resolution) {}
101+
102+
std::string metric_name;
103+
std::string unit;
104+
int64_t timestamp;
105+
double value; // mutually exclusive with statistic_values
106+
std::map<StatisticValuesType, double> statistic_values; // mutually exclusive with value
107+
std::map<std::string, std::string> dimensions;
108+
int storage_resolution;
60109
};
61110

62111
/**
63112
* Helper method to constructor an Aws::CloudWatch::Model::MetricDatum from a MetricObject.
64113
*
65-
* Note: currently this does not support statistics data
66-
*
67114
* @param metrics input MetricObject
68115
* @param timestamp
69116
* @return Aws::CloudWatch::Model::MetricDatum
@@ -74,21 +121,39 @@ static MetricDatum metricObjectToDatum(const MetricObject &metrics, const int64_
74121
Aws::String aws_metric_name(metrics.metric_name.c_str());
75122
Aws::Utils::DateTime date_time(timestamp);
76123

77-
datum.WithMetricName(aws_metric_name).WithTimestamp(date_time).WithValue(metrics.value);
124+
datum.WithMetricName(aws_metric_name).WithTimestamp(date_time);
125+
126+
if (metrics.statistic_values.empty()) {
127+
datum.SetValue(metrics.value);
128+
} else {
129+
Aws::CloudWatch::Model::StatisticSet stats;
130+
for (const auto & keyval : metrics.statistic_values) {
131+
if (keyval.first == StatisticValuesType::MINIMUM) {
132+
stats.SetMinimum(keyval.second);
133+
} else if (keyval.first == StatisticValuesType::MAXIMUM) {
134+
stats.SetMaximum(keyval.second);
135+
} else if (keyval.first == StatisticValuesType::SUM) {
136+
stats.SetSum(keyval.second);
137+
} else if (keyval.first == StatisticValuesType::SAMPLE_COUNT) {
138+
stats.SetSampleCount(keyval.second);
139+
}
140+
}
141+
datum.SetStatisticValues(std::move(stats));
142+
}
78143

79144
auto mapped_unit = units_mapper.find(metrics.unit);
80145
if (units_mapper.end() != mapped_unit) {
81-
datum.WithUnit(mapped_unit->second);
146+
datum.SetUnit(mapped_unit->second);
82147
} else {
83148
Aws::String unit_name(metrics.unit.c_str());
84-
datum.WithUnit(Aws::CloudWatch::Model::StandardUnitMapper::GetStandardUnitForName(unit_name));
149+
datum.SetUnit(Aws::CloudWatch::Model::StandardUnitMapper::GetStandardUnitForName(unit_name));
85150
}
86151

87152
for (auto it = metrics.dimensions.begin(); it != metrics.dimensions.end(); ++it) {
88153
Aws::CloudWatch::Model::Dimension dimension;
89154
Aws::String name(it->first.c_str());
90155
Aws::String d_value(it->second.c_str());
91-
dimension.WithName(name.c_str()).WithValue(d_value);
156+
dimension.WithName(name).WithValue(d_value);
92157
datum.AddDimensions(dimension);
93158
}
94159

@@ -99,4 +164,4 @@ static MetricDatum metricObjectToDatum(const MetricObject &metrics, const int64_
99164

100165
} // namespace Utils
101166
} // namespace CloudWatchMetrics
102-
} // namespace Aws
167+
} // namespace Aws

cloudwatch_metrics_common/test/metric_pipeline_test.cpp

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ class PipelineTest : public ::testing::Test {
179179
std::shared_ptr<Aws::DataFlow::QueueMonitor<TaskPtr<MetricDatumCollection>>>queue_monitor;
180180
};
181181

182-
MetricObject createTestMetricObject(
182+
MetricObject createTestMetricObjectWithValue(
183183
const std::string & name,
184184
const double value = 2.42,
185185
const std::string & unit = "gigawatts",
@@ -190,6 +190,23 @@ MetricObject createTestMetricObject(
190190
return MetricObject {name, value, unit, timestamp, dimensions, storage_resolution};
191191
}
192192

193+
MetricObject createTestMetricObjectWithStatisticValues(
194+
const std::string & name,
195+
const double value = 2.42,
196+
const std::string & unit = "gigawatts",
197+
const int64_t timestamp = 1234,
198+
const std::map<std::string, std::string> & dimensions = std::map<std::string, std::string>(),
199+
const int storage_resolution = 60
200+
) {
201+
std::map<StatisticValuesType, double> statistic_values = {
202+
{StatisticValuesType::MINIMUM, value},
203+
{StatisticValuesType::MAXIMUM, value},
204+
{StatisticValuesType::SUM, value},
205+
{StatisticValuesType::SAMPLE_COUNT, 1.0}
206+
};
207+
return MetricObject{name, statistic_values, unit, timestamp, dimensions, storage_resolution};
208+
}
209+
193210
TEST_F(PipelineTest, Sanity) {
194211
ASSERT_TRUE(true);
195212
}
@@ -198,23 +215,51 @@ TEST(MetricPipelineTest, TestCreateMetricObject) {
198215

199216
std::string name("HeWhoShallNotBenamed");
200217

201-
auto object = createTestMetricObject(name);
202-
auto empty = std::map<std::string, std::string>();
203-
218+
auto object = createTestMetricObjectWithValue(name);
204219
EXPECT_EQ(name, object.metric_name);
205220
EXPECT_EQ(2.42, object.value);
206221
EXPECT_EQ("gigawatts", object.unit);
207222
EXPECT_EQ(1234, object.timestamp);
208-
EXPECT_EQ(empty, object.dimensions);
223+
EXPECT_TRUE(object.statistic_values.empty());
224+
EXPECT_TRUE(object.dimensions.empty());
209225
EXPECT_EQ(60, object.storage_resolution);
210226
}
211227

228+
TEST_F(PipelineTest, TestConvertToBatchedData) {
229+
230+
const std::string metric_name = "test_object";
231+
const std::string dimension_name = "blah";
232+
const std::string dimension_value = "blah blah";
233+
234+
auto object = createTestMetricObjectWithStatisticValues(metric_name);
235+
object.unit = "percent";
236+
object.statistic_values.clear();
237+
object.statistic_values[StatisticValuesType::SUM] = 111.1;
238+
object.statistic_values[StatisticValuesType::SAMPLE_COUNT] = 24;
239+
object.dimensions[dimension_name] = dimension_value;
240+
241+
auto datum = cw_service->convertInputToBatched(object);
242+
EXPECT_EQ(metric_name, datum.GetMetricName().c_str());
243+
EXPECT_EQ(Aws::CloudWatch::Model::StandardUnit::Percent, datum.GetUnit());
244+
EXPECT_EQ(1234, datum.GetTimestamp().Millis());
245+
EXPECT_EQ(60, datum.GetStorageResolution());
246+
247+
const auto & statistic_values = datum.GetStatisticValues();
248+
EXPECT_DOUBLE_EQ(24, statistic_values.GetSampleCount());
249+
EXPECT_DOUBLE_EQ(111.1, statistic_values.GetSum());
250+
251+
const auto & dimensions = datum.GetDimensions();
252+
EXPECT_EQ(1u, dimensions.size());
253+
EXPECT_EQ(dimension_name, dimensions[0].GetName().c_str());
254+
EXPECT_EQ(dimension_value, dimensions[0].GetValue().c_str());
255+
}
256+
212257
/**
213258
* Simple Pipeline test to check that everything was hooked up correctly.
214259
*/
215260
TEST_F(PipelineTest, TestBatcherManualPublish) {
216261

217-
auto toBatch = createTestMetricObject(std::string("testMetric"));
262+
auto toBatch = createTestMetricObjectWithValue(std::string("testMetric"));
218263
EXPECT_EQ(0u, batcher->getCurrentBatchSize());
219264
bool is_batched = cw_service->batchData(toBatch);
220265
EXPECT_EQ(1u, batcher->getCurrentBatchSize());
@@ -244,12 +289,12 @@ TEST_F(PipelineTest, TestBatcherManualPublish) {
244289
*/
245290
TEST_F(PipelineTest, TestBatcherManualPublishMultipleItems) {
246291

247-
auto toBatch = createTestMetricObject(std::string("TestBatcherManualPublish"));
292+
auto toBatch = createTestMetricObjectWithValue(std::string("TestBatcherManualPublish"));
248293
bool is_batched = cw_service->batchData(toBatch);
249294
EXPECT_TRUE(is_batched);
250295

251296
for(int i=99; i>0; i--) {
252-
auto batchedBottles = createTestMetricObject(std::to_string(99) + std::string(" bottles of beer on the wall"));
297+
auto batchedBottles = createTestMetricObjectWithValue(std::to_string(99) + std::string(" bottles of beer on the wall"));
253298
is_batched = cw_service->batchData(batchedBottles);
254299
EXPECT_TRUE(is_batched);
255300
}
@@ -281,7 +326,7 @@ TEST_F(PipelineTest, TestBatcherSize) {
281326
EXPECT_EQ(PublisherState::UNKNOWN, test_publisher->getPublisherState());
282327

283328
for(size_t i=1; i<size; i++) {
284-
auto toBatch = createTestMetricObject(std::string("test message ") + std::to_string(i));
329+
auto toBatch = createTestMetricObjectWithValue(std::string("test message ") + std::to_string(i));
285330
bool is_batched = cw_service->batchData(toBatch);
286331

287332
EXPECT_TRUE(is_batched);
@@ -291,7 +336,7 @@ TEST_F(PipelineTest, TestBatcherSize) {
291336
}
292337

293338
ASSERT_EQ(size, batcher->getTriggerBatchSize());
294-
auto toBatch = createTestMetricObject(("test message " + std::to_string(size)));
339+
auto toBatch = createTestMetricObjectWithValue(("test message " + std::to_string(size)));
295340
bool is_batched = cw_service->batchData(toBatch);
296341

297342
EXPECT_TRUE(is_batched);
@@ -312,7 +357,7 @@ TEST_F(PipelineTest, TestSinglePublisherFailureToFileManager) {
312357
batcher->setMetricFileManager(fileManager);
313358

314359
// batch
315-
auto toBatch = createTestMetricObject(std::string("TestBatcherManualPublish"));
360+
auto toBatch = createTestMetricObjectWithValue(std::string("TestBatcherManualPublish"));
316361
EXPECT_EQ(0u, batcher->getCurrentBatchSize());
317362
bool is_batched = cw_service->batchData(toBatch);
318363
EXPECT_EQ(1u, batcher->getCurrentBatchSize());
@@ -346,7 +391,7 @@ TEST_F(PipelineTest, TestInvalidDataNotPassedToFileManager) {
346391
batcher->setMetricFileManager(fileManager);
347392

348393
// batch
349-
auto toBatch = createTestMetricObject(std::string("TestBatcherManualPublish"));
394+
auto toBatch = createTestMetricObjectWithValue(std::string("TestBatcherManualPublish"));
350395
EXPECT_EQ(0u, batcher->getCurrentBatchSize());
351396
bool is_batched = cw_service->batchData(toBatch);
352397
EXPECT_EQ(1u, batcher->getCurrentBatchSize());
@@ -390,7 +435,7 @@ TEST_F(PipelineTest, TestPublisherIntermittant) {
390435
batcher->setMetricFileManager(fileManager);
391436

392437
// batch
393-
auto toBatch = createTestMetricObject(std::string("TestPublisherIntermittant"));
438+
auto toBatch = createTestMetricObjectWithValue(std::string("TestPublisherIntermittant"));
394439
EXPECT_EQ(0u, batcher->getCurrentBatchSize());
395440
bool is_batched = cw_service->batchData(toBatch);
396441
EXPECT_EQ(1u, batcher->getCurrentBatchSize());
@@ -438,7 +483,7 @@ TEST_F(PipelineTest, TestBatchDataTooFast) {
438483
EXPECT_EQ(max, batcher->getMaxAllowableBatchSize());
439484

440485
for(size_t i=1; i<=max; i++) {
441-
auto toBatch = createTestMetricObject(std::string("test message " + std::to_string(i)));
486+
auto toBatch = createTestMetricObjectWithValue(std::string("test message " + std::to_string(i)));
442487
bool is_batched = cw_service->batchData(toBatch);
443488

444489
EXPECT_TRUE(is_batched);
@@ -447,7 +492,7 @@ TEST_F(PipelineTest, TestBatchDataTooFast) {
447492
EXPECT_EQ(PublisherState::UNKNOWN, test_publisher->getPublisherState());
448493
}
449494

450-
auto toBatch = createTestMetricObject(std::string("iocaine powder"));
495+
auto toBatch = createTestMetricObjectWithValue(std::string("iocaine powder"));
451496
bool b = cw_service->batchData(toBatch);
452497

453498
EXPECT_FALSE(b);
@@ -468,4 +513,4 @@ int main(int argc, char ** argv)
468513
{
469514
testing::InitGoogleTest(&argc, argv);
470515
return RUN_ALL_TESTS();
471-
}
516+
}

0 commit comments

Comments
 (0)