29
29
#include " opentelemetry/sdk/metrics/state/metric_collector.h"
30
30
#include " opentelemetry/sdk/metrics/state/sync_metric_storage.h"
31
31
#include " opentelemetry/sdk/metrics/view/attributes_processor.h"
32
+ #include " opentelemetry/sdk/metrics/view/view.h"
32
33
33
34
#ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
34
35
# include " opentelemetry/sdk/metrics/exemplar/filter_type.h"
@@ -39,6 +40,22 @@ using namespace opentelemetry::sdk::metrics;
39
40
using namespace opentelemetry ::common;
40
41
namespace nostd = opentelemetry::nostd;
41
42
43
+ TEST (CardinalityLimit, ViewCardinalityLimitConfiguration)
44
+ {
45
+ // Test View without cardinality limit
46
+ View view_no_limit (" test_view_no_limit" );
47
+ EXPECT_FALSE (view_no_limit.HasAggregationCardinalityLimit ());
48
+ EXPECT_EQ (view_no_limit.GetAggregationCardinalityLimit (), 0 );
49
+
50
+ // Test View with cardinality limit
51
+ View view_with_limit (" test_view_with_limit" , " " , " " , AggregationType::kDefault , nullptr ,
52
+ std::unique_ptr<opentelemetry::sdk::metrics::AttributesProcessor>(
53
+ new opentelemetry::sdk::metrics::DefaultAttributesProcessor ()),
54
+ 500 );
55
+ EXPECT_TRUE (view_with_limit.HasAggregationCardinalityLimit ());
56
+ EXPECT_EQ (view_with_limit.GetAggregationCardinalityLimit (), 500 );
57
+ }
58
+
42
59
TEST (CardinalityLimit, AttributesHashMapBasicTests)
43
60
{
44
61
AttributesHashMap hash_map (10 );
@@ -156,3 +173,81 @@ TEST_P(WritableMetricStorageCardinalityLimitTestFixture, LongCounterSumAggregati
156
173
INSTANTIATE_TEST_SUITE_P (All,
157
174
WritableMetricStorageCardinalityLimitTestFixture,
158
175
::testing::Values (AggregationTemporality::kDelta ));
176
+
177
+ TEST (CardinalityLimit, SyncMetricStorageWithViewCardinalityLimit)
178
+ {
179
+ auto sdk_start_ts = std::chrono::system_clock::now ();
180
+ InstrumentDescriptor instr_desc = {" name" , " desc" , " 1unit" , InstrumentType::kCounter ,
181
+ InstrumentValueType::kLong };
182
+ std::shared_ptr<DefaultAttributesProcessor> default_attributes_processor{
183
+ new DefaultAttributesProcessor{}};
184
+
185
+ // Create a view with a cardinality limit of 5
186
+ View view_with_limit (" test_view" , " " , " " , AggregationType::kSum , nullptr ,
187
+ std::unique_ptr<opentelemetry::sdk::metrics::AttributesProcessor>(
188
+ new opentelemetry::sdk::metrics::DefaultAttributesProcessor ()),
189
+ 5 );
190
+
191
+ // Test that the view has the cardinality limit
192
+ EXPECT_TRUE (view_with_limit.HasAggregationCardinalityLimit ());
193
+ EXPECT_EQ (view_with_limit.GetAggregationCardinalityLimit (), 5 );
194
+
195
+ // Create SyncMetricStorage using the cardinality limit from the view
196
+ size_t cardinality_limit = view_with_limit.HasAggregationCardinalityLimit ()
197
+ ? view_with_limit.GetAggregationCardinalityLimit ()
198
+ : kAggregationCardinalityLimit ;
199
+ SyncMetricStorage storage (instr_desc, AggregationType::kSum , default_attributes_processor,
200
+ #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW
201
+ ExemplarFilterType::kAlwaysOff ,
202
+ ExemplarReservoir::GetNoExemplarReservoir (),
203
+ #endif
204
+ nullptr , cardinality_limit);
205
+
206
+ int64_t record_value = 100 ;
207
+ // Add 5 unique metric points (should all fit within limit)
208
+ for (auto i = 0 ; i < 5 ; i++)
209
+ {
210
+ std::map<std::string, std::string> attributes = {{" key" , std::to_string (i)}};
211
+ storage.RecordLong (record_value,
212
+ KeyValueIterableView<std::map<std::string, std::string>>(attributes),
213
+ opentelemetry::context::Context{});
214
+ }
215
+
216
+ // Add 3 more unique metric points (should trigger overflow behavior)
217
+ for (auto i = 5 ; i < 8 ; i++)
218
+ {
219
+ std::map<std::string, std::string> attributes = {{" key" , std::to_string (i)}};
220
+ storage.RecordLong (record_value,
221
+ KeyValueIterableView<std::map<std::string, std::string>>(attributes),
222
+ opentelemetry::context::Context{});
223
+ }
224
+
225
+ AggregationTemporality temporality = AggregationTemporality::kDelta ;
226
+ std::shared_ptr<CollectorHandle> collector (new MockCollectorHandle (temporality));
227
+ std::vector<std::shared_ptr<CollectorHandle>> collectors;
228
+ collectors.push_back (collector);
229
+ auto collection_ts = std::chrono::system_clock::now ();
230
+ size_t count_attributes = 0 ;
231
+ bool overflow_present = false ;
232
+
233
+ storage.Collect (
234
+ collector.get (), collectors, sdk_start_ts, collection_ts, [&](const MetricData &metric_data) {
235
+ for (const auto &data_attr : metric_data.point_data_attr_ )
236
+ {
237
+ count_attributes++;
238
+ if (data_attr.attributes .begin ()->first == kAttributesLimitOverflowKey )
239
+ {
240
+ // The overflow attribute should contain the aggregated values from the 3 excess metrics
241
+ const auto &data = opentelemetry::nostd::get<SumPointData>(data_attr.point_data );
242
+ EXPECT_EQ (nostd::get<int64_t >(data.value_ ), record_value * 3 );
243
+ overflow_present = true ;
244
+ }
245
+ }
246
+ return true ;
247
+ });
248
+
249
+ // We should have exactly 5 attributes (the cardinality limit)
250
+ EXPECT_EQ (count_attributes, 5 );
251
+ // And there should be an overflow attribute
252
+ EXPECT_TRUE (overflow_present);
253
+ }
0 commit comments