diff --git a/api/include/opentelemetry/common/macros.h b/api/include/opentelemetry/common/macros.h index 8f88fc8f85..4741677a74 100644 --- a/api/include/opentelemetry/common/macros.h +++ b/api/include/opentelemetry/common/macros.h @@ -90,6 +90,14 @@ # define OPENTELEMETRY_DEPRECATED_MESSAGE(msg) #endif +// Regex support +#if (__GNUC__ == 4 && (__GNUC_MINOR__ == 8 || __GNUC_MINOR__ == 9)) +# define HAVE_WORKING_REGEX 0 +#else +# include +# define HAVE_WORKING_REGEX 1 +#endif + /* clang-format off */ /** diff --git a/sdk/include/opentelemetry/sdk/metrics/instrument_metadata_validator.h b/sdk/include/opentelemetry/sdk/metrics/instrument_metadata_validator.h new file mode 100644 index 0000000000..97e7550cde --- /dev/null +++ b/sdk/include/opentelemetry/sdk/metrics/instrument_metadata_validator.h @@ -0,0 +1,38 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#ifndef ENABLE_METRICS_PREVIEW +# include +# include "opentelemetry/common/macros.h" +# include "opentelemetry/nostd/string_view.h" +# include "opentelemetry/version.h" + +# if HAVE_WORKING_REGEX +# include +# endif + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +class InstrumentMetaDataValidator +{ +public: + InstrumentMetaDataValidator(); + bool ValidateName(nostd::string_view name) const; + bool ValidateUnit(nostd::string_view unit) const; + bool ValidateDescription(nostd::string_view description) const; + +private: +# if HAVE_WORKING_REGEX + const std::regex name_reg_key_; + const std::regex unit_reg_key_; +# endif +}; + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/include/opentelemetry/sdk/metrics/meter.h b/sdk/include/opentelemetry/sdk/metrics/meter.h index c9d9fe1566..80f9d5bc67 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter.h @@ -5,7 +5,9 @@ #ifndef ENABLE_METRICS_PREVIEW # include # include "opentelemetry/metrics/meter.h" +# include "opentelemetry/metrics/noop.h" # include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +# include "opentelemetry/sdk/metrics/instrument_metadata_validator.h" # include "opentelemetry/sdk/metrics/instruments.h" # include "opentelemetry/sdk/metrics/meter_context.h" # include "opentelemetry/sdk/metrics/state/async_metric_storage.h" @@ -121,6 +123,23 @@ class Meter final : public opentelemetry::metrics::Meter std::unique_ptr RegisterAsyncMetricStorage( InstrumentDescriptor &instrument_descriptor); opentelemetry::common::SpinLockMutex storage_lock_; + const InstrumentMetaDataValidator instrument_metadata_validator; + + static nostd::shared_ptr + GetNoopObservableInsrument() + { + static nostd::shared_ptr noop_instrument( + new opentelemetry::metrics::NoopObservableInstrument("", "", "")); + return noop_instrument; + } + static bool ValidateInstrument(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit) + { + const static InstrumentMetaDataValidator instrument_validator; + return instrument_validator.ValidateName(name) && instrument_validator.ValidateUnit(unit) && + instrument_validator.ValidateDescription(description); + } }; } // namespace metrics } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/metrics/view/predicate.h b/sdk/include/opentelemetry/sdk/metrics/view/predicate.h index 2fe5fbf155..b60a338b7f 100644 --- a/sdk/include/opentelemetry/sdk/metrics/view/predicate.h +++ b/sdk/include/opentelemetry/sdk/metrics/view/predicate.h @@ -4,15 +4,9 @@ #pragma once #ifndef ENABLE_METRICS_PREVIEW # include -# if (__GNUC__ == 4 && (__GNUC_MINOR__ == 8 || __GNUC_MINOR__ == 9)) -# define HAVE_WORKING_REGEX 0 -# include "opentelemetry/sdk/common/global_log_handler.h" -# else -# include -# define HAVE_WORKING_REGEX 1 -# endif - +# include "opentelemetry/common/macros.h" # include "opentelemetry/nostd/string_view.h" +# include "opentelemetry/sdk/common/global_log_handler.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk diff --git a/sdk/src/metrics/CMakeLists.txt b/sdk/src/metrics/CMakeLists.txt index 80c1bff335..7f9214e324 100644 --- a/sdk/src/metrics/CMakeLists.txt +++ b/sdk/src/metrics/CMakeLists.txt @@ -5,6 +5,7 @@ add_library( meter.cc meter_context.cc metric_reader.cc + instrument_metadata_validator.cc export/periodic_exporting_metric_reader.cc state/metric_collector.cc state/observable_registry.cc diff --git a/sdk/src/metrics/instrument_metadata_validator.cc b/sdk/src/metrics/instrument_metadata_validator.cc new file mode 100644 index 0000000000..8dd0930e77 --- /dev/null +++ b/sdk/src/metrics/instrument_metadata_validator.cc @@ -0,0 +1,80 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/instrument_metadata_validator.h" +# include "opentelemetry/common/macros.h" +# include "opentelemetry/nostd/string_view.h" + +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +// instrument-name = ALPHA 0*62 ("_" / "." / "-" / ALPHA / DIGIT) +const std::string kInstrumentNamePattern = "[a-zA-Z][-_.a-zA-Z0-9]{0,62}"; +// +const std::string kInstrumentUnitPattern = "[\x01-\x7F]{0,63}"; +// instrument-unit = It can have a maximum length of 63 ASCII chars + +InstrumentMetaDataValidator::InstrumentMetaDataValidator() +# if HAVE_WORKING_REGEX + // clang-format off + : name_reg_key_{kInstrumentNamePattern}, + unit_reg_key_{kInstrumentUnitPattern} +// clang-format on +# endif +{} + +bool InstrumentMetaDataValidator::ValidateName(nostd::string_view name) const +{ + +# if HAVE_WORKING_REGEX + return std::regex_match(name.data(), name_reg_key_); +# else + const size_t kMaxSize = 63; + // size atmost 63 chars + if (name.size() > kMaxSize) + { + return false; + } + // first char should be alpha + if (!isalpha(name[0])) + { + return false; + } + // subsequent chars should be either of alphabets, digits, underscore, minus, dot + return !std::any_of(std::next(name.begin()), name.end(), + [](char c) { return !isalnum(c) && c != '-' && c != '_' && c != '.'; }); +# endif +} + +bool InstrumentMetaDataValidator::ValidateUnit(nostd::string_view unit) const +{ +# if HAVE_WORKING_REGEX + return std::regex_match(unit.data(), unit_reg_key_); +# else + const size_t kMaxSize = 63; + // length atmost 63 chars + if (unit.size() > kMaxSize) + { + return false; + } + // all should be ascii chars. + return !std::any_of(unit.begin(), unit.end(), + [](char c) { return static_cast(c) > 127; }); +# endif +} + +bool InstrumentMetaDataValidator::ValidateDescription(nostd::string_view /*description*/) const +{ + return true; +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/src/metrics/meter.cc b/sdk/src/metrics/meter.cc index bff0cf5535..e5848e8619 100644 --- a/sdk/src/metrics/meter.cc +++ b/sdk/src/metrics/meter.cc @@ -12,6 +12,7 @@ # include "opentelemetry/sdk/metrics/state/observable_registry.h" # include "opentelemetry/sdk/metrics/state/sync_metric_storage.h" +# include "opentelemetry/sdk/common/global_log_handler.h" # include "opentelemetry/sdk/metrics/sync_instruments.h" # include "opentelemetry/sdk_config.h" @@ -39,6 +40,14 @@ nostd::unique_ptr> Meter::CreateUInt64Counter( nostd::string_view description, nostd::string_view unit) noexcept { + if (!ValidateInstrument(name, description, unit)) + { + OTEL_INTERNAL_LOG_ERROR("Meter::CreateUInt64Counter - failed. Invalid parameters." + << name << " " << description << " " << unit + << ". Measurements won't be recorded."); + return nostd::unique_ptr>( + new metrics::NoopCounter(name, description, unit)); + } InstrumentDescriptor instrument_descriptor = { std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, std::string{unit.data(), unit.size()}, InstrumentType::kCounter, InstrumentValueType::kLong}; @@ -52,6 +61,14 @@ nostd::unique_ptr> Meter::CreateDoubleCounter( nostd::string_view description, nostd::string_view unit) noexcept { + if (!ValidateInstrument(name, description, unit)) + { + OTEL_INTERNAL_LOG_ERROR("Meter::CreateDoubleCounter - failed. Invalid parameters." + << name << " " << description << " " << unit + << ". Measurements won't be recorded."); + return nostd::unique_ptr>( + new metrics::NoopCounter(name, description, unit)); + } InstrumentDescriptor instrument_descriptor = { std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, std::string{unit.data(), unit.size()}, InstrumentType::kCounter, @@ -66,6 +83,13 @@ nostd::shared_ptr Meter::CreateInt nostd::string_view description, nostd::string_view unit) noexcept { + if (!ValidateInstrument(name, description, unit)) + { + OTEL_INTERNAL_LOG_ERROR("Meter::CreateInt64ObservableCounter - failed. Invalid parameters." + << name << " " << description << " " << unit + << ". Measurements won't be recorded."); + return GetNoopObservableInsrument(); + } InstrumentDescriptor instrument_descriptor = { std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, std::string{unit.data(), unit.size()}, InstrumentType::kObservableCounter, @@ -80,6 +104,13 @@ Meter::CreateDoubleObservableCounter(nostd::string_view name, nostd::string_view description, nostd::string_view unit) noexcept { + if (!ValidateInstrument(name, description, unit)) + { + OTEL_INTERNAL_LOG_ERROR("Meter::CreateDoubleObservableCounter - failed. Invalid parameters." + << name << " " << description << " " << unit + << ". Measurements won't be recorded."); + return GetNoopObservableInsrument(); + } InstrumentDescriptor instrument_descriptor = { std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, std::string{unit.data(), unit.size()}, InstrumentType::kObservableCounter, @@ -94,6 +125,14 @@ nostd::unique_ptr> Meter::CreateUInt64Histogram( nostd::string_view description, nostd::string_view unit) noexcept { + if (!ValidateInstrument(name, description, unit)) + { + OTEL_INTERNAL_LOG_ERROR("Meter::CreateUInt64Histogram - failed. Invalid parameters." + << name << " " << description << " " << unit + << ". Measurements won't be recorded."); + return nostd::unique_ptr>( + new metrics::NoopHistogram(name, description, unit)); + } InstrumentDescriptor instrument_descriptor = { std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, std::string{unit.data(), unit.size()}, InstrumentType::kHistogram, @@ -108,6 +147,14 @@ nostd::unique_ptr> Meter::CreateDoubleHistogram( nostd::string_view description, nostd::string_view unit) noexcept { + if (!ValidateInstrument(name, description, unit)) + { + OTEL_INTERNAL_LOG_ERROR("Meter::CreateDoubleHistogram - failed. Invalid parameters." + << name << " " << description << " " << unit + << ". Measurements won't be recorded."); + return nostd::unique_ptr>( + new metrics::NoopHistogram(name, description, unit)); + } InstrumentDescriptor instrument_descriptor = { std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, std::string{unit.data(), unit.size()}, InstrumentType::kHistogram, @@ -122,6 +169,13 @@ nostd::shared_ptr Meter::CreateInt nostd::string_view description, nostd::string_view unit) noexcept { + if (!ValidateInstrument(name, description, unit)) + { + OTEL_INTERNAL_LOG_ERROR("Meter::CreateInt64ObservableGauge - failed. Invalid parameters." + << name << " " << description << " " << unit + << ". Measurements won't be recorded."); + return GetNoopObservableInsrument(); + } InstrumentDescriptor instrument_descriptor = { std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, std::string{unit.data(), unit.size()}, InstrumentType::kObservableGauge, @@ -136,6 +190,13 @@ nostd::shared_ptr Meter::CreateDou nostd::string_view description, nostd::string_view unit) noexcept { + if (!ValidateInstrument(name, description, unit)) + { + OTEL_INTERNAL_LOG_ERROR("Meter::CreateDoubleObservableGauge - failed. Invalid parameters." + << name << " " << description << " " << unit + << ". Measurements won't be recorded."); + return GetNoopObservableInsrument(); + } InstrumentDescriptor instrument_descriptor = { std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, std::string{unit.data(), unit.size()}, InstrumentType::kObservableGauge, @@ -150,6 +211,14 @@ nostd::unique_ptr> Meter::CreateInt64UpDownCount nostd::string_view description, nostd::string_view unit) noexcept { + if (!ValidateInstrument(name, description, unit)) + { + OTEL_INTERNAL_LOG_ERROR("Meter::CreateInt64UpDownCounter - failed. Invalid parameters." + << name << " " << description << " " << unit + << ". Measurements won't be recorded."); + return nostd::unique_ptr>( + new metrics::NoopUpDownCounter(name, description, unit)); + } InstrumentDescriptor instrument_descriptor = { std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, std::string{unit.data(), unit.size()}, InstrumentType::kUpDownCounter, @@ -164,6 +233,14 @@ nostd::unique_ptr> Meter::CreateDoubleUpDownCount nostd::string_view description, nostd::string_view unit) noexcept { + if (!ValidateInstrument(name, description, unit)) + { + OTEL_INTERNAL_LOG_ERROR("Meter::CreateDoubleUpDownCounter - failed. Invalid parameters." + << name << " " << description << " " << unit + << ". Measurements won't be recorded."); + return nostd::unique_ptr>( + new metrics::NoopUpDownCounter(name, description, unit)); + } InstrumentDescriptor instrument_descriptor = { std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, std::string{unit.data(), unit.size()}, InstrumentType::kUpDownCounter, @@ -178,6 +255,13 @@ Meter::CreateInt64ObservableUpDownCounter(nostd::string_view name, nostd::string_view description, nostd::string_view unit) noexcept { + if (!ValidateInstrument(name, description, unit)) + { + OTEL_INTERNAL_LOG_ERROR( + "Meter::CreateInt64ObservableUpDownCounter - failed. Invalid parameters -" + << name << " " << description << " " << unit << ". Measurements won't be recorded."); + return GetNoopObservableInsrument(); + } InstrumentDescriptor instrument_descriptor = { std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, std::string{unit.data(), unit.size()}, InstrumentType::kObservableUpDownCounter, @@ -192,6 +276,13 @@ Meter::CreateDoubleObservableUpDownCounter(nostd::string_view name, nostd::string_view description, nostd::string_view unit) noexcept { + if (!ValidateInstrument(name, description, unit)) + { + OTEL_INTERNAL_LOG_ERROR( + "Meter::CreateDoubleObservableUpDownCounter - failed. Invalid parameters." + << name << " " << description << " " << unit << ". Measurements won't be recorded."); + return GetNoopObservableInsrument(); + } InstrumentDescriptor instrument_descriptor = { std::string{name.data(), name.size()}, std::string{description.data(), description.size()}, std::string{unit.data(), unit.size()}, InstrumentType::kObservableUpDownCounter, diff --git a/sdk/test/metrics/CMakeLists.txt b/sdk/test/metrics/CMakeLists.txt index 2ff8b5f30a..3e5e8f488a 100644 --- a/sdk/test/metrics/CMakeLists.txt +++ b/sdk/test/metrics/CMakeLists.txt @@ -16,7 +16,8 @@ foreach( async_instruments_test metric_reader_test observable_registry_test - periodic_exporting_metric_reader_test) + periodic_exporting_metric_reader_test + instrument_metadata_validator_test) add_executable(${testname} "${testname}.cc") target_link_libraries( ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} diff --git a/sdk/test/metrics/instrument_metadata_validator_test.cc b/sdk/test/metrics/instrument_metadata_validator_test.cc new file mode 100644 index 0000000000..df7e7be594 --- /dev/null +++ b/sdk/test/metrics/instrument_metadata_validator_test.cc @@ -0,0 +1,75 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ENABLE_METRICS_PREVIEW +# include "opentelemetry/sdk/metrics/instrument_metadata_validator.h" +# include + +static std::string CreateVeryLargeString(size_t multiple) +{ + std::string result = "START"; + std::string repeater = "0123456789"; + for (size_t i = 0; i < multiple; i++) + { + result += repeater; + } + return result; +} + +TEST(InstrumentMetadataValidator, TestName) +{ + opentelemetry::sdk::metrics::InstrumentMetaDataValidator validator; + std::vector invalid_names = { + "", // empty string + "1sdf", // string starting with number + "123€AAA€BBB", // unicode characters + "/\\sdsd", // string starting with special character + "***sSSs", // string starting with special character + CreateVeryLargeString(5) + "ABCERTYGJ", // total 64 charactes + CreateVeryLargeString(7), // string much bigger than 63 chars + }; + for (auto const &str : invalid_names) + { + EXPECT_FALSE(validator.ValidateName(str)); + } + + std::vector valid_names = { + "T", // single char string + "s123", // starting with char, followed by numbers + "dsdsdsd_-.", // string , and valid nonalphanumeric + "d1234_-sDSDs.sdsd344", // combination of all valid characters + CreateVeryLargeString(5) + "ABCERTYG", // total 63 charactes + }; + for (auto const &str : valid_names) + { + EXPECT_TRUE(validator.ValidateName(str)); + } +} + +TEST(InstrumentMetadataValidator, TestUnit) +{ + opentelemetry::sdk::metrics::InstrumentMetaDataValidator validator; + std::vector invalid_units = { + CreateVeryLargeString(5) + "ABCERTYGJ", // total 64 charactes + CreateVeryLargeString(7), // string bigger than 63 chars + "123€AAA€BBB", // unicode string + }; + for (auto const &str : invalid_units) + { + EXPECT_FALSE(validator.ValidateUnit(str)); + } + + std::vector valid_units = { + "T", // single char + "s123", // starting with char, followed by numbers + "dsdsdsd_-.", // string , and valid nonalphanumeric + "d1234_-sdsds.sdsd344", // combination of all valid characters + CreateVeryLargeString(5) + "ABCERTYG", // total 63 charactes + "ASDDSDF", // uppercase + }; + for (auto const &str : valid_units) + { + EXPECT_TRUE(validator.ValidateName(str)); + } +} +#endif