// ball_categorymanager.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_BALL_CATEGORYMANAGER #define INCLUDED_BALL_CATEGORYMANAGER #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a manager of named categories each having "thresholds". // //@CLASSES: // ball::CategoryManager: manager of category registry // //@SEE_ALSO: ball_category, ball_loggermanager, ball_loggercategoryutil // //@DESCRIPTION: This component provides a registry for category information and // functions to manage the registry and its members. By "category" we mean a // named entity that identifies a region or functional area of a program. A // category name can be an arbitrary string, including the empty string. // Note that category names are case-sensitive. // // Associated with each category, besides its name, are four threshold levels // known as "record", "pass", "trigger", and "trigger-all". Threshold // levels are values in the range '[0 .. 255]'. (See the 'ball_loggermanager' // component-level documentation for a typical interpretation of these four // thresholds.) // // A category is represented by a 'ball::Category' object. Although instances // of 'ball::Category' can be created directly, within the BALL logging // framework they are generally created by the 'ball::CategoryManager' class. // 'ball::CategoryManager' manages a registry of categories and exposes methods // to add new categories to the registry ('addCategory') and modify the // threshold levels of existing categories ('setThresholdLevels'). // 'ball::Category' provides accessors for direct access to the name and // threshold levels of a given category, and a single manipulator to set the // four threshold levels levels (see 'ball_category'). // ///Thread Safety ///------------- // 'ball::CategoryManager' is *thread-safe*, meaning that any operation on the // same instance can be safely invoked from any thread concurrently with any // other operation. // ///Usage ///----- // The code fragments in the following example illustrate some basic operations // of category management including (1) adding categories to the registry, // (2) accessing and modifying the threshold levels of existing categories, // and (3) iterating over the categories in the registry. // // First we define some hypothetical category names: //.. // const char *myCategories[] = { // "EQUITY.MARKET.NYSE", // "EQUITY.MARKET.NASDAQ", // "EQUITY.GRAPHICS.MATH.FACTORIAL", // "EQUITY.GRAPHICS.MATH.ACKERMANN" // }; //.. // Next we create a 'ball::CategoryManager' named 'manager' and use the // 'addCategory' method to define a category for each of the names in // 'myCategories'. The threshold levels of each of the categories are set to // slightly different values to help distinguish them when they are displayed // later: //.. // ball::CategoryManager manager; // // const int NUM_CATEGORIES = sizeof myCategories / sizeof *myCategories; // for (int i = 0; i < NUM_CATEGORIES; ++i) { // manager.addCategory(myCategories[i], // 192 + i, 96 + i, 64 + i, 32 + i); // } //.. // In the following, each of the new categories is accessed from the registry // and their names and threshold levels printed: //.. // for (int i = 0; i < NUM_CATEGORIES; ++i) { // const ball::Category *category = // manager.lookupCategory(myCategories[i]); // bsl::cout << "[ " << myCategories[i] // << ", " << category->recordLevel() // << ", " << category->passLevel() // << ", " << category->triggerLevel() // << ", " << category->triggerAllLevel() // << " ]" << bsl::endl; // } //.. // The following is printed to 'stdout': //.. // [ EQUITY.MARKET.NYSE, 192, 96, 64, 32 ] // [ EQUITY.MARKET.NASDAQ, 193, 97, 65, 33 ] // [ EQUITY.GRAPHICS.MATH.FACTORIAL, 194, 98, 66, 34 ] // [ EQUITY.GRAPHICS.MATH.ACKERMANN, 195, 99, 67, 35 ] //.. // We next use the 'setLevels' method of 'ball::Category' to adjust the // threshold levels of our categories. The following also demonstrates use // of the 'recordLevel', etc., accessors of 'ball::Category': //.. // for (int i = 0; i < NUM_CATEGORIES; ++i) { // ball::Category *category = manager.lookupCategory(myCategories[i]); // category->setLevels(category->recordLevel() + 1, // category->passLevel() + 1, // category->triggerLevel() + 1, // category->triggerAllLevel() + 1); // } //.. // Repeating the second 'for' loop from above generates the following output // on 'stdout': //.. // [ EQUITY.MARKET.NYSE, 193, 97, 65, 33 ] // [ EQUITY.MARKET.NASDAQ, 194, 98, 66, 34 ] // [ EQUITY.GRAPHICS.MATH.FACTORIAL, 195, 99, 67, 35 ] // [ EQUITY.GRAPHICS.MATH.ACKERMANN, 196, 100, 68, 36 ] //.. // Next we illustrate use of the index operator as a means of iterating over // the registry of categories. In particular, we illustrate an alternate // approach to modifying the threshold levels of our categories by iterating // over the categories in the registry of 'manager' to increment their // threshold levels a second time: //.. // for (int i = 0; i < manager.length(); ++i) { // ball::Category& category = manager[i]; // category.setLevels(category.recordLevel() + 1, // category.passLevel() + 1, // category.triggerLevel() + 1, // category.triggerAllLevel() + 1); // } //.. // Finally, we iterate over the categories in the registry to print them out // one last time: //.. // for (int i = 0; i < manager.length(); ++i) { // const ball::Category& category = manager[i]; // bsl::cout << "[ " << category.categoryName() // << ", " << category.recordLevel() // << ", " << category.passLevel() // << ", " << category.triggerLevel() // << ", " << category.triggerAllLevel() // << " ]" << bsl::endl; // } //.. // This iteration produces the following output on 'stdout': //.. // [ EQUITY.MARKET.NYSE, 194, 98, 66, 34 ] // [ EQUITY.MARKET.NASDAQ, 195, 99, 67, 35 ] // [ EQUITY.GRAPHICS.MATH.FACTORIAL, 196, 100, 68, 36 ] // [ EQUITY.GRAPHICS.MATH.ACKERMANN, 197, 101, 69, 37 ] //.. #include <balscm_version.h> #include <ball_category.h> #include <ball_ruleset.h> #include <ball_thresholdaggregate.h> #include <bdlb_cstringequalto.h> #include <bdlb_cstringhash.h> #include <bslma_allocator.h> #include <bslma_default.h> #include <bslmt_mutex.h> #include <bslmt_readlockguard.h> #include <bslmt_readerwriterlock.h> #include <bsls_atomic.h> #include <bsls_types.h> #include <bsl_new.h> #include <bsl_string.h> #include <bsl_unordered_map.h> #include <bsl_vector.h> namespace BloombergLP { namespace ball { // ===================== // class CategoryManager // ===================== class CategoryManager { // This class manages a set (or "registry") of categories. Categories may // be added to the registry, but they cannot be removed. However, the // threshold levels of existing categories may be accessed and modified // directly. // TYPES typedef bsl::unordered_map<const char *, int, bdlb::CStringHash, bdlb::CStringEqualTo> CategoryMap; // DATA CategoryMap d_registry; // mapping names to // indices in // 'd_categories' bsls::AtomicInt64 d_ruleSetSequenceNumber; // sequence number that // is incremented each // time the rule set is // changed RuleSet d_ruleSet; // rule set that contains // all registered rules bslmt::Mutex d_ruleSetMutex; // serialize access to // 'd_ruleset' bsl::vector<Category *> d_categories; // providing random // access to categories mutable bslmt::ReaderWriterLock d_registryLock; // ensuring MT-safety of // category map bslma::Allocator *d_allocator_p; // memory allocator // (held, not owned) private: // NOT IMPLEMENTED CategoryManager(const CategoryManager&); CategoryManager& operator=(const CategoryManager&); // PRIVATE MANIPULATORS Category *addNewCategory(const char *categoryName, int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel); // Add to the registry of this category manager a category having the // specified 'categoryName' and the specified 'recordLevel', // 'passLevel', 'triggerLevel', and 'triggerAllLevel' threshold values, // respectively. Return the address of the newly-created, modifiable // category. The behavior is undefined unless a category having // 'categoryName' does not already exist in the registry and each of // the specified threshold values is in the range '[0 .. 255]'. Note // that the category registry should be properly synchronized before // calling this method. public: // CREATORS explicit CategoryManager(bslma::Allocator *basicAllocator = 0); // Create a category manager. Optionally specify a 'basicAllocator' // used to supply memory. If 'basicAllocator' is 0, the currently // installed default allocator is used. ~CategoryManager(); // Destroy this category manager. // MANIPULATORS Category& operator[](int index); // Return a non-'const' reference to the category at the specified // 'index' in the registry of this category manager. The behavior is // undefined unless '0 <= index < length()'. Category *addCategory(const char *categoryName, int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel); // Add to the registry of this category manager a category having the // specified 'categoryName' and the specified 'recordLevel', // 'passLevel', 'triggerLevel', and 'triggerAllLevel' threshold values, // respectively, if there is no category having 'categoryName' and each // of the specified threshold values is in the range '[0 .. 255]'. // Return the address of the newly-created, modifiable category on // success, and 0 otherwise. The behavior is undefined unless a lock // is not held by this thread on the mutex returned by 'rulesetMutex'. // Note that if a category having 'categoryName' already exists in the // registry, 0 is returned. Category *addCategory(CategoryHolder *categoryHolder, const char *categoryName, int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel); // Add to the registry of this category manager a category having the // specified 'categoryName' and the specified 'recordLevel', // 'passLevel', 'triggerLevel', and 'triggerAllLevel' threshold values, // respectively, if there is no category having 'categoryName' and each // of the specified threshold values is in the range '[0 .. 255]'. // Return the address of the newly-created, modifiable category on // success, and 0 otherwise. If a newly-created category is returned // and the specified 'categoryHolder' is non-null, then also load into // 'categoryHolder' the returned category and its maximum level and // link 'categoryHolder' to the category. The behavior is undefined // unless a lock is not held by this thread on the mutex returned by // 'rulesetMutex'. Note that if a category having 'categoryName' // already exists in the registry, 0 is returned. Category *lookupCategory(const char *categoryName); // Return the address of the modifiable category having the specified // 'categoryName' in the registry of this category manager, or 0 if no // such category exists. Category *lookupCategory(CategoryHolder *categoryHolder, const char *categoryName); // Return the address of the modifiable category having the specified // 'categoryName' in the registry of this category manager, or 0 if no // such category exists. If a category is returned and the specified // 'categoryHolder' is non-null, then also load into 'categoryHolder' // the returned category and its maximum level and link // 'categoryHolder' to the category if it has not yet been linked. void resetCategoryHolders(); // Reset the category holders to which all categories in the registry // of this category manager are linked to their default value. See the // function-level documentation of 'CategoryHolder::reset()' for // further information on the default value of category holders. Category *setThresholdLevels(const char *categoryName, int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel); // Set the threshold levels of the category having the specified // 'categoryName' in the registry of this category manager to the // specified 'recordLevel', 'passLevel', 'triggerLevel', and // 'triggerAllLevel' values, respectively, if a category having // 'categoryName' exists and each of the specified threshold values is // in the range '[0 .. 255]'. Otherwise, add to the registry a // category having the specified 'categoryName' and the specified // 'recordLevel', 'passLevel', 'triggerLevel', and 'triggerAllLevel' // threshold values, respectively, if there is no category having // 'categoryName' and each of the specified threshold values is in the // range '[0 .. 255]'. Return the address of the (possibly // newly-created) modifiable category on success, and 0 otherwise (with // no effect on any category). The behavior is undefined unless a lock // is not held by this thread on the mutex returned by 'rulesetMutex'. int addRule(const Rule& rule); // Add the specified 'rule' to the set of (unique) rules maintained by // this object. Return the number of rules added (i.e., 1 on success // and 0 if a rule with the same value is already present). The // behavior is undefined unless a lock is not held by this thread on // the mutex returned by 'rulesetMutex'. int addRules(const RuleSet& ruleSet); // Add each rule in the specified 'ruleSet' to the set of (unique) // rules maintained by this object. Return the number of rules added. // The behavior is undefined unless a lock is not held by this thread // on the mutex returned by 'rulesetMutex'. Note that each rule having // the same value as an existing rule will be ignored. int removeRule(const Rule& rule); // Remove the specified 'rule' from the set of (unique) rules // maintained by this object. Return the number of rules removed // (i.e., 1 on success and 0 if no rule having the same value is // found). The behavior is undefined unless a lock is not held by this // thread on the mutex returned by 'rulesetMutex'. int removeRules(const RuleSet& ruleSet); // Remove each rule in the specified 'ruleSet' from the set of rules // maintained by this object. Return the number of rules removed. The // behavior is undefined unless a lock is not held by this thread on // the mutex returned by 'rulesetMutex'. void removeAllRules(); // Remove every rule from the set of rules maintained by this object. // The behavior is undefined unless a lock is not held by this thread // on the mutex returned by 'rulesetMutex'. bslmt::Mutex& rulesetMutex(); // Return a non-'const' reference to the mutex that is used to guard // against concurrent access to the rule set. A lock on the returned // mutex should be acquired before accessing the properties of the rule // set returned by 'ruleSet'. The behavior is undefined unless a lock // is acquired solely for the purpose of calling 'ruleSet'. template <class t_CATEGORY_VISITOR> void visitCategories(const t_CATEGORY_VISITOR& visitor); // Invoke the specified 'visitor' functor on each category managed by // this object, supplying that functor modifiable access to each // category. 'visitor' must be a functor that can be called as if it // had the following signature: //.. // void operator()(Category *); //.. // ACCESSORS const Category& operator[](int index) const; // Return a 'const' reference to the category at the specified 'index' // in the registry of this category manager. The behavior is undefined // unless '0 <= index < length()'. int length() const; // Return the number of categories in the registry of this category // manager. const Category *lookupCategory(const char *categoryName) const; // Return the address of the non-modifiable category having the // specified 'categoryName' in the registry of this category manager, // or 0 if no such category exists. const RuleSet& ruleSet() const; // Return a 'const' reference to the rule set maintained by this // category manager. The mutex returned by 'rulesetMutex' should be // locked prior to accessing the rule set. bsls::Types::Int64 ruleSetSequenceNumber() const; // Return the sequence number that tracks changes to the rule set // maintained by this category manager. The value returned by this // method is guaranteed to monotonically increase between calls before // and after the rule set is changed, and is otherwise implementation // defined. template <class t_CATEGORY_VISITOR> void visitCategories(const t_CATEGORY_VISITOR& visitor) const; // Invoke the specified 'visitor' functor on each category managed by // this object, supplying that functor non-modifiable access to each // category. 'visitor' must be a functor that can be called as if it // had the following signature: //.. // void operator()(const Category *); //.. }; #ifndef BDE_OMIT_INTERNAL_DEPRECATED // ========================= // class CategoryManagerIter // ========================= class CategoryManagerIter { // This class defines an iterator providing sequential, read-only access to // the categories in the registry of a category manager. The order of the // iteration is undefined. // // !DEPRECATED!: Use the 'CategoryManager::visitCategories' accessor // instead. // DATA const CategoryManager *d_cm_p; // associated category manager (held) int d_index; // index into category manager private: // NOT IMPLEMENTED CategoryManagerIter(const CategoryManagerIter&); CategoryManagerIter& operator=(const CategoryManagerIter&); public: // CREATORS explicit CategoryManagerIter(const CategoryManager& categoryManager); // Create an iterator providing non-modifiable access to the categories // in the specified 'categoryManager' that is initialized to refer to // the first category in the sequence of categories in the registry of // 'categoryManager', if one exists, and is initialized to be invalid // otherwise. The order of iteration is undefined. The behavior is // undefined unless the lifetime of 'categoryManager' is at least as // long as the lifetime of this iterator. //! ~CategoryManagerIter() = default; // Destroy this iterator. // MANIPULATORS void operator++(); // Advance this iterator to refer to the next unvisited category. If // no such category exists, this iterator becomes invalid. The // behavior is undefined unless this iterator is initially valid. Note // that the order of iteration is undefined. // ACCESSORS operator const void *() const; // Return a non-zero value if this iterator is valid, and 0 otherwise. const Category& operator()() const; // Return a 'const' reference to the category currently referred to by // this iterator. The behavior is undefined unless this iterator is // valid. }; // ========================== // class CategoryManagerManip // ========================== class CategoryManagerManip { // This class defines an iterator providing sequential, modifiable access // to the categories in the registry of a category manager. The order of // the iteration is undefined. // // !DEPRECATED!: Use the 'CategoryManager::visitCategories' manipulator // instead. // DATA CategoryManager *d_cm_p; // associated category manager (held) int d_index; // index into category manager private: // NOT IMPLEMENTED CategoryManagerManip(const CategoryManagerManip&); CategoryManagerManip& operator=(const CategoryManagerManip&); public: // CREATORS explicit CategoryManagerManip(CategoryManager *categoryManager); // Create an iterator providing modifiable access to the categories in // the specified 'categoryManager' that is initialized to refer to the // first category in the sequence of categories in the registry of // 'categoryManager', if one exists, and is initialized to be invalid // otherwise. The order of iteration is undefined. The behavior is // undefined unless the lifetime of 'categoryManager' is at least as // long as the lifetime of this iterator. //! ~CategoryManagerManip() = default; // Destroy this iterator. // MANIPULATORS void advance(); // Advance this iterator to refer to the next unvisited category. If // no such category exists, this iterator becomes invalid. The // behavior is undefined unless this iterator is initially valid. Note // that the order of iteration is undefined. Category& operator()(); // Return a non-'const' reference to the category currently referred to // by this iterator. The behavior is undefined unless this iterator is // valid. // ACCESSORS operator const void *() const; // Return a non-zero value if this iterator is valid, and 0 otherwise. }; #endif // BDE_OMIT_INTERNAL_DEPRECATED // ============================================================================ // INLINE FUNCTION DEFINITIONS // ============================================================================ // --------------------- // class CategoryManager // --------------------- // MANIPULATORS inline Category& CategoryManager::operator[](int index) { bslmt::ReadLockGuard<bslmt::ReaderWriterLock> guard(&d_registryLock); return *d_categories[index]; } inline bslmt::Mutex& CategoryManager::rulesetMutex() { return d_ruleSetMutex; } template <class t_CATEGORY_VISITOR> void CategoryManager::visitCategories(const t_CATEGORY_VISITOR& visitor) { bslmt::ReadLockGuard<bslmt::ReaderWriterLock> guard(&d_registryLock); for (bsl::vector<Category *>::iterator it = d_categories.begin(); it != d_categories.end(); ++it) { visitor(*it); } } // ACCESSORS inline int CategoryManager::length() const { bslmt::ReadLockGuard<bslmt::ReaderWriterLock> guard(&d_registryLock); return static_cast<int>(d_categories.size()); } inline const Category& CategoryManager::operator[](int index) const { bslmt::ReadLockGuard<bslmt::ReaderWriterLock> guard(&d_registryLock); return *d_categories[index]; } inline const RuleSet& CategoryManager::ruleSet() const { return d_ruleSet; } inline bsls::Types::Int64 CategoryManager::ruleSetSequenceNumber() const { return d_ruleSetSequenceNumber; } template <class t_CATEGORY_VISITOR> void CategoryManager::visitCategories(const t_CATEGORY_VISITOR& visitor) const { bslmt::ReadLockGuard<bslmt::ReaderWriterLock> guard(&d_registryLock); for (bsl::vector<Category *>::const_iterator it = d_categories.begin(); it != d_categories.end(); ++it) { visitor(*it); } } #ifndef BDE_OMIT_INTERNAL_DEPRECATED // ------------------------- // class CategoryManagerIter // ------------------------- // CREATORS inline CategoryManagerIter::CategoryManagerIter( const CategoryManager& categoryManager) : d_cm_p(&categoryManager) , d_index(0) { } // MANIPULATORS inline void CategoryManagerIter::operator++() { ++d_index; } // ACCESSORS inline CategoryManagerIter::operator const void *() const { return (0 <= d_index && d_index < d_cm_p->length()) ? this : 0; } inline const Category& CategoryManagerIter::operator()() const { return d_cm_p->operator[](d_index); } // -------------------------- // class CategoryManagerManip // -------------------------- // CREATORS inline CategoryManagerManip::CategoryManagerManip(CategoryManager *categoryManager) : d_cm_p(categoryManager) , d_index(0) { } // MANIPULATORS inline void CategoryManagerManip::advance() { ++d_index; } inline Category& CategoryManagerManip::operator()() { return d_cm_p->operator[](d_index); } // ACCESSORS inline CategoryManagerManip::operator const void *() const { return (0 <= d_index && d_index < d_cm_p->length()) ? this : 0; } #endif // BDE_OMIT_INTERNAL_DEPRECATED } // 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 ----------------------------------