Skip to content

Commit a30be41

Browse files
[METRICS SDK] Use wildcard matching for view instrument names (#4520)
1 parent eec1b36 commit a30be41

8 files changed

Lines changed: 201 additions & 40 deletions

File tree

‎sdk/include/opentelemetry/sdk/metrics/view/instrument_selector.h‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ class InstrumentSelector
2020
public:
2121
InstrumentSelector(opentelemetry::sdk::metrics::InstrumentType instrument_type,
2222
const std::string &name,
23-
const std::string &units)
24-
: name_filter_{PredicateFactory::GetPredicate(name, PredicateType::kPattern)},
23+
const std::string &units,
24+
PredicateType name_predicate_type = PredicateType::kPattern)
25+
: name_filter_{PredicateFactory::GetPredicate(name, name_predicate_type)},
2526
unit_filter_{PredicateFactory::GetPredicate(units, PredicateType::kExact)},
2627
instrument_type_{instrument_type}
2728
{}

‎sdk/include/opentelemetry/sdk/metrics/view/predicate.h‎

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@
33

44
#pragma once
55

6-
#include <vector>
7-
#include "opentelemetry/common/macros.h"
8-
#include "opentelemetry/nostd/string_view.h"
9-
#include "opentelemetry/sdk/common/global_log_handler.h"
6+
#include <memory>
7+
#include <string>
108

11-
#if OPENTELEMETRY_HAVE_WORKING_REGEX
12-
# include <regex>
13-
#endif
9+
#include "opentelemetry/nostd/string_view.h"
10+
#include "opentelemetry/version.h"
1411

1512
OPENTELEMETRY_BEGIN_NAMESPACE
1613
namespace sdk
@@ -35,39 +32,39 @@ class Predicate
3532
class PatternPredicate : public Predicate
3633
{
3734
public:
38-
PatternPredicate(opentelemetry::nostd::string_view pattern) : reg_key_{pattern.data()} {}
39-
bool Match(opentelemetry::nostd::string_view str) const noexcept override
40-
{
41-
#if OPENTELEMETRY_HAVE_WORKING_REGEX
42-
return std::regex_match(str.data(), reg_key_);
43-
#else
44-
// TBD - Support regex match for GCC4.8
45-
OTEL_INTERNAL_LOG_ERROR(
46-
"PatternPredicate::Match - failed. std::regex not fully supported for this compiler.");
47-
return false; // not supported
48-
#endif
49-
}
35+
explicit PatternPredicate(opentelemetry::nostd::string_view pattern);
36+
37+
PatternPredicate(const PatternPredicate &) = delete;
38+
PatternPredicate(PatternPredicate &&) = delete;
39+
PatternPredicate &operator=(const PatternPredicate &) = delete;
40+
PatternPredicate &operator=(PatternPredicate &&) = delete;
41+
42+
~PatternPredicate() override;
43+
44+
bool Match(opentelemetry::nostd::string_view str) const noexcept override;
45+
46+
private:
47+
class Impl;
48+
std::unique_ptr<Impl> impl_;
49+
};
50+
51+
class WildcardPredicate : public Predicate
52+
{
53+
public:
54+
explicit WildcardPredicate(opentelemetry::nostd::string_view pattern);
55+
56+
bool Match(opentelemetry::nostd::string_view str) const noexcept override;
5057

5158
private:
52-
#if OPENTELEMETRY_HAVE_WORKING_REGEX
53-
std::regex reg_key_;
54-
#else
55-
std::string reg_key_;
56-
#endif
59+
std::string pattern_;
5760
};
5861

