// balm_metricregistry.h -*-C++-*- // ---------------------------------------------------------------------------- // NOTICE // // This component is not up to date with current BDE coding standards, and // should not be used as an example for new development. // ---------------------------------------------------------------------------- #ifndef INCLUDED_BALM_METRICREGISTRY #define INCLUDED_BALM_METRICREGISTRY #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a registry for metrics. // //@CLASSES: // balm::MetricRegistry: a registry for metrics // //@SEE_ALSO: balm_metricsmanager, balm_metricrecord // //@DESCRIPTION: This component defines a class, 'balm::MetricRegistry', that // provides operations to register both metric categories and individual // metrics. A metric is uniquely identified by its name and category, and the // metric registry provides a mapping from those identifying properties to a // 'balm::MetricId'. A 'balm::MetricRegistry' object also provides a mapping // from a category name to the address of a non-modifiable 'balm::Category' // object. // ///Alternative Systems for Telemetry ///--------------------------------- // Bloomberg software may alternatively use the GUTS telemetry API, which is // integrated into Bloomberg infrastructure. // ///Thread Safety ///------------- // 'balm::MetricRegistry' is fully *thread-safe*, meaning that all non-creator // operations on a given object can be safely invoked simultaneously from // multiple threads. // ///Usage ///----- // The following example illustrates how to create and use a // 'balm::MetricRegistry'. We start by creating a 'balm::MetricRegistry' // object, 'registry', and then using this registry to create a // 'balm::MetricId' for a metric named "MetricA" belonging to the category // "MyCategory" (i.e., "MyCategory.MetricA"). //.. // bslma::Allocator *allocator = bslma::Default::allocator(0); // balm::MetricRegistry registry(allocator); // // balm::MetricId idA = registry.addId("MyCategory", "MetricA"); //.. // Now that we have added a metric id, "MyCategory.MetricA", attempting to add // the metric id again will return an invalid id. We retrieve the same // identifier we have created using either 'getId' or 'findId': //.. // balm::MetricId invalidId = registry.addId("MyCategory", "MetricA"); // assert(!invalidId.isValid()); // // balm::MetricId idA_copy1 = registry.getId("MyCategory", "MetricA"); // assert(idA_copy1.isValid()); // assert(idA_copy1 == idA); // // balm::MetricId idA_copy2 = registry.findId("MyCategory", "MetricA"); // assert(idA_copy2.isValid()); // assert(idA_copy2 == idA); //.. // We use the 'getId' method to add a new metric to the registry, then verify // we can lookup the metric: //.. // balm::MetricId idB = registry.getId("MyCategory", "MetricB"); // assert(idB.isValid()); // assert(idB == registry.getId("MyCategory", "MetricB")); // assert(idB == registry.findId("MyCategory", "MetricB")); // assert(!registry.addId("MyCategory", "MetricB").isValid()); //.. // Next we use 'getCategory' to find the address of the 'balm::Category' object // corresponding to "MyCategory": //.. // const balm::Category *myCategory = registry.getCategory("MyCategory"); // assert(myCategory == idA.category()); // assert(myCategory == idB.category()); // assert(myCategory->isEnabled()); //.. // Finally we use the 'setCategoryEnabled' method to disable the category // "MyCategory": //.. // registry.setCategoryEnabled(myCategory, false); // assert(!myCategory->isEnabled()); //.. #include <balscm_version.h> #include <balm_category.h> #include <balm_metricdescription.h> #include <balm_metricid.h> #include <balm_publicationtype.h> #include <bslmt_rwmutex.h> #include <bdlb_cstringless.h> #include <bslma_allocator.h> #include <bslmf_nestedtraitdeclaration.h> #include <bsl_iosfwd.h> #include <bsl_map.h> #include <bsl_memory.h> #include <bsl_set.h> #include <bsl_string.h> #include <bsl_utility.h> #include <bsl_vector.h> #include <bsl_cstddef.h> #include <bsl_cstring.h> #include <bslma_allocator.h> namespace BloombergLP { namespace balm { class MetricFormat; // ==================== // class MetricRegistry // ==================== class MetricRegistry { // The class defines a thread-aware mechanism for registering metrics and // metric categories. A metric is uniquely identified by its name and // category, and the metric registry provides a mapping from those // identifying properties to a 'balm::MetricId'. A 'balm::MetricRegistry' // object also provides a mapping from a category name to the address of a // non-modifiable 'balm::Category' object. // PRIVATE TYPES typedef bsl::pair<const char *, const char *> CategoryAndName; // 'CategoryAndName' is an alias for a pair of null-terminated // constant strings that represent the category and name of a metric. // The first element is the category and the second is the name. struct CategoryAndNameLess { // This 'struct' defines an ordering on 'CategoryAndName' values // allowing them to be included in sorted containers such as // 'bsl::map'. Note that the category and name strings are compared // by value. typedef bsl::pair<const char *, const char *> CategoryAndName; bool operator()(const CategoryAndName& lhs, const CategoryAndName& rhs) const // Return 'true' if the value of the specified 'lhs' is less than // (ordered before) the value of the specified 'rhs', and 'false' // otherwise. The 'lhs' value is considered less than the 'rhs' // value if the first value in the 'lhs' pair (the category) is // less than the first value in the 'rhs' pair or, if the first // values are equal, if the second value in the 'lhs' pair (the // name) is less than the second value in the 'rhs' pair. { int cmp = bsl::strcmp(lhs.first, rhs.first); if (0 == cmp) { cmp = bsl::strcmp(lhs.second, rhs.second); } return cmp < 0; } }; typedef bsl::map<CategoryAndName, bsl::shared_ptr<MetricDescription>, CategoryAndNameLess> MetricMap; // A 'MetricMap' is a type that maps a category and name to a // 'balm::MetricDescription' object address. typedef bsl::map<const char *, bsl::shared_ptr<Category>, bdlb::CStringLess> CategoryRegistry; // A 'CategoryRegistry' is a type that maps a name to a // 'balm::Category' object address. typedef bsl::map<const char *, bsl::vector<const void *>, bdlb::CStringLess> UserDataRegistry; // 'UserDataRegistry' is an alias for a type that maps a category (or // category prefix) to the user data set for that category (or group of // categories). // DATA bsl::set<bsl::string> d_uniqueStrings; // unique string memory CategoryRegistry d_categories; // category -> 'balm::Category' MetricMap d_metrics; // map (category,name) -> MetricId bool d_defaultEnabled; // default enabled status UserDataRegistry d_categoryUserData; // map category -> user data UserDataRegistry d_categoryPrefixUserData; // map category-prefix -> user // data int d_nextKey; // next valid user data key mutable bslmt::RWMutex d_lock; // read-write property lock bslma::Allocator *d_allocator_p; // allocator (held, not owned) // NOT IMPLEMENTED MetricRegistry(const MetricRegistry&); MetricRegistry& operator=(const MetricRegistry&); private: // PRIVATE MANIPULATORS bsl::pair<MetricId, bool> insertId(const char *category, const char *name); // Insert a metric id having the specified 'category' and 'name' into // this metric registry. Return a pair whose first member is the id // of the metric, and whose second member is 'true' if the returned // metric id is newly-created and 'false' otherwise. The behavior is // undefined unless the calling thread has a *write* lock on 'd_lock'. void setCurrentUserData(const char *category, MetricDescription::UserDataKey key, const void *value); // Associate the specified 'value' with the specified 'key' for every // metric belonging to the specified 'category'. Note that this // operation modifies existing metrics, but does not affect metrics // created after this method is called. The behavior is undefined // unless the calling thread has a *write* lock on 'd_lock'. // PRIVATE ACCESSORS void defaultUserData(bsl::vector<const void *> *result, const char *categoryName) const; // Load into the specified 'result' the user data associated (via // 'setUserData') with a category having the specified 'categoryName'. // Each index position in 'result' will contain 0, or an (opaque) // application-specific data value provided by the client, either for // 'categoryName' or a prefix of 'categoryName'. If there is more // than one non-null user-supplied data value applicable to an index // position in 'result', it is unspecified which value will be // returned. The behavior is undefined unless the calling thread has a // lock on 'd_lock'. public: // PUBLIC TRAITS BSLMF_NESTED_TRAIT_DECLARATION(MetricRegistry, bslma::UsesBslmaAllocator); // CREATORS MetricRegistry(bslma::Allocator *basicAllocator = 0); // Create an empty metric registry. Optionally specify a // 'basicAllocator' used to supply memory. If 'basicAllocator' is 0, // the currently installed default allocator is used. ~MetricRegistry(); // Destroy this metric registry. // MANIPULATORS MetricId addId(const char *category, const char *name); // Add the specified 'category' and 'name' to this registry, unless it // has already been registered, and return a 'balm::MetricId' object // identifying the newly-registered metric. If the indicated metric // has already been registered, the returned 'balm::MetricId' object // will *not* be valid (i.e., 'isValid' will return 'false'). The // behavior is undefined unless 'category' and 'name' are // null-terminated. MetricId getId(const char *category, const char *name); // Return a 'balm::MetricId' object for the metric identified by the // specified 'category' and 'name'. If no corresponding metric has // already been registered, register a new metric and return a // 'balm::MetricId' object identifying that newly-registered metric. // The behavior is undefined unless 'category' and 'name' are // null-terminated. Note that this operation is guaranteed to return // a valid 'balm::MetricId' object. const Category *addCategory(const char *category); // Add the specified 'category' to this registry, unless it has already // been registered. Return the address of the newly-created // non-modifiable 'balm::Category' object on success, and 0 otherwise. // The behavior is undefined unless 'category' is null-terminated. const Category *getCategory(const char *category); // Return the address of the non-modifiable 'balm::Category' object for // the specified 'category'. If no corresponding category exists, // register a new category and return the address of the newly-created // 'balm::Category' object. The behavior is undefined unless // 'category' is null-terminated. Note that this operation is // guaranteed to return a valid address. void setCategoryEnabled(const Category* category, bool value); // Set whether the specified 'category' is enabled to the specified // 'value'. The behavior is undefined unless 'category' is a valid // address of a category previously returned by this metric registry. // Note that this operation is thread-safe, but *not* atomic: Other // threads may simultaneously access the current enabled value for // 'category' while this operation completes. Also note that this // operation has *linear* runtime performance with respect to the // number of registered category holders for 'category'. void setAllCategoriesEnabled(bool value); // Set whether each currently registered category is enabled to the // specified 'value', and ensure that categories registered after this // call are initialized as either enabled or disabled, accordingly. // This operation is logically equivalent to iterating over the list // of currently registered categories and calling 'setCategoryEnabled' // on each category individually, and also setting a default 'enabled' // value (for newly-created categories). Hence, subsequent calls // 'setCategoryEnabled' will override this value for a particular // category. Note that this operation is thread-safe, but *not* // atomic: Other threads may simultaneously access the current enabled // status for registered categories while this operation completes. // Also note that this operation has *linear* runtime performance with // respect to the total number of category holders registered with this // repository. void registerCategoryHolder(const Category *category, CategoryHolder *holder); // Load into the specified 'holder' the address of the specified // 'category', its 'enabled' status, and the address of the next holder // in the linked list of category holders maintained by 'category' // (prepending 'holder' to the linked list of category holders for // 'category'). The supplied 'category' will update the value returned // by 'holder->enabled()' when its enabled state changes, and will // reset 'holder' (i.e., 'holder->reset()') when 'category' is // destroyed. The behavior is undefined unless 'holder' remains valid // and *unmodified* (by the client) for the lifetime of this object. // // This method should *not* be used directly by client code. It is an // implementation detail of the 'balm' metric collection system. void setPreferredPublicationType(const MetricId& metric, PublicationType::Value type); // Set the preferred publication type of the specified 'metric' to the // specified 'type'. The preferred publication type of a metric // indicates the preferred aggregate to publish for that metric, or // 'balm::PublicationType::UNSPECIFIED' if there is no preference. The // behavior is undefined unless 'metric' was previously returned by // this metric registry. Note that there is no uniform definition for // how publishers will interpret this value; an 'UNSPECIFIED' value // generally indicates that the all the collected aggregates (total, // count, minimum, and maximum value) should be published. Also note // that the preferred publication type is accessed through the // 'balm::MetricDescription' (i.e., // 'metric.description()->preferredPublicationType()'). void setFormat(const MetricId& metricId, const MetricFormat& format); // Set the format for the specified 'metricId' to the specified // 'format'. Note that there is no uniform specification for how // publisher implementations will interpret the supplied 'format'. // Also note that the format for a metric is accessed through the // 'balm::MetricDescription'. For example: //.. // metric.description()->format(); //.. MetricDescription::UserDataKey createUserDataKey(); // Return a new unique key that can be used to associate (via // 'setUserData') and retrieve (via 'userData') a value with a metric // (or group of metrics). Note that the returned key can be used by // clients of 'balm' to associate additional information with a metric. void setUserData(const MetricId& metricId, MetricDescription::UserDataKey key, const void *value); // Associate the specified 'value' with the specified 'key' in the // description of the specified 'metricId'. The behavior is undefined // unless 'key' was previously returned from 'createUserDataKey'. Note // that this method allows clients of 'balm' to associate (opaque) // application-specific information with a metric. void setUserData(const char *categoryName, MetricDescription::UserDataKey key, const void *value, bool prefixFlag = false); // Associate the specified 'value' with the specified 'key' in any // metric belonging to a category having the specified 'categoryName', // or a category whose name begins with 'categoryName', as determined // by the optionally specified 'prefixFlag'. If 'prefixFlag' is // 'false' or is not specified, only those metrics belonging to a // category having 'categoryName' will be mapped; otherwise, 'value' // will be associated with 'key' for all metrics belonging to any // category whose name begins with 'categoryName'. This association // applies to existing metrics as well as any subsequently created // ones. When a metric is created that matches more than one // registered category prefix, it is not specified which supplied value // will be associated with 'key', unless only one of those values is // non-null, in which case the unique non-null value is used. The // behavior is undefined unless 'key' was previously returned from // 'createUserDataKey'. // ACCESSORS bsl::size_t numMetrics() const; // Return the number of metrics in this registry. bsl::size_t numCategories() const; // Return the number of categories in this registry. const Category *findCategory(const char *category) const; // Find the specified 'category', a null-terminated string, in this // registry. Return the address of the non-modifiable 'balm::Category' // object corresponding to the 'category', or 0 if no such category has // been registered. MetricId findId(const char *category, const char *name) const; // Find the specified null-terminated strings 'category' and 'name' in // this registry. Return the 'balm::MetricId' object corresponding to // the metric having the 'category' and 'name', if found, or an invalid // metric id if no such metric has been registered (i.e., 'isValid' // will return 'false'). void getAllCategories(bsl::vector<const Category *> *categories) const; // Append to the specified 'categories' the addresses of all the // categories registered by this 'balm::MetricRegistry' object. bsl::ostream& print(bsl::ostream& stream, int level = 0, int spacesPerLevel = 4) const; // Format this object to the specified output 'stream' at the (absolute // value of) the optionally specified indentation 'level' and return a // reference to 'stream'. If 'level' is specified, optionally specify // 'spacesPerLevel', the number of spaces per indentation level for // this and all of its nested objects. If 'level' is negative, // suppress indentation of the first line. If 'spacesPerLevel' is // negative, format the entire output on one line, suppressing all but // the initial indentation (as governed by 'level'). If 'stream' is // not valid on entry, this operation has no effect. }; } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2015 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------