BDE 4.39.x Production Release
Loading...
Searching...
No Matches
ball_categorymanager

Detailed Description

Provide a manager of named categories each having "thresholds".

Outline

Purpose

Provide a manager of named categories each having "thresholds".

Classes

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 ).

Category Threshold Levels

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:

Non-Hierarchical Default Threshold Mechanisms

The category manager provides several mechanisms for determining threshold levels when categories are created without explicit threshold values:

The precedence order for determining thresholds when creating categories is:

  1. Explicit threshold values provided to addCategory
  2. Default threshold callback (if installed)
  3. Current default threshold levels

Hierarchical Category Management

While 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 Name Filtering

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.

Registry Capacity Management

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.

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

This section illustrates intended use of this component.

Example 1: Basic 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:

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);
}
Definition ball_categorymanager.h:426
Category * addCategory(const char *categoryName, int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel)
Definition ball_categorymanager.h:1215

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;
}
Category * lookupCategory(const char *categoryName)
Definition ball_category.h:184
int triggerLevel() const
Return the trigger level of this category.
Definition ball_category.h:552
int triggerAllLevel() const
Return the trigger-all level of this category.
Definition ball_category.h:559
int recordLevel() const
Return the record level of this category.
Definition ball_category.h:538
int passLevel() const
Return the pass level of this category.
Definition ball_category.h:545

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);
}
int setLevels(int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel)

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);
}
int length() const
Definition ball_categorymanager.h:1282

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;
}
const char * categoryName() const
Return the name of this category.
Definition ball_category.h:520

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 ]

Example 2: Hierarchical Category Management

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:

manager.setDefaultThresholdLevels(191, 95, 63, 31);
int setDefaultThresholdLevels(int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel)

Then, we create two new categories, "EQ" and "EQ.MARKET", with explicitly set threshold levels (different from the defaults):

manager.addCategory("EQ", 192, 96, 64, 32);
manager.addCategory("EQ.MARKET", 193, 97, 65, 33);

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:

ball::Category *nyseCategory =
manager.addCategoryHierarchically("EQ.MARKET.NYSE");
Category * addCategoryHierarchically(const char *categoryName)

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:

assert(193 == nyseCategory->recordLevel());
assert( 97 == nyseCategory->passLevel());
assert( 65 == nyseCategory->triggerLevel());
assert( 33 == nyseCategory->triggerAllLevel());

Then, we use setThresholdLevelsHierarchically to adjust the threshold levels for all categories whose name starts with "EQ.MARKET":

int numUpdated = manager.setThresholdLevelsHierarchically("EQ.MARKET",
194,
98,
66,
34);
assert(2 == numUpdated); // Updated "EQ.MARKET" and "EQ.MARKET.NYSE"
int setThresholdLevelsHierarchically(const char *categoryNamePrefix, int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel)

We can verify that both "EQ.MARKET" and "EQ.MARKET.NYSE" have been updated, while "EQ" remains unchanged:

const ball::Category *eqCategory = manager.lookupCategory("EQ");
const ball::Category *marketCategory =
manager.lookupCategory("EQ.MARKET");
const ball::Category *nyseCategory2 =
manager.lookupCategory("EQ.MARKET.NYSE");
assert(192 == eqCategory->recordLevel()); // unchanged
assert(194 == marketCategory->recordLevel()); // updated
assert(194 == nyseCategory2->recordLevel()); // updated

Finally, if we add another category under "EQ.MARKET" using addCategoryHierarchically, it will inherit the updated thresholds:

ball::Category *nasdaqCategory =
manager.addCategoryHierarchically("EQ.MARKET.NASDAQ");
assert(194 == nasdaqCategory->recordLevel());
assert( 98 == nasdaqCategory->passLevel());
assert( 66 == nasdaqCategory->triggerLevel());
assert( 34 == nasdaqCategory->triggerAllLevel());

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.