5962
class ExactPredicate : public Predicate
6063
{
6164
public:
62-
ExactPredicate(opentelemetry::nostd::string_view pattern) : pattern_{pattern} {}
63-
bool Match(opentelemetry::nostd::string_view str) const noexcept override
64-
{
65-
if (pattern_ == str)
66-
{
67-
return true;
68-
}
69-
return false;
70-
}
65+
explicit ExactPredicate(opentelemetry::nostd::string_view pattern);
66+
67+
bool Match(opentelemetry::nostd::string_view str) const noexcept override;
7168

7269
private:
7370
std::string pattern_;
@@ -76,13 +73,13 @@ class ExactPredicate : public Predicate
7673
class MatchEverythingPattern : public Predicate
7774
{
7875
public:
79-
bool Match(opentelemetry::nostd::string_view /* str */) const noexcept override { return true; }
76+
bool Match(opentelemetry::nostd::string_view str) const noexcept override;
8077
};
8178

8279
class MatchNothingPattern : public Predicate
8380
{
8481
public:
85-
bool Match(opentelemetry::nostd::string_view /* str */) const noexcept override { return false; }
82+
bool Match(opentelemetry::nostd::string_view str) const noexcept override;
8683
};
8784
} // namespace metrics
8885
} // namespace sdk

‎sdk/include/opentelemetry/sdk/metrics/view/predicate_factory.h‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ namespace metrics
1818
enum class PredicateType : uint8_t
1919
{
2020
kPattern,
21-
kExact
21+
kExact,
22+
kWildcard
2223
};
2324

