// ball_category.h -*-C++-*- #ifndef INCLUDED_BALL_CATEGORY #define INCLUDED_BALL_CATEGORY #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a container for a name and associated thresholds. // //@CLASSES: // ball::Category: container for a name and associated threshold levels // ball::CategoryHolder: *private* holder of a category and its maximum level // ball::CategoryManagerImpUtil: *private* used in creating a category manager // //@SEE_ALSO: ball_categorymanager // //@DESCRIPTION: This component primarily provides a class, 'ball::Category', // used to describe the properties of a logging category. A 'ball::Category' // provides access to the category name and the 4 logging threshold levels // associated with a category (see {'ball_loggermanager'} for a description of // the purpose of the various thresholds). // ///'ball' "Private" Methods and Classes ///------------------------------------ // This component provides classes that are *not* intended for use by the users // of the 'ball' logging sub-system: 'ball::CategoryHolder' and // 'ball::CategoryManagerImpUtil'. These classes are defined in this component // because they are either friends of 'ball::Category' or have a circular // definition with 'ball::Category'. They are used within the logging // sub-system to efficiently process log records. // ///'ball::CategoryHolder' /// - - - - - - - - - - - // A 'ball::CategoryHolder' is a statically-initializable pointer to a log // category. It is designed to work with the logging macros provided by 'ball' // (see {'ball_log'}), and provide a static cache of the log category at the // point where a log macro is invoked. // ///'ball::CategoryManagerImpUtil' /// - - - - - - - - - - - - - - - // A 'ball::CategoryManagerImpUtil' provides a suite of utility functions used // in creating a manager for log categories (see {'ball_categorymanager'}). A // 'ball::Category' object maintains private state that is accessed and // manipulated via this utility. Each 'ball::Category' contains private data // members that provide: // //: o A linked list of associated 'ball::CategoryHolder' objects that refer to //: the category. //: //: o A cache of the logging rules that apply to the category. //: //: o A cache of the maximum threshold associated with any rule that applies //: to the category (this is the threshold at which a more complicated //: evaluation of the logging rules and current 'ball::AttributeContext' must //: be performed). // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Basic Use of 'ball::Category' /// - - - - - - - - - - - - - - - - - - - - // The following example demonstrates creating a category and accessing its // threshold information. // // Note that other components in the logging subsystem provide more user // focused examples of using categories (see {'ball_loggermanager'}, // {'ball_administration'}, and {'ball_categorymanager'}). // // First we create a simple category, 'example', that has the record-level, // trigger-level, and trigger-all thresholds set to OFF and the pass-level set // to WARN, and verify these values: //.. // ball::Category example("example", // ball::Severity::e_OFF, // ball::Severity::e_WARN, // ball::Severity::e_OFF, // ball::Severity::e_OFF); // // assert(0 == bsl::strcmp("example", example.categoryName()); // assert(ball::Severity::e_OFF == example.recordLevel()); // assert(ball::Severity::e_WARN == example.passLevel()); // assert(ball::Severity::e_OFF == example.triggerLevel()); // assert(ball::Severity::e_OFF == example.triggerAllLevel()); //.. // See {'ball_loggermanager'} for more information on the use of various // thresholds levels. // // Finally, we test if a the category is enabled for log record recorded with // 'e_ERROR' severity: //.. // if (example.isEnabled(ball::Severity::e_ERROR)) { // // publish record // } //.. #include <balscm_version.h> #include <ball_ruleset.h> #include <ball_thresholdaggregate.h> #include <bdlb_bitutil.h> #include <bslma_allocator.h> #include <bslma_usesbslmaallocator.h> #include <bslmf_nestedtraitdeclaration.h> #include <bsls_assert.h> #include <bsls_atomicoperations.h> #include <bsls_keyword.h> #include <bsls_review.h> #include <bsls_types.h> namespace BloombergLP { namespace ball { class CategoryHolder; // ============== // class Category // ============== class Category { // This class provides a container to hold the name and threshold levels of // a category. Instances of 'Category' are created and manipulated by // 'CategoryManager'. All threshold levels are integral values in the // range '[0 .. 255]'. // // Implementation Note: The 'd_ruleThreshold' and 'd_relevantRuleMask' // serve as a cache for logging rule evaluation (see // 'ball_attributecontext'). They are not meant to be modified by users // of the logging system, and may be modified by 'const' operations of the // logging system. // DATA ThresholdAggregate d_thresholdLevels; // record, pass, trigger, and // trigger-all levels int d_threshold; // numerical maximum of the four // levels bsl::string d_categoryName; // category name CategoryHolder *d_categoryHolder; // linked list of holders of this // category mutable bsls::AtomicOperations::AtomicTypes::Uint d_relevantRuleMask; // the mask indicating which rules // are relevant (i.e., have been // attached to this category) mutable int d_ruleThreshold; // numerical maximum of all four // levels for all relevant rules // FRIENDS friend class CategoryManagerImpUtil; // NOT IMPLEMENTED Category(const Category&); Category& operator=(const Category&); private: // PRIVATE MANIPULATORS void linkCategoryHolder(CategoryHolder *categoryHolder); // Load this category and its corresponding 'maxLevel()' into the // specified 'categoryHolder', and add 'categoryHolder' to the linked // list of category holders managed by this category. void resetCategoryHolders(); // Reset the category holders to which this category is linked to their // default value. See the function-level documentation of // 'CategoryHolder::reset' for further information on the default value // of category holders. void updateThresholdForHolders(); // Update the threshold of all category holders that hold the address // of this object to the maximum of 'd_threshold' and // 'd_ruleThreshold'. public: // CLASS METHODS static bool areValidThresholdLevels(int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel); // Return 'true' if each of the specified 'recordLevel', 'passLevel', // 'triggerLevel' and 'triggerAllLevel' threshold values are in the // range '[0 .. 255]', and 'false' otherwise. // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(Category, bslma::UsesBslmaAllocator); // CREATORS Category(const char *categoryName, int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel, bslma::Allocator *basicAllocator = 0); // Create a category having the specified 'categoryName' and the // specified 'recordLevel', 'passLevel', 'triggerLevel', and // 'triggerAllLevel' threshold values, respectively. Optionally // specify a 'basicAllocator' used to supply memory. If // 'basicAllocator' is 0, the currently installed default allocator is // used. The behavior is undefined unless each of the specified // threshold levels is in the range '[0 .. 255]', and 'categoryName' is // null-terminated. //! ~Category() = default; // Destroy this category. // MANIPULATORS int setLevels(int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel); // Set the threshold levels of this category to the specified // 'recordLevel', 'passLevel', 'triggerLevel', and 'triggerAllLevel' // values, respectively, if each of the specified values is in the // range '[0 .. 255]'. Return 0 on success, and a non-zero value // otherwise (with no effect on the threshold levels of this category). // ACCESSORS const char *categoryName() const; // Return the name of this category. bool isEnabled(int level) const; // Return 'true' if logging at the specified 'level' is enabled for // this category, and 'false' otherwise. Logging is enabled if 'level' // is numerically less than or equal to any of the four threshold // levels of this category. int maxLevel() const; // Return the numerical maximum of the four levels of this category. int recordLevel() const; // Return the record level of this category. int passLevel() const; // Return the pass level of this category. int triggerLevel() const; // Return the trigger level of this category. int triggerAllLevel() const; // Return the trigger-all level of this category. const ThresholdAggregate& thresholdLevels() const; // Return the aggregate threshold levels of this category. int threshold() const; // Return the current maximum threshold (i.e., the lowest severity) // between the 'recordLevel', 'passLevel', 'triggerLevel', and // 'triggerAllLevel'. Note that this is the threshold at which a log // record having this severity will need to be acted upon. int ruleThreshold() const; // Return the current maximum threshold (i.e., the lowest severity) for // any logging rule associated with this category. Note that the rule // having this threshold may not be active given the current thread's // logging context (see 'ball::AttributeContext'); this value caches // the lowest possible severity where the currently rules need to be // evaluated (log records below this threshold do not need any rule // evaluation). RuleSet::MaskType relevantRuleMask() const; // Return a reference to the non-modifiable relevant rule mask for this // category. The returned 'RuleSet::MaskType' value is a bit-mask, // where each bit is a boolean value indicating whether the rule at the // corresponding index (in the rule set of the category manager that // owns this category) applies at this category. Note that a rule // applies to this category if the rule's pattern matches the name // returned by 'categoryName'. }; // ==================== // class CategoryHolder // ==================== class CategoryHolder { // This class, informally referred to as a "category holder" (or simply // "holder"), holds a category, a threshold level, and a pointer to a // "next" holder. Both the category and next pointer may be null. The // intended use is as follows: (1) instances of this class are (only) // declared in contexts where logging occurs; (2) if the held category is // non-null, then the held threshold is the numerical maximum of the four // levels of that category; (3) if the next pointer is non-null, then the // holder pointed to holds the same category and threshold. Instances of // this class must be *statically* initializable. Hence, the data members // are 'public', and automatically generated constructors and destructor // are used. // // This class should *not* be used directly by client code. It is an // implementation detail of the 'ball' logging system. // NOT IMPLEMENTED CategoryHolder& operator=(const CategoryHolder&) BSLS_KEYWORD_DELETED; typedef bsls::AtomicOperations AtomicOps; typedef bsls::AtomicOperations::AtomicTypes::Int AtomicInt; typedef bsls::AtomicOperations::AtomicTypes::Pointer AtomicPointer; public: // PUBLIC TYPES enum { e_UNINITIALIZED_CATEGORY = 256, // indicates no logger manager e_DYNAMIC_CATEGORY = 257 // corresponding category is dynamic #ifndef BDE_OMIT_INTERNAL_DEPRECATED , BAEL_UNINITIALIZED_CATEGORY = e_UNINITIALIZED_CATEGORY , BAEL_DYNAMIC_CATEGORY = e_DYNAMIC_CATEGORY , UNINITIALIZED_CATEGORY = e_UNINITIALIZED_CATEGORY , DYNAMIC_CATEGORY = e_DYNAMIC_CATEGORY #endif // BDE_OMIT_INTERNAL_DEPRECATED }; // This enumeration defines distinguished values for category holder // threshold levels. Note that these values are intentionally outside // the range '[0 .. 255]'. // PUBLIC DATA AtomicInt d_threshold; // threshold level AtomicPointer d_category_p; // held category (not owned) AtomicPointer d_next_p; // next category holder in linked list // CREATORS // No constructors or destructors are declared in order to allow for static // initialization of instances of this class. // MANIPULATORS void reset(); // Reset this object to its default value. The default value is: //.. // { e_UNINITIALIZED_CATEGORY, 0, 0 } //.. void setCategory(const Category *category); // Set the address of the category held by this holder to the specified // 'category'. void setThreshold(int threshold); // Set the threshold level held by this holder to the specified // 'threshold'. void setNext(CategoryHolder *holder); // Set this holder to point to the specified 'holder'. // ACCESSORS const Category *category() const; // Return the address of the non-modifiable category held by this // holder. int threshold() const; // Return the threshold level held by this holder. CategoryHolder *next() const; // Return the address of the modifiable holder held by this holder. }; // ============================ // class CategoryManagerImpUtil // ============================ class CategoryManagerImpUtil { // This class provides a suite of free functions used to help implement a // manager of categories and category holders. // // This class should *not* be used directly by client code. It is an // implementation detail of the 'ball' logging system. public: // PRIVATE MANIPULATORS static void linkCategoryHolder(Category *category, CategoryHolder *categoryHolder); // Load the specified 'category' and its corresponding 'maxLevel()' // into the specified 'categoryHolder', and add 'categoryHolder' to // the linked list of category holders maintained by 'category'. static void resetCategoryHolders(Category *category); // Reset the category holders to which the specified 'category' is // linked to their default value. See the function-level documentation // of 'CategoryHolder::reset' for further information on the default // value of category holders. static void updateThresholdForHolders(Category *category); // Update the threshold of all category holders that hold the address // of the specified 'category' object to the maximum of 'd_threshold' // and 'd_ruleThreshold'. static void setRuleThreshold(Category *category, int ruleThreshold); // Set the cached rule threshold for the specified 'category' to the // specified 'ruleThreshold'. static void enableRule(Category *category, int ruleIndex); // Set the bit in the relevant rule-mask at the specified 'ruleIndex' // in the specified 'category' to 'true'. static void disableRule(Category *category, int ruleIndex); // Set the bit in the rule-mask at the specified 'ruleIndex' in the // specified 'category' to 'false'. static void setRelevantRuleMask(Category *category, RuleSet::MaskType mask); // Set the rule-mask for the specified 'category' to the specified // 'mask'. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // -------------- // class Category // -------------- // CLASS METHODS inline bool Category::areValidThresholdLevels(int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel) { enum { k_BITS_PER_CHAR = 8 }; return !((recordLevel | passLevel | triggerLevel | triggerAllLevel) >> k_BITS_PER_CHAR); } // ACCESSORS inline const char *Category::categoryName() const { return d_categoryName.c_str(); } inline bool Category::isEnabled(int level) const { return d_threshold >= level; } inline int Category::maxLevel() const { return d_threshold; } inline int Category::recordLevel() const { return d_thresholdLevels.recordLevel(); } inline int Category::passLevel() const { return d_thresholdLevels.passLevel(); } inline int Category::triggerLevel() const { return d_thresholdLevels.triggerLevel(); } inline int Category::triggerAllLevel() const { return d_thresholdLevels.triggerAllLevel(); } inline const ThresholdAggregate& Category::thresholdLevels() const { return d_thresholdLevels; } inline int Category::threshold() const { return d_threshold; } inline int Category::ruleThreshold() const { return d_ruleThreshold; } inline RuleSet::MaskType Category::relevantRuleMask() const { return bsls::AtomicOperations::getUintAcquire(&d_relevantRuleMask); } // -------------------- // class CategoryHolder // -------------------- // MANIPULATORS inline void CategoryHolder::setCategory(const Category *category) { AtomicOps::setPtrRelease(&d_category_p, const_cast<Category *>(category)); } inline void CategoryHolder::setThreshold(int threshold) { AtomicOps::setIntRelaxed(&d_threshold, threshold); } inline void CategoryHolder::setNext(CategoryHolder *holder) { AtomicOps::setPtrRelease(&d_next_p, holder); } // ACCESSORS inline const Category *CategoryHolder::category() const { return reinterpret_cast<const Category *>( AtomicOps::getPtrAcquire(&d_category_p)); } inline int CategoryHolder::threshold() const { return AtomicOps::getIntRelaxed(&d_threshold); } inline CategoryHolder *CategoryHolder::next() const { return reinterpret_cast<CategoryHolder *>( AtomicOps::getPtrAcquire(&d_next_p)); } // ---------------------------- // class CategoryManagerImpUtil // ---------------------------- // CLASS METHODS inline void CategoryManagerImpUtil::linkCategoryHolder(Category *category, CategoryHolder *categoryHolder) { BSLS_ASSERT(category); BSLS_ASSERT(categoryHolder); category->linkCategoryHolder(categoryHolder); } inline void CategoryManagerImpUtil::resetCategoryHolders(Category *category) { BSLS_ASSERT(category); category->resetCategoryHolders(); } inline void CategoryManagerImpUtil::updateThresholdForHolders(Category *category) { BSLS_ASSERT(category); category->updateThresholdForHolders(); } inline void CategoryManagerImpUtil::setRuleThreshold(Category *category, int ruleThreshold) { category->d_ruleThreshold = ruleThreshold; } inline void CategoryManagerImpUtil::enableRule(Category *category, int ruleIndex) { unsigned int currentMask = bsls::AtomicOperations::getUintRelaxed(&category->d_relevantRuleMask); unsigned int expectedMask; do { const unsigned int updatedMask = bdlb::BitUtil::withBitSet(currentMask, ruleIndex); expectedMask = currentMask; currentMask = bsls::AtomicOperations::testAndSwapUintAcqRel( &category->d_relevantRuleMask, currentMask, updatedMask); } while (expectedMask != currentMask); } inline void CategoryManagerImpUtil::disableRule(Category *category, int ruleIndex) { unsigned int currentMask = bsls::AtomicOperations::getUintRelaxed(&category->d_relevantRuleMask); unsigned int expectedMask; do { const unsigned int updatedMask = bdlb::BitUtil::withBitCleared(currentMask, ruleIndex); expectedMask = currentMask; currentMask = bsls::AtomicOperations::testAndSwapUintAcqRel( &category->d_relevantRuleMask, currentMask, updatedMask); } while (expectedMask != currentMask); } inline void CategoryManagerImpUtil::setRelevantRuleMask(Category *category, RuleSet::MaskType mask) { bsls::AtomicOperations::setUintRelease(&category->d_relevantRuleMask, mask); } } // 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 ----------------------------------