BDE 4.14.0 Production release
Loading...
Searching...
No Matches
ball_attributecontext

Detailed Description

Outline

Purpose

Provide a container for storing attributes and caching results.

Classes

See also
ball_attributecontainer

Description

This component provides a mechanism, ball::AttributeContext, used for storing attributes in thread-local storage and evaluating rules associated with a given category using those stored attributes, and a scoped proctor, ball::AttributeContextProctor, used for destroying the attribute context of the current thread.

This component participates in the implementation of "Rule-Based Logging". For more information on how to use that feature, please see the package level documentation and usage examples for "Rule-Based Logging".

Clients obtain the attribute context for the current thread by calling the getContext class method. Attributes are added and removed from an attribute context using the addAttributes and removeAttributes methods, respectively. Additionally, ball::AttributeContext provides methods, used primarily by other components in the ball package' (see {Active Rules} below), to determine the effect of the current logging rules on the logging thresholds of a category.

ball::AttributeContext contains class data members that must be initialized, using the initialize class method, with a CategoryManager object containing a RuleSet representing the currently installed (global) logging rules for the process. However, clients generally should not call initialize directly. Instead, initialize is called internally when the logger manager singleton is initialized.

Active Rules

ball::AttributeContext provides two methods, hasRelevantActiveRules and determineThresholdLevels, that are used to determine the effect of the current logging rules maintained by the category manager on the logging thresholds of a given category. Note that these methods are generally intended for use by other components in the ball package'.

hasRelevantActiveRules returns true if there is at least one relevant and active rule (in the global set of rules) that might modify the logging thresholds of the supplied category. A rule is "relevant" if the rule's pattern matches the category's name, and a rule is "active" if all the attributes defined for that rule are satisfied by the current thread's attributes (i.e., ball::Rule::evaluate returns true for the collection of attributes maintained for the current thread by the thread's ball::AttributeContext object).

determineThresholdLevels returns the logging threshold levels for a category, factoring in any active rules that apply to the category that might override the category's thresholds.

Usage

This section illustrates the intended use of ball::AttributeContext.

Example 1: Managing Attributes

First we will define a thread function that will create and install two attributes. Note that we will use the AttributeSet implementation of the ball::AttributeContainer protocol defined in the component documentation for ball_attributecontainer ; the ball package provides a similar class in the ball_defaultattributecontainer component.

extern "C" void *workerThread1(void *)
{

Inside this thread function, we create an attribute set to hold our attribute values, then we create two ball::Attribute objects and add them to that set:

AttributeSet attributes;
ball::Attribute a1("uuid", 4044457);
ball::Attribute a2("name", "Gang Chen");
attributes.insert(a1);
attributes.insert(a2);
Definition ball_attribute.h:198

Next, we obtain a reference to the current thread's attribute context using the getContext class method (note that in practice we would use a scoped guard for this purpose; see ball_scopedattributes ):

assert(context);
Definition ball_attributecontext.h:520
static AttributeContext * getContext()
static AttributeContext * lookupContext()

We can add our attribute container, attributes, to the current context using the addAttributes method. We store the returned iterator so that we can remove attributes before it goes out of scope and is destroyed:

context->addAttributes(&attributes);
assert(context->hasAttribute(a1));
assert(context->hasAttribute(a2));
Definition ball_attributecontainerlist.h:168
bool hasAttribute(const Attribute &value) const
Definition ball_attributecontext.h:825
iterator addAttributes(const AttributeContainer *attributes)
Definition ball_attributecontext.h:796

We then call the removeAttributes method to remove the attributes from the attribute context:

context->removeAttributes(it);
assert(false == context->hasAttribute(a1));
assert(false == context->hasAttribute(a2));
void removeAttributes(iterator element)
Definition ball_attributecontext.h:811

This completes the first thread function:

return 0;
}

The second thread function will simply verify that there is no currently available attribute context. Note that attribute contexts are created and managed by individual threads using thread-specific storage, and that attribute contexts created by one thread are not visible in any other threads:

extern "C" void *workerThread2(void *)
{
return 0;
}

Example 2: Calling hasRelevantActiveRules and determineThresholdLevels

In this example we demonstrate how to call the hasRelevantActiveRules and determineThresholdLevels methods. These methods are used (primarily by other components in the ball package) to determine the effect of the current logging rules on the logging thresholds of a category. Note that a rule is "relevant" if the rule's pattern matches the category's name, and a rule is "active" if ball::Rule::evaluate returns true for the collection of attributes maintained for the current thread by the thread's ball::AttributeContext object.

We start by creating a ball::CategoryManager and use it to initialize the static data members of ball::AttributeContext. Note that, in practice, this initialization should not be performed by clients of the ball package: ball::AttributeContext::initialize is called internally as part of the initialization of the ball::LoggerManager singleton.

ball::CategoryManager categoryManager;
static void initialize(CategoryManager *categoryManager, bslma::Allocator *globalAllocator=0)
Definition ball_categorymanager.h:231