2425
class PredicateFactory
@@ -27,7 +28,7 @@ class PredicateFactory
2728
static std::unique_ptr<Predicate> GetPredicate(opentelemetry::nostd::string_view pattern,
2829
PredicateType type)
2930
{
30-
if ((type == PredicateType::kPattern && pattern == "*") ||
31+
if (((type == PredicateType::kPattern || type == PredicateType::kWildcard) && pattern == "*") ||
3132
(type == PredicateType::kExact && pattern == ""))
3233
{
3334
return std::unique_ptr<Predicate>(new MatchEverythingPattern());
@@ -40,6 +41,10 @@ class PredicateFactory
4041
{
4142
return std::unique_ptr<Predicate>(new ExactPredicate(pattern));
4243
}
44+
if (type == PredicateType::kWildcard)
45+
{
46+
return std::unique_ptr<Predicate>(new WildcardPredicate(pattern));
47+
}
4348
return std::unique_ptr<Predicate>(new MatchNothingPattern());
4449
}
4550
};

‎sdk/src/configuration/sdk_builder.cc‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@
192192
#include "opentelemetry/sdk/metrics/view/attributes_processor.h"
193193
#include "opentelemetry/sdk/metrics/view/instrument_selector.h"
194194
#include "opentelemetry/sdk/metrics/view/meter_selector.h"
195+
#include "opentelemetry/sdk/metrics/view/predicate_factory.h"
195196
#include "opentelemetry/sdk/metrics/view/view.h"
196197
#include "opentelemetry/sdk/metrics/view/view_registry.h"
197198
#include "opentelemetry/sdk/metrics/view/view_registry_factory.h"
@@ -2068,7 +2069,8 @@ void SdkBuilder::AddView(
20682069

20692070
auto sdk_instrument_selector =
20702071
std::make_unique<opentelemetry::sdk::metrics::InstrumentSelector>(
2071-
sdk_instrument_type, instrument_name, selector->unit);
2072+
sdk_instrument_type, instrument_name, selector->unit,
2073+
opentelemetry::sdk::metrics::PredicateType::kWildcard);
20722074

20732075
auto sdk_meter_selector = std::make_unique<opentelemetry::sdk::metrics::MeterSelector>(
20742076
selector->meter_name, selector->meter_version, selector->meter_schema_url);

‎sdk/src/metrics/CMakeLists.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ add_library(
3333
view/attributes_processor.cc
3434
view/instrument_selector_factory.cc
3535
view/meter_selector_factory.cc
36+
view/predicate.cc
3637
view/view_factory.cc
3738
view/view_registry.cc
3839
view/view_registry_factory.cc

‎sdk/src/metrics/view/predicate.cc‎

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#include "opentelemetry/sdk/metrics/view/predicate.h"
5+
6+
#include <string>
7+
8+
#if OPENTELEMETRY_HAVE_WORKING_REGEX
9+
# include <regex>
10+
#endif
11+
12+
#if !OPENTELEMETRY_HAVE_WORKING_REGEX
13+
# include "opentelemetry/sdk/common/global_log_handler.h"
14+
#endif
15+
16+
#include "src/common/wildcard_match.h"
17+
18+
OPENTELEMETRY_BEGIN_NAMESPACE
19+
namespace sdk
20+
{
21+
namespace metrics
22+
{
23+
24+
class PatternPredicate::Impl
25+
{
26+
public:
27+
explicit Impl(opentelemetry::nostd::string_view pattern)
28+
#if OPENTELEMETRY_HAVE_WORKING_REGEX
29+
: reg_key_{std::string{pattern.data(), pattern.size()}}
30+
#else
31+
: reg_key_{pattern.data(), pattern.size()}
32+
#endif
33+
{}
34+
35+
#if OPENTELEMETRY_HAVE_WORKING_REGEX
36+
std::regex reg_key_;
37+
#else
38+
std::string reg_key_;
39+
#endif
40+
};
41+
42+
PatternPredicate::PatternPredicate(opentelemetry::nostd::string_view pattern)
43+
: impl_{new Impl(pattern)}
44+
{}
45+
46+
PatternPredicate::~PatternPredicate() = default;
47+
48+
bool PatternPredicate::Match(opentelemetry::nostd::string_view str) const noexcept
49+
{
50+
#if OPENTELEMETRY_HAVE_WORKING_REGEX
51+
return std::regex_match(str.data(), str.data() + str.size(), impl_->reg_key_);
52+
#else
53+
OTEL_INTERNAL_LOG_ERROR(
54+
"PatternPredicate::Match - failed. std::regex not fully supported for this compiler.");
55+
return false;
56+
#endif
57+
}
58+
59+
WildcardPredicate::WildcardPredicate(opentelemetry::nostd::string_view pattern)
60+
: pattern_{pattern.data(), pattern.size()}
61+
{}
62+
63+
bool WildcardPredicate::Match(opentelemetry::nostd::string_view str) const noexcept
64+
{
65+
return opentelemetry::sdk::common::WildcardMatch(pattern_, str);
66+
}
67+
68+
ExactPredicate::ExactPredicate(opentelemetry::nostd::string_view pattern)
69+
: pattern_{pattern.data(), pattern.size()}
70+
{}
71+
72+
bool ExactPredicate::Match(opentelemetry::nostd::string_view str) const noexcept
73+
{
74+
return pattern_ == str;
75+
}
76+
77+
bool MatchEverythingPattern::Match(opentelemetry::nostd::string_view /* str */) const noexcept
78+
{
79+
return true;
80+
}
81+
82+
bool MatchNothingPattern::Match(opentelemetry::nostd::string_view /* str */) const noexcept
83+
{
84+
return false;
85+
}
86+
87+
} // namespace metrics
88+
} // namespace sdk
89+
OPENTELEMETRY_END_NAMESPACE

‎sdk/test/configuration/sdk_builder_test.cc‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,41 @@ TEST(SdkBuilder, AddViewEmptySelectorMatchesAllSupportedInstrumentTypes)
11891189
}
11901190
}
11911191

