diff --git a/sdk/include/opentelemetry/sdk/metrics/meter.h b/sdk/include/opentelemetry/sdk/metrics/meter.h index ccb96ea804..001866dd99 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter.h @@ -1,9 +1,14 @@ #pragma once #include "opentelemetry/metrics/meter.h" -#include "opentelemetry/version.h" +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/sdk/metrics/async_instruments.h" +#include "opentelemetry/sdk/metrics/instrument.h" +#include "opentelemetry/sdk/metrics/record.h" +#include "opentelemetry/sdk/metrics/sync_instruments.h" -#include +#include +#include OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk @@ -11,7 +16,7 @@ namespace sdk namespace metrics { namespace metrics_api = opentelemetry::metrics; -class Meter final : public metrics_api::Meter, public std::enable_shared_from_this +class Meter : public metrics_api::Meter { public: explicit Meter(std::string library_name, std::string library_version = "") @@ -20,254 +25,351 @@ class Meter final : public metrics_api::Meter, public std::enable_shared_from_th library_version_ = library_version; } + /** + * Creates a Counter with the passed characteristics and returns a shared_ptr to that Counter. + * + * @param name the name of the new Counter. + * @param description a brief description of what the Counter is used for. + * @param unit the unit of metric values following https://unitsofmeasure.org/ucum.html. + * @param enabled a boolean value that turns on or off the metric instrument. + * @return a shared pointer to the created Counter. + * @throws invalid_argument exception if name is null or does not conform to OTel syntax. + */ nostd::shared_ptr> NewShortCounter(nostd::string_view name, nostd::string_view description, nostd::string_view unit, - const bool enabled) override - { - return nostd::shared_ptr>(nullptr); - } + const bool enabled) override; nostd::shared_ptr> NewIntCounter(nostd::string_view name, nostd::string_view description, nostd::string_view unit, - const bool enabled) override - { - return nostd::shared_ptr>(nullptr); - } + const bool enabled) override; nostd::shared_ptr> NewFloatCounter(nostd::string_view name, nostd::string_view description, nostd::string_view unit, - const bool enabled) override - { - return nostd::shared_ptr>(nullptr); - } + const bool enabled) override; nostd::shared_ptr> NewDoubleCounter(nostd::string_view name, nostd::string_view description, nostd::string_view unit, - const bool enabled) override - { - return nostd::shared_ptr>(nullptr); - } - + const bool enabled) override; + + /** + * Creates an UpDownCounter with the passed characteristics and returns a shared_ptr to that + * UpDownCounter. + * + * @param name the name of the new UpDownCounter. + * @param description a brief description of what the UpDownCounter is used for. + * @param unit the unit of metric values following https://unitsofmeasure.org/ucum.html. + * @param enabled a boolean value that turns on or off the metric instrument. + * @return a shared pointer to the created UpDownCounter. + * @throws invalid_argument exception if name is null or does not conform to OTel syntax. + */ nostd::shared_ptr> NewShortUpDownCounter( nostd::string_view name, nostd::string_view description, nostd::string_view unit, - const bool enabled) override - { - return nostd::shared_ptr>(nullptr); - } + const bool enabled) override; nostd::shared_ptr> NewIntUpDownCounter( nostd::string_view name, nostd::string_view description, nostd::string_view unit, - const bool enabled) override - { - return nostd::shared_ptr>(nullptr); - } + const bool enabled) override; nostd::shared_ptr> NewFloatUpDownCounter( nostd::string_view name, nostd::string_view description, nostd::string_view unit, - const bool enabled) override - { - return nostd::shared_ptr>(nullptr); - } + const bool enabled) override; nostd::shared_ptr> NewDoubleUpDownCounter( nostd::string_view name, nostd::string_view description, nostd::string_view unit, - const bool enabled) override - { - return nostd::shared_ptr>(nullptr); - } - + const bool enabled) override; + + /** + * Creates a ValueRecorder with the passed characteristics and returns a shared_ptr to that + * ValueRecorder. + * + * @param name the name of the new ValueRecorder. + * @param description a brief description of what the ValueRecorder is used for. + * @param unit the unit of metric values following https://unitsofmeasure.org/ucum.html. + * @param enabled a boolean value that turns on or off the metric instrument. + * @return a shared pointer to the created DoubleValueRecorder. + * @throws invalid_argument exception if name is null or does not conform to OTel syntax. + */ nostd::shared_ptr> NewShortValueRecorder( nostd::string_view name, nostd::string_view description, nostd::string_view unit, - const bool enabled) override - { - return nostd::shared_ptr>(nullptr); - } + const bool enabled) override; nostd::shared_ptr> NewIntValueRecorder( nostd::string_view name, nostd::string_view description, nostd::string_view unit, - const bool enabled) override - { - return nostd::shared_ptr>(nullptr); - } + const bool enabled) override; nostd::shared_ptr> NewFloatValueRecorder( nostd::string_view name, nostd::string_view description, nostd::string_view unit, - const bool enabled) override - { - return nostd::shared_ptr>(nullptr); - } + const bool enabled) override; nostd::shared_ptr> NewDoubleValueRecorder( nostd::string_view name, nostd::string_view description, nostd::string_view unit, - const bool enabled) override - { - return nostd::shared_ptr>(nullptr); - } - + const bool enabled) override; + + /** + * Creates a SumObserver with the passed characteristics and returns a shared_ptr to that + * SumObserver. + * + * @param name the name of the new SumObserver. + * @param description a brief description of what the SumObserver is used for. + * @param unit the unit of metric values following https://unitsofmeasure.org/ucum.html. + * @param enabled a boolean value that turns on or off the metric instrument. + * @param callback the function to be observed by the instrument. + * @return a shared pointer to the created SumObserver. + * @throws invalid_argument exception if name is null or does not conform to OTel syntax. + */ nostd::shared_ptr> NewShortSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, const bool enabled, - void (*callback)(metrics_api::ObserverResult)) override - { - return nostd::shared_ptr>(nullptr); - } + void (*callback)(metrics_api::ObserverResult)) override; nostd::shared_ptr> NewIntSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, const bool enabled, - void (*callback)(metrics_api::ObserverResult)) override - { - return nostd::shared_ptr>(nullptr); - } + void (*callback)(metrics_api::ObserverResult)) override; nostd::shared_ptr> NewFloatSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, const bool enabled, - void (*callback)(metrics_api::ObserverResult)) override - { - return nostd::shared_ptr>(nullptr); - } + void (*callback)(metrics_api::ObserverResult)) override; nostd::shared_ptr> NewDoubleSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, const bool enabled, - void (*callback)(metrics_api::ObserverResult)) override - { - return nostd::shared_ptr>(nullptr); - } - + void (*callback)(metrics_api::ObserverResult)) override; + + /** + * Creates an UpDownSumObserver with the passed characteristics and returns a shared_ptr to + * that UpDowNSumObserver. + * + * @param name the name of the new UpDownSumObserver. + * @param description a brief description of what the UpDownSumObserver is used for. + * @param unit the unit of metric values following https://unitsofmeasure.org/ucum.html. + * @param enabled a boolean value that turns on or off the metric instrument. + * @param callback the function to be observed by the instrument. + * @return a shared pointer to the created UpDownSumObserver. + * @throws invalid_argument exception if name is null or does not conform to OTel syntax. + */ nostd::shared_ptr> NewShortUpDownSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, const bool enabled, - void (*callback)(metrics_api::ObserverResult)) override - { - return nostd::shared_ptr>(nullptr); - } + void (*callback)(metrics_api::ObserverResult)) override; nostd::shared_ptr> NewIntUpDownSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, const bool enabled, - void (*callback)(metrics_api::ObserverResult)) override - { - return nostd::shared_ptr>(nullptr); - } + void (*callback)(metrics_api::ObserverResult)) override; nostd::shared_ptr> NewFloatUpDownSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, const bool enabled, - void (*callback)(metrics_api::ObserverResult)) override - { - return nostd::shared_ptr>(nullptr); - } + void (*callback)(metrics_api::ObserverResult)) override; nostd::shared_ptr> NewDoubleUpDownSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, const bool enabled, - void (*callback)(metrics_api::ObserverResult)) override - { - return nostd::shared_ptr>(nullptr); - } - + void (*callback)(metrics_api::ObserverResult)) override; + + /** + * Creates a ValueObserver with the passed characteristics and returns a shared_ptr to that + * ValueObserver. + * + * @param name the name of the new ValueObserver. + * @param description a brief description of what the ValueObserver is used for. + * @param unit the unit of metric values following https://unitsofmeasure.org/ucum.html. + * @param enabled a boolean value that turns on or off the metric instrument. + * @param callback the function to be observed by the instrument. + * @return a shared pointer to the created ValueObserver. + * @throws invalid_argument exception if name is null or does not conform to OTel syntax. + */ nostd::shared_ptr> NewShortValueObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, const bool enabled, - void (*callback)(metrics_api::ObserverResult)) override - { - return nostd::shared_ptr>(nullptr); - } + void (*callback)(metrics_api::ObserverResult)) override; nostd::shared_ptr> NewIntValueObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, const bool enabled, - void (*callback)(metrics_api::ObserverResult)) override - { - return nostd::shared_ptr>(nullptr); - } + void (*callback)(metrics_api::ObserverResult)) override; nostd::shared_ptr> NewFloatValueObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, const bool enabled, - void (*callback)(metrics_api::ObserverResult)) override - { - return nostd::shared_ptr>(nullptr); - } + void (*callback)(metrics_api::ObserverResult)) override; nostd::shared_ptr> NewDoubleValueObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, const bool enabled, - void (*callback)(metrics_api::ObserverResult)) override - { - return nostd::shared_ptr>(nullptr); - } - + void (*callback)(metrics_api::ObserverResult)) override; + + /** + * Utility method that allows users to atomically record measurements to a set of + * synchronous metric instruments with a common set of labels. + * + * @param labels the set of labels to associate with this recorder. + * @param values a span of pairs where the first element of the pair is a metric instrument + * to record to, and the second element is the value to update that instrument with. + */ void RecordShortBatch(const trace::KeyValueIterable &labels, nostd::span *> instruments, - nostd::span values) noexcept override - {} + nostd::span values) noexcept override; void RecordIntBatch(const trace::KeyValueIterable &labels, nostd::span *> instruments, - nostd::span values) noexcept override - {} + nostd::span values) noexcept override; void RecordFloatBatch(const trace::KeyValueIterable &labels, nostd::span *> instruments, - nostd::span values) noexcept override - {} + nostd::span values) noexcept override; void RecordDoubleBatch(const trace::KeyValueIterable &labels, nostd::span *> instruments, - nostd::span values) noexcept override - {} + nostd::span values) noexcept override; + + /** + * An SDK-only function that checkpoints the aggregators of all instruments created from + * this meter, creates a {@code Record} out of them, and sends them for export. + * + * @return A vector of {@code Records} to be sent to the processor. + */ + std::vector Collect() noexcept; private: + /** + * A private function that creates records from all synchronous instruments created from + * this meter. + * + * @param records A reference to the vector to push the new records to. + */ + void CollectMetrics(std::vector &records); + + /** + * Helper function to collect Records from a single synchronous instrument + * + * @tparam T The integral type of the instrument to collect from. + * @param i A map iterator pointing to the instrument to collect from + * @param records The vector to add the new records to. + */ + template + void CollectSingleSyncInstrument( + typename std::map>>::iterator i, + std::vector &records); + + /** + * A private function that creates records from all asynchronous instruments created from + * this meter. + * + * @param records A reference to the vector to push the new records to. + */ + void CollectObservers(std::vector &records); + + /** + * Helper function to collect Records from a single asynchronous instrument + * + * @tparam T The integral type of the instrument to collect from. + * @param i A map iterator pointing to the instrument to collect from + * @param records The vector to add the new records to. + */ + template + void CollectSingleAsyncInstrument( + typename std::map>>::iterator i, + std::vector &records); + + /** + * Utility function used by the meter that checks if a user-passed name abides by OpenTelemetry + * naming rules. The rules are as follows: + * 1. The name must not be empty. + * 2. The name must not start with a digit, a space, or any punctuation. + * 3. The name must only have the following chaacters: + * All alphanumeric characters, '.', '_' and '-'. + * + * @param name The name to be examined for legality. + * @return A bool representing whether the name is valid by the OpenTelemetry syntax rules. + */ + bool IsValidName(nostd::string_view name); + + /** + * A utility function used by the meter to determine whether an instrument of a specified + * name already exists in this meter. + * + * @param name The name to examine. + * @return A boolean representing whether the name has already been used by this meter. + */ + bool NameAlreadyUsed(nostd::string_view name); + + /* + * All instruments must be stored in a map so the meter can collect on these instruments. + * Additionally, when creating a new instrument, the meter must check if an instrument of the same + * name already exists. + */ + std::map>> short_metrics_; + std::map>> int_metrics_; + std::map>> float_metrics_; + std::map>> + double_metrics_; + + std::map>> + short_observers_; + std::map>> int_observers_; + std::map>> + float_observers_; + std::map>> + double_observers_; + + std::unordered_set names_; + std::string library_name_; std::string library_version_; + + std::mutex metrics_lock_; + std::mutex observers_lock_; }; + } // namespace metrics } // namespace sdk OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/metrics/CMakeLists.txt b/sdk/src/metrics/CMakeLists.txt index 0a5faed745..c147a3e3c2 100644 --- a/sdk/src/metrics/CMakeLists.txt +++ b/sdk/src/metrics/CMakeLists.txt @@ -1 +1,2 @@ -add_library(opentelemetry_metrics meter_provider.cc ungrouped_processor.cc) +add_library(opentelemetry_metrics meter_provider.cc meter.cc + ungrouped_processor.cc) diff --git a/sdk/src/metrics/meter.cc b/sdk/src/metrics/meter.cc new file mode 100644 index 0000000000..f856481a5e --- /dev/null +++ b/sdk/src/metrics/meter.cc @@ -0,0 +1,764 @@ +#include "opentelemetry/sdk/metrics/meter.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ +nostd::shared_ptr> Meter::NewShortCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto counter = new Counter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(counter); + metrics_lock_.lock(); + short_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewIntCounter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto counter = new Counter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(counter); + metrics_lock_.lock(); + int_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewFloatCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto counter = new Counter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(counter); + metrics_lock_.lock(); + float_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewDoubleCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto counter = new Counter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(counter); + metrics_lock_.lock(); + double_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewShortUpDownCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto udcounter = new UpDownCounter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(udcounter); + metrics_lock_.lock(); + short_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewIntUpDownCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto udcounter = new UpDownCounter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(udcounter); + metrics_lock_.lock(); + int_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewFloatUpDownCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto udcounter = new UpDownCounter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(udcounter); + metrics_lock_.lock(); + float_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewDoubleUpDownCounter( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto udcounter = new UpDownCounter(name, description, unit, enabled); + auto ptr = std::shared_ptr>(udcounter); + metrics_lock_.lock(); + double_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewShortValueRecorder( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto recorder = new ValueRecorder(name, description, unit, enabled); + auto ptr = std::shared_ptr>(recorder); + metrics_lock_.lock(); + short_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewIntValueRecorder( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto recorder = new ValueRecorder(name, description, unit, enabled); + auto ptr = std::shared_ptr>(recorder); + metrics_lock_.lock(); + int_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewFloatValueRecorder( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto recorder = new ValueRecorder(name, description, unit, enabled); + auto ptr = std::shared_ptr>(recorder); + metrics_lock_.lock(); + float_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewDoubleValueRecorder( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto recorder = new ValueRecorder(name, description, unit, enabled); + auto ptr = std::shared_ptr>(recorder); + metrics_lock_.lock(); + double_metrics_.insert(std::make_pair(std::string(name), ptr)); + metrics_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewShortSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto sumobs = new SumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + short_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewIntSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto sumobs = new SumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + int_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewFloatSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto sumobs = new SumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + float_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewDoubleSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto sumobs = new SumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + double_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewShortUpDownSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto sumobs = new UpDownSumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + short_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewIntUpDownSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto sumobs = new UpDownSumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + int_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewFloatUpDownSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto sumobs = new UpDownSumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + float_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewDoubleUpDownSumObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto sumobs = new UpDownSumObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + double_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewShortValueObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto sumobs = new ValueObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + short_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewIntValueObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto sumobs = new ValueObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + int_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewFloatValueObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto sumobs = new ValueObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + float_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +nostd::shared_ptr> Meter::NewDoubleValueObserver( + nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled, + void (*callback)(metrics_api::ObserverResult)) +{ + if (!IsValidName(name) || NameAlreadyUsed(name)) + { +#if __EXCEPTIONS + throw std::invalid_argument("Invalid Name"); +#else + std::terminate(); +#endif + } + auto sumobs = new ValueObserver(name, description, unit, enabled, callback); + auto ptr = std::shared_ptr>(sumobs); + observers_lock_.lock(); + double_observers_.insert(std::make_pair(std::string(name), ptr)); + observers_lock_.unlock(); + return nostd::shared_ptr>(ptr); +} + +void Meter::RecordShortBatch(const trace::KeyValueIterable &labels, + nostd::span *> instruments, + nostd::span values) noexcept +{ + for (int i = 0; i < instruments.size(); ++i) + { + instruments[i]->update(values[i], labels); + } +} + +void Meter::RecordIntBatch(const trace::KeyValueIterable &labels, + nostd::span *> instruments, + nostd::span values) noexcept +{ + for (int i = 0; i < instruments.size(); ++i) + { + instruments[i]->update(values[i], labels); + } +} + +void Meter::RecordFloatBatch(const trace::KeyValueIterable &labels, + nostd::span *> instruments, + nostd::span values) noexcept +{ + for (int i = 0; i < instruments.size(); ++i) + { + instruments[i]->update(values[i], labels); + } +} + +void Meter::RecordDoubleBatch(const trace::KeyValueIterable &labels, + nostd::span *> instruments, + nostd::span values) noexcept +{ + for (int i = 0; i < instruments.size(); ++i) + { + instruments[i]->update(values[i], labels); + } +} + +std::vector Meter::Collect() noexcept +{ + std::vector records; + CollectMetrics(records); + CollectObservers(records); + return records; +} + +// Must cast to sdk::SynchronousInstrument to have access to GetRecords() function +void Meter::CollectMetrics(std::vector &records) +{ + metrics_lock_.lock(); + for (auto i = short_metrics_.begin(); i != short_metrics_.end();) + { + CollectSingleSyncInstrument(i, records); + if (i->second.use_count() == 1) // Evaluates to true if user's shared_ptr has been deleted + { + i = short_metrics_.erase(i); // Remove instrument that is no longer accessible + } + else + { + i++; + } + } + for (auto i = int_metrics_.begin(); i != int_metrics_.end();) + { + CollectSingleSyncInstrument(i, records); + if (i->second.use_count() == 1) // Evaluates to true if user's shared_ptr has been deleted + { + i = int_metrics_.erase(i); // Remove instrument that is no longer accessible + } + else + { + i++; + } + } + for (auto i = float_metrics_.begin(); i != float_metrics_.end();) + { + CollectSingleSyncInstrument(i, records); + if (i->second.use_count() == 1) // Evaluates to true if user's shared_ptr has been deleted + { + i = float_metrics_.erase(i); // Remove instrument that is no longer accessible + } + else + { + i++; + } + } + for (auto i = double_metrics_.begin(); i != double_metrics_.end();) + { + CollectSingleSyncInstrument(i, records); + if (i->second.use_count() == 1) // Evaluates to true if user's shared_ptr has been deleted + { + i = double_metrics_.erase(i); // Remove instrument that is no longer accessible + } + else + { + i++; + } + } + metrics_lock_.unlock(); +} + +template +void Meter::CollectSingleSyncInstrument( + typename std::map>>::iterator + i, + std::vector &records) +{ + if (!i->second->IsEnabled()) + { + i++; + return; + } + auto cast_ptr = std::dynamic_pointer_cast>(i->second); + std::vector new_records = cast_ptr->GetRecords(); + records.insert(records.begin(), new_records.begin(), new_records.end()); +} + +void Meter::CollectObservers(std::vector &records) +{ + observers_lock_.lock(); + for (auto i = short_observers_.begin(); i != short_observers_.end();) + { + CollectSingleAsyncInstrument(i, records); + if (i->second.use_count() == 1) + { + i = short_observers_.erase(i); + } + else + { + i++; + } + } + for (auto i = int_observers_.begin(); i != int_observers_.end();) + { + CollectSingleAsyncInstrument(i, records); + if (i->second.use_count() == 1) + { + i = int_observers_.erase(i); + } + else + { + i++; + } + } + for (auto i = float_observers_.begin(); i != float_observers_.end();) + { + CollectSingleAsyncInstrument(i, records); + if (i->second.use_count() == 1) + { + i = float_observers_.erase(i); + } + else + { + i++; + } + } + for (auto i = double_observers_.begin(); i != double_observers_.end();) + { + CollectSingleAsyncInstrument(i, records); + if (i->second.use_count() == 1) + { + i = double_observers_.erase(i); + } + else + { + i++; + } + } + observers_lock_.unlock(); +} + +template +void Meter::CollectSingleAsyncInstrument( + typename std::map>>::iterator i, + std::vector &records) +{ + if (!i->second->IsEnabled()) + { + i++; + return; + } + auto cast_ptr = std::dynamic_pointer_cast>(i->second); + std::vector new_records = cast_ptr->GetRecords(); + records.insert(records.begin(), new_records.begin(), new_records.end()); +} + +bool Meter::IsValidName(nostd::string_view name) +{ + if (name.empty() || isdigit(name[0]) || isspace(name[0]) || ispunct(name[0])) + return false; + else + { + for (int i = 0; i < name.size(); ++i) + { + if (!isalnum(name[i]) && name[i] != '_' && name[i] != '.' && name[i] != '-') + return false; + } + } + return true; +} + +bool Meter::NameAlreadyUsed(nostd::string_view name) +{ + std::lock_guard lg_metrics(metrics_lock_); + std::lock_guard lg_obsevers(observers_lock_); + if (names_.find(std::string(name)) != names_.end()) + return true; + else + { + names_.insert(std::string(name)); + return false; + } +} +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/sdk/test/metrics/BUILD b/sdk/test/metrics/BUILD index dd49336d59..ee6638a030 100644 --- a/sdk/test/metrics/BUILD +++ b/sdk/test/metrics/BUILD @@ -31,6 +31,17 @@ cc_test( ], ) +cc_test( + name = "meter_test", + srcs = [ + "meter_test.cc", + ], + deps = [ + "//sdk/src/metrics", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "counter_aggregator_test", srcs = [ diff --git a/sdk/test/metrics/CMakeLists.txt b/sdk/test/metrics/CMakeLists.txt index 0acf9af35c..5b6faf60ed 100644 --- a/sdk/test/metrics/CMakeLists.txt +++ b/sdk/test/metrics/CMakeLists.txt @@ -6,7 +6,8 @@ foreach( exact_aggregator_test counter_aggregator_test histogram_aggregator_test - ungrouped_processor_test) + ungrouped_processor_test + meter_test) add_executable(${testname} "${testname}.cc") target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} opentelemetry_metrics) diff --git a/sdk/test/metrics/meter_test.cc b/sdk/test/metrics/meter_test.cc new file mode 100644 index 0000000000..b3cdc0ce39 --- /dev/null +++ b/sdk/test/metrics/meter_test.cc @@ -0,0 +1,289 @@ +#include "opentelemetry/sdk/metrics/meter.h" +#include +#include + +using namespace opentelemetry::sdk::metrics; +namespace metrics_api = opentelemetry::metrics; + +OPENTELEMETRY_BEGIN_NAMESPACE + +TEST(Meter, CreateSyncInstruments) +{ + // Test that there are no errors creating synchronous instruments. + Meter m("Test"); + + m.NewShortCounter("Test-short-counter", "For testing", "Unitless", true); + m.NewIntCounter("Test-int-counter", "For testing", "Unitless", true); + m.NewFloatCounter("Test-float-counter", "For testing", "Unitless", true); + m.NewDoubleCounter("Test-double-counter", "For testing", "Unitless", true); + + m.NewShortUpDownCounter("Test-short-ud-counter", "For testing", "Unitless", true); + m.NewIntUpDownCounter("Test-int-ud-counter", "For testing", "Unitless", true); + m.NewFloatUpDownCounter("Test-float-ud-counter", "For testing", "Unitless", true); + m.NewDoubleUpDownCounter("Test-double-ud-counter", "For testing", "Unitless", true); + + m.NewShortValueRecorder("Test-short-recorder", "For testing", "Unitless", true); + m.NewIntValueRecorder("Test-int-recorder", "For testing", "Unitless", true); + m.NewFloatValueRecorder("Test-float-recorder", "For testing", "Unitless", true); + m.NewDoubleValueRecorder("Test-double-recorder", "For testing", "Unitless", true); +} + +// Dummy functions for asynchronous instrument constructors +void ShortCallback(metrics_api::ObserverResult) {} +void IntCallback(metrics_api::ObserverResult) {} +void FloatCallback(metrics_api::ObserverResult) {} +void DoubleCallback(metrics_api::ObserverResult) {} + +TEST(Meter, CreateAsyncInstruments) +{ + // Test that there are no errors when creating asynchronous instruments. + Meter m("Test"); + + m.NewShortSumObserver("Test-short-sum-obs", "For testing", "Unitless", true, &ShortCallback); + m.NewIntSumObserver("Test-int-sum-obs", "For testing", "Unitless", true, &IntCallback); + m.NewFloatSumObserver("Test-float-sum-obs", "For testing", "Unitless", true, &FloatCallback); + m.NewDoubleSumObserver("Test-double-sum-obs", "For testing", "Unitless", true, &DoubleCallback); + + m.NewShortUpDownSumObserver("Test-short-ud-sum-obs", "For testing", "Unitless", true, + &ShortCallback); + m.NewIntUpDownSumObserver("Test-int-ud-sum-obs", "For testing", "Unitless", true, &IntCallback); + m.NewFloatUpDownSumObserver("Test-float-ud-sum-obs", "For testing", "Unitless", true, + &FloatCallback); + m.NewDoubleUpDownSumObserver("Test-double-ud-sum-obs", "For testing", "Unitless", true, + &DoubleCallback); + + m.NewShortValueObserver("Test-short-val-obs", "For testing", "Unitless", true, &ShortCallback); + m.NewIntValueObserver("Test-int-val-obs", "For testing", "Unitless", true, &IntCallback); + m.NewFloatValueObserver("Test-float-val-obs", "For testing", "Unitless", true, &FloatCallback); + m.NewDoubleValueObserver("Test-double-val-obs", "For testing", "Unitless", true, &DoubleCallback); +} + +TEST(Meter, CollectSyncInstruments) +{ + // Verify that the records returned on a call to Collect() are correct for synchronous + // instruments. + Meter m("Test"); + + ASSERT_EQ(m.Collect().size(), 0); + + auto counter = m.NewShortCounter("Test-counter", "For testing", "Unitless", true); + + std::map labels = {{"Key", "Value"}}; + auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + + counter->add(1, labelkv); + + std::vector res = m.Collect(); + auto agg_var = res[0].GetAggregator(); + auto agg = opentelemetry::nostd::get<0>(agg_var); + + ASSERT_EQ(agg->get_checkpoint()[0], 1); + + // Now call add() and Collect() again to ensure that the value in the underlying + // aggregator was reset to the default. + + counter->add(10, labelkv); + + res = m.Collect(); + agg_var = res[0].GetAggregator(); + agg = opentelemetry::nostd::get<0>(agg_var); + + ASSERT_EQ(agg->get_checkpoint()[0], 10); +} + +TEST(Meter, CollectDeletedSync) +{ + // Verify that calling Collect() after creating a synchronous instrument and destroying + // the return pointer does not result in a segfault. + + Meter m("Test"); + + ASSERT_EQ(m.Collect().size(), 0); + + std::map labels = {{"Key", "Value"}}; + auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + { + auto counter = m.NewShortCounter("Test-counter", "For testing", "Unitless", true); + counter->add(1, labelkv); + } // counter shared_ptr deleted here + + std::vector res = m.Collect(); + auto agg_var = res[0].GetAggregator(); + auto agg = opentelemetry::nostd::get<0>(agg_var); + + ASSERT_EQ(agg->get_checkpoint()[0], 1); +} + +// Dummy function for asynchronous instrument constructors. +void Callback(opentelemetry::metrics::ObserverResult result) +{ + std::map labels = {{"key", "value"}}; + auto labelkv = trace::KeyValueIterableView{labels}; + result.observe(1, labelkv); +} + +TEST(Meter, CollectAsyncInstruments) +{ + // Verify that the records returned on a call to Collect() are correct for asynchronous + // instruments. + Meter m("Test"); + + ASSERT_EQ(m.Collect().size(), 0); + + auto sumobs = + m.NewShortSumObserver("Test-counter", "For testing", "Unitless", true, &ShortCallback); + + std::map labels = {{"Key", "Value"}}; + auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + + sumobs->observe(1, labelkv); + + std::vector res = m.Collect(); + auto agg_var = res[0].GetAggregator(); + auto agg = opentelemetry::nostd::get<0>(agg_var); + + ASSERT_EQ(agg->get_checkpoint()[0], 1); + + // Now call observe() and Collect() again to ensure that the value in the underlying + // aggregator was reset to the default. + + sumobs->observe(10, labelkv); + + res = m.Collect(); + agg_var = res[0].GetAggregator(); + agg = opentelemetry::nostd::get<0>(agg_var); + + ASSERT_EQ(agg->get_checkpoint()[0], 10); +} + +TEST(Meter, CollectDeletedAsync) +{ + // Verify that calling Collect() after creating an asynchronous instrument and destroying + // the return pointer does not result in a segfault. + + Meter m("Test"); + + ASSERT_EQ(m.Collect().size(), 0); + + std::map labels = {{"Key", "Value"}}; + auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + { + auto sumobs = m.NewShortSumObserver("Test-counter", "For testing", "Unitless", true, &Callback); + sumobs->observe(1, labelkv); + } // sumobs shared_ptr deleted here + + std::vector res = m.Collect(); + auto agg_var = res[0].GetAggregator(); + auto agg = opentelemetry::nostd::get<0>(agg_var); + + ASSERT_EQ(agg->get_checkpoint()[0], 1); +} + +TEST(Meter, RecordBatch) +{ + // This tests that RecordBatch appropriately updates the aggregators of the instruments + // passed to the function. Short, int, float, and double data types are tested. + Meter m("Test"); + + auto scounter = m.NewShortCounter("Test-scounter", "For testing", "Unitless", true); + auto icounter = m.NewIntCounter("Test-icounter", "For testing", "Unitless", true); + auto fcounter = m.NewFloatCounter("Test-fcounter", "For testing", "Unitless", true); + auto dcounter = m.NewDoubleCounter("Test-dcounter", "For testing", "Unitless", true); + + std::map labels = {{"Key", "Value"}}; + auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + + metrics_api::SynchronousInstrument *sinstr_arr[] = {scounter.get()}; + short svalues_arr[] = {1}; + + nostd::span *> sinstrs{sinstr_arr}; + nostd::span svalues{svalues_arr}; + + m.RecordShortBatch(labelkv, sinstrs, svalues); + std::vector res = m.Collect(); + auto short_agg_var = res[0].GetAggregator(); + auto short_agg = opentelemetry::nostd::get<0>(short_agg_var); + ASSERT_EQ(short_agg->get_checkpoint()[0], 1); + + metrics_api::SynchronousInstrument *iinstr_arr[] = {icounter.get()}; + int ivalues_arr[] = {1}; + + nostd::span *> iinstrs{iinstr_arr}; + nostd::span ivalues{ivalues_arr}; + + m.RecordIntBatch(labelkv, iinstrs, ivalues); + res = m.Collect(); + auto int_agg_var = res[0].GetAggregator(); + auto int_agg = opentelemetry::nostd::get<1>(int_agg_var); + ASSERT_EQ(int_agg->get_checkpoint()[0], 1); + + metrics_api::SynchronousInstrument *finstr_arr[] = {fcounter.get()}; + float fvalues_arr[] = {1.0}; + + nostd::span *> finstrs{finstr_arr}; + nostd::span fvalues{fvalues_arr}; + + m.RecordFloatBatch(labelkv, finstrs, fvalues); + res = m.Collect(); + auto float_agg_var = res[0].GetAggregator(); + auto float_agg = opentelemetry::nostd::get<2>(float_agg_var); + ASSERT_EQ(float_agg->get_checkpoint()[0], 1.0); + + metrics_api::SynchronousInstrument *dinstr_arr[] = {dcounter.get()}; + double dvalues_arr[] = {1.0}; + + nostd::span *> dinstrs{dinstr_arr}; + nostd::span dvalues{dvalues_arr}; + + m.RecordDoubleBatch(labelkv, dinstrs, dvalues); + res = m.Collect(); + auto double_agg_var = res[0].GetAggregator(); + auto double_agg = opentelemetry::nostd::get<3>(double_agg_var); + ASSERT_EQ(double_agg->get_checkpoint()[0], 1.0); +} + +TEST(Meter, DisableCollectSync) +{ + Meter m("Test"); + std::map labels = {{"Key", "Value"}}; + auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + auto c = m.NewShortCounter("c", "", "", false); + c->add(1, labelkv); + ASSERT_EQ(m.Collect().size(), 0); +} + +TEST(Meter, DisableCollectAsync) +{ + Meter m("Test"); + std::map labels = {{"Key", "Value"}}; + auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + auto c = m.NewShortValueObserver("c", "", "", false, &ShortCallback); + c->observe(1, labelkv); + ASSERT_EQ(m.Collect().size(), 0); +} + +TEST(MeterStringUtil, IsValid) +{ +#if __EXCEPTIONS + Meter m("Test"); + ASSERT_ANY_THROW(m.NewShortCounter("", "Empty name is invalid", " ", true)); + ASSERT_ANY_THROW(m.NewShortCounter("1a", "Can't begin with a number", " ", true)); + ASSERT_ANY_THROW(m.NewShortCounter(".a", "Can't begin with punctuation", " ", true)); + ASSERT_ANY_THROW(m.NewShortCounter(" a", "Can't begin with space", " ", true)); + ASSERT_ANY_THROW(m.NewShortCounter( + "te^ s=%t", "Only alphanumeric ., -, and _ characters are allowed", " ", true)); +#endif +} + +TEST(MeterStringUtil, AlreadyExists) +{ +#if __EXCEPTIONS + Meter m("Test"); + + m.NewShortCounter("a", "First instance of instrument named 'a'", "", true); + ASSERT_ANY_THROW(m.NewShortCounter("a", "Second (illegal) instrument named 'a'", "", true)); + ASSERT_ANY_THROW(m.NewShortSumObserver("a", "Still illegal even though it is not a short counter", + "", true, &ShortCallback)); +#endif +} +OPENTELEMETRY_END_NAMESPACE