Next, we add a category to the category manager. Each created category has a name and the logging threshold levels for that category. The logging threshold levels indicate the minimum severity for logged messages that will trigger the relevant action. The four thresholds are the "record level" (messages logged with a higher severity than this threshold should be added to the current logger's record buffer), the "pass-through level" (messages logged with a severity higher than this threshold should be published immediately), the "trigger level" (messages logged with a higher severity than this threshold should trigger the publication of the entire contents of the current logger's record buffer), and the "trigger-all level" (messages logged with a higher severity than this threshold should trigger the publication of every logger's record buffer), respectively. Note that clients are generally most interested in the "pass-through" threshold level. Also note that a higher number indicates a lower severity.

const ball::Category *cat1 =
categoryManager.addCategory("MyCategory", 128, 96, 64, 32);
Category * addCategory(const char *categoryName, int recordLevel, int passLevel, int triggerLevel, int triggerAllLevel)
Definition ball_category.h:184

Next, we obtain the context for the current thread:

We call hasRelevantActiveRules on cat1. This will be false because we haven't supplied any rules:

assert(!context->hasRelevantActiveRules(cat1));
bool hasRelevantActiveRules(const Category *category) const

We call determineThresholdLevels on cat1. This will simply return the logging threshold levels we defined for cat1 when it was created because no rules have been defined that might modify those thresholds:

ball::ThresholdAggregate cat1ThresholdLevels(0, 0, 0, 0);
context->determineThresholdLevels(&cat1ThresholdLevels, cat1);
assert(128 == cat1ThresholdLevels.recordLevel());
assert( 96 == cat1ThresholdLevels.passLevel());
assert( 64 == cat1ThresholdLevels.triggerLevel());
assert( 32 == cat1ThresholdLevels.triggerAllLevel());
void determineThresholdLevels(ThresholdAggregate *levels, const Category *category) const
Definition ball_thresholdaggregate.h:97

Next, we create a rule that will apply to those categories whose names match the pattern "My*", where * is a wild-card value. The rule defines a set of thresholds levels that may override the threshold levels of those categories whose name matches the rule's pattern:

ball::Rule myRule("My*", 120, 110, 70, 40);
categoryManager.addRule(myRule);
int addRule(const Rule &ruleToAdd)
Definition ball_rule.h:177

Now, we call hasRelevantActiveRules again for cat1, but this time the method returns true because the rule we just added is both "relevant" to cat1 and "active". myRule is "relevant" to cat1 because the name of cat1 ("MyCategory") matches the pattern for myRule ("My*") (i.e., myRule applies to cat1). myRule is also "active" because all the attributes defined for the rule are satisfied by the current thread (in this case the rule has no attributes, so the rule is always "active"). Note that we will discuss the meaning of "active" and the use of attributes later in this example.

assert(context->hasRelevantActiveRules(cat1));

Next, we call determineThresholdLevels for cat1. This method compares the threshold levels defined for a category with those of any active rules that apply to that category, and determines the minimum severity (i.e., the maximum numerical value) for each respective threshold amongst those values.

ball::ThresholdAggregate thresholdLevels(0, 0, 0, 0);
context->determineThresholdLevels(&thresholdLevels, cat1);
assert(128 == thresholdLevels.recordLevel());
assert(110 == thresholdLevels.passLevel());
assert( 70 == thresholdLevels.triggerLevel());
assert( 40 == thresholdLevels.triggerAllLevel());

In this case the "pass-through", "trigger", and "trigger-all" threshold levels defined by myRule (110, 70, and 40) are greater (i.e., define a lower severity) than those respective values defined for cat1 (96, 64, and 32), so those values override the values defined for cat1. On the other hand, the "record" threshold level for cat1 (128) is greater than the value defined by myRule (120), so the threshold level defined for cat1 is returned. In effect, myRule has lowered the severity at which messages logged in the "MyCategory" category will be published immediately, trigger the publication of the current logger's record buffer, and trigger the publication of every logger's record buffer.

Next we modify myRule, adding an attribute indicating that the rule should only apply if the attribute context for the current thread contains the attribute ("uuid", 3938908):

categoryManager.removeRule(myRule);
ball::ManagedAttribute attribute("uuid", 3938908);
myRule.addAttribute(attribute);
categorymanager.addRule(myRule);
int removeRule(const Rule &ruleToRemove)
Definition ball_managedattribute.h:117

When we again call hasRelevantActiveRules for cat1, it now returns false. The rule, myRule, still applies to cat1 (i.e., it is still "relevant" to cat1), but the attributes defined by myRule are no longer satisfied by the current thread, i.e., the current thread's attribute context does not contain an attribute matching ("uuid", 3938908).

assert(!context->hasRelevantActiveRules(cat1));

Next, we call determineThresholdLevels on cat1 and find that it returns the threshold levels we defined for cat1 when we created it:

context->determineThresholdLevels(&thresholdLevels, cat1);
assert(thresholdLevels == cat1ThresholdLevels);

Finally, we add an attribute to the current thread's attribute context (as we did in the first example, "Managing Attributes"). Note that we keep an iterator referring to the added attributes so that we can remove them before attributes goes out of scope and is destroyed. Also note that the class AttributeSet is defined in the component documentation for ball_attributecontainer .

AttributeSet attributes;
attributes.insert(ball::Attribute("uuid", 3938908));

The following call to hasRelevantActiveRules will return true for cat1 because there is at least one rule, myRule, that is both "relevant" (i.e., its pattern matches the category name of cat1) and "active" (i.e., all of the attributes defined for myRule are satisfied by the attributes held by this thread's attribute context):

assert(context->hasRelevantActiveRules(cat1));

Now, when we call determineThresholdLevels, it will again return the maximum threshold level from cat1 and myRule:

context->determineThresholdLevels(&thresholdLevels, cat1);
assert(128 == thresholdLevels.recordLevel());
assert(110 == thresholdLevels.passLevel());
assert( 70 == thresholdLevels.triggerLevel());
assert( 40 == thresholdLevels.triggerAllLevel());

We must be careful to remove attributes from the attribute context before it goes out of scope and is destroyed. Note that the ball package provides a component, ball_scopedattributes , for adding, and automatically removing, attributes from the current thread's attribute context.

context->removeAttributes(it);