1192+
TEST(SdkBuilder, AddViewUsesWildcardMatchingForInstrumentName)
1193+
{
1194+
namespace metrics_sdk = opentelemetry::sdk::metrics;
1195+
1196+
auto model = MakeCardinalityOnlyViewConfig(config_sdk::InstrumentType::counter, 42);
1197+
model->selector->instrument_name = "my.counter*";
1198+
1199+
auto registry = std::make_shared<config_sdk::Registry>();
1200+
config_sdk::SdkBuilder builder(registry);
1201+
metrics_sdk::ViewRegistry view_registry;
1202+
builder.AddView(&view_registry, model);
1203+
1204+
auto instrumentation_scope = scope_sdk::InstrumentationScope::Create("");
1205+
1206+
auto count_matches = [&](const std::string &name) {
1207+
metrics_sdk::InstrumentDescriptor instrument_descriptor{
1208+
name, "test description", "units", metrics_sdk::InstrumentType::kCounter,
1209+
metrics_sdk::InstrumentValueType::kLong};
1210+
int matched = 0;
1211+
view_registry.FindViews(instrument_descriptor, *instrumentation_scope,
1212+
[&](const metrics_sdk::View &view) {
1213+
auto *config = view.GetAggregationConfig();
1214+
if (config != nullptr && config->cardinality_limit_ == 42u)
1215+
{
1216+
++matched;
1217+
}
1218+
return true;
1219+
});
1220+
return matched;
1221+
};
1222+
1223+
EXPECT_EQ(count_matches("my.counter_one"), 1);
1224+
EXPECT_EQ(count_matches("myXcounter_one"), 0);
1225+
}
1226+
11921227
TEST(SdkBuilder, AddViewHistogramCardinalityLimitOnly)
11931228
{
11941229
namespace metrics_sdk = opentelemetry::sdk::metrics;

‎sdk/test/metrics/view_registry_test.cc‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,43 @@
1212
#include "opentelemetry/sdk/metrics/instruments.h"
1313
#include "opentelemetry/sdk/metrics/view/instrument_selector.h"
1414
#include "opentelemetry/sdk/metrics/view/meter_selector.h"
15+
#include "opentelemetry/sdk/metrics/view/predicate.h"
16+
#include "opentelemetry/sdk/metrics/view/predicate_factory.h"
1517
#include "opentelemetry/sdk/metrics/view/view.h"
1618
#include "opentelemetry/sdk/metrics/view/view_registry.h"
1719

1820
using namespace opentelemetry::sdk::metrics;
1921
using namespace opentelemetry::sdk::instrumentationscope;
2022

23+
TEST(InstrumentSelector, NameFilterSupportsExplicitWildcardMatching)
24+
{
25+
InstrumentSelector star_selector{InstrumentType::kCounter, "my_counter*", "",
26+
PredicateType::kWildcard};
27+
EXPECT_TRUE(star_selector.GetNameFilter()->Match("my_counter"));
28+
EXPECT_TRUE(star_selector.GetNameFilter()->Match("my_counter_one"));
29+
EXPECT_TRUE(star_selector.GetNameFilter()->Match("my_counter_two"));
30+
EXPECT_FALSE(star_selector.GetNameFilter()->Match("other_counter"));
31+
32+
InstrumentSelector question_selector{InstrumentType::kCounter, "my_counter_?", "",
33+
PredicateType::kWildcard};
34+
EXPECT_TRUE(question_selector.GetNameFilter()->Match("my_counter_1"));
35+
EXPECT_FALSE(question_selector.GetNameFilter()->Match("my_counter_12"));
36+
37+
InstrumentSelector literal_selector{InstrumentType::kCounter, "my.counter*", "",
38+
PredicateType::kWildcard};
39+
EXPECT_TRUE(literal_selector.GetNameFilter()->Match("my.counter_one"));
40+
EXPECT_FALSE(literal_selector.GetNameFilter()->Match("myXcounter_one"));
41+
}
42+
43+
#if OPENTELEMETRY_HAVE_WORKING_REGEX
44+
TEST(InstrumentSelector, NameFilterDefaultsToPatternMatching)
45+
{
46+
InstrumentSelector selector{InstrumentType::kCounter, "my\\.counter.*", ""};
47+
EXPECT_TRUE(selector.GetNameFilter()->Match("my.counter_one"));
48+
EXPECT_FALSE(selector.GetNameFilter()->Match("myXcounter_one"));
49+
}
50+
#endif
51+
2152
TEST(ViewRegistry, FindViewsEmptyRegistry)
2253
{
2354
InstrumentDescriptor default_instrument_descriptor = {

0 commit comments

Comments
 (0)