|
BDE 4.39.x Production Release
|
Provide a manager of named categories each having "thresholds".
Provide a manager of named categories each having "thresholds".
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 ).
Every category has four severity threshold levels that govern logging behavior: Record, Pass, Trigger, and Trigger-All. These threshold levels can be set explicitly when a category is created (via addCategory) or derived from default values when using addCategory with fewer arguments or addCategoryHierarchically.
The threshold levels have the following meanings in the logging framework:
The category manager provides several mechanisms for determining threshold levels when categories are created without explicit threshold values:
defaultRecordThresholdLevel(), defaultPassThresholdLevel(), defaultTriggerThresholdLevel(), and defaultTriggerAllThresholdLevel(). These defaults can be modified at any time using setDefaultThresholdLevels.DefaultThresholdLevelsCallback functor can be installed (via setDefaultThresholdLevelsCallback) to dynamically compute threshold levels for new categories. When installed, this callback takes precedence over the (above) default threshold levels mechanism. The callback receives the category name and loads four threshold values into output parameters. This allows for sophisticated threshold management strategies.resetDefaultThresholdLevels.The precedence order for determining thresholds when creating categories is:
addCategoryWhile categories are fundamentally flat (category names have no intrinsic hierarchical structure), the category manager provides functions that support hierarchical naming conventions. The addCategoryHierarchically method creates a new category that inherits threshold levels from an existing category whose name is the longest prefix match. The setThresholdLevelsHierarchically method modifies thresholds for all categories whose names share a common prefix.
For example, consider categories named "EQUITY", "EQUITY.MARKET", and "EQUITY.MARKET.NYSE". Using addCategoryHierarchically to add "EQUITY.MARKET.NYSE" would cause it to inherit threshold levels from "EQUITY.MARKET" (the longest prefix match), not from "EQUITY" or the defaults. Using setThresholdLevelsHierarchically("EQUITY.MARKET", ...) would update both "EQUITY.MARKET" and "EQUITY.MARKET.NYSE", but not "EQUITY".
This hierarchical support facilitates organizing logging categories into logical groupings where related categories can share common threshold configurations while still allowing fine-grained control.
In order to keep the hierarchical category management sane the empty category (the default category added by LoggerManager) is treated as if it did not exist for hierarchical settings. So when looking for the longest matching prefix, if it is found to be the empty string, we use the default threshold levels instead of the levels of the empty category. Similarly, setting hierarchical levels with an empty prefix will never store an orphaned setting (even if the default category does not exist) but instead we just update the threshold levels of all existing categories and drop all orphaned settings.
Category names can be transformed by a CategoryNameFilterCallback functor before being stored in the registry. This allows for normalization of category names, such as converting all names to lowercase. When a name filter is installed (via setCategoryNameFilterCallback), it is applied to every category name on addition and lookup operations, ensuring consistent naming regardless of how client code specifies names.
The category registry can have a maximum capacity limit set via setMaxNumCategories. A value of 0 (the default) means no limit is imposed. When the limit is reached, attempts to add new categories will fail (methods that add categories will return null pointers). The current capacity limit can be queried via maxNumCategories, and the current number of categories via length.
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.
This section illustrates intended use of this component.
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:
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:
In the following, each of the new categories is accessed from the registry and their names and threshold levels printed:
The following is printed to stdout:
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:
Repeating the second for loop from above generates the following output on stdout:
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:
Finally, we iterate over the categories in the registry to print them out one last time:
This iteration produces the following output on stdout:
The following example demonstrates hierarchical category management using addCategoryHierarchically and setThresholdLevelsHierarchically. These methods support a hierarchical naming scheme where categories can inherit threshold levels from ancestor categories based on prefix matching.
First, we create a category manager and set default threshold levels:
Then, we create two new categories, "EQ" and "EQ.MARKET", with explicitly set threshold levels (different from the defaults):
Next, we add a new category using addCategoryHierarchically. This method finds the longest prefix match among existing categories and inherits threshold levels from that category:
The new category "EQ.MARKET.NYSE" inherits its threshold levels from "EQ.MARKET" (rather than from "EQ" or the defaults) because "EQ.MARKET" is the longest prefix match:
Then, we use setThresholdLevelsHierarchically to adjust the threshold levels for all categories whose name starts with "EQ.MARKET":
We can verify that both "EQ.MARKET" and "EQ.MARKET.NYSE" have been updated, while "EQ" remains unchanged:
Finally, if we add another category under "EQ.MARKET" using addCategoryHierarchically, it will inherit the updated thresholds:
Note that hierarchical category management facilitates organizing logging categories into logical groupings where related categories can share common threshold configurations while still allowing fine-grained control over individual categories.