Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component ball_attributecontext
[Package ball]

Provide a container for storing attributes and caching results. More...

Namespaces

namespace  ball

Detailed Description

Outline
Purpose:
Provide a container for storing attributes and caching results.
Classes:
ball::AttributeContext thread-local list of attribute containers
ball::AttributeContextProctor proctor for deleting an attribute context
See also:
Component 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);
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): 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:
      ball::AttributeContext::iterator it =
                                         context->addAttributes(&attributes);
      assert(context->hasAttribute(a1));
      assert(context->hasAttribute(a2));
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));
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 *)
  {
      assert(0 == ball::AttributeContext::lookupContext());
      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;
  ball::AttributeContext::initialize(&categoryManager);
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);
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));
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());
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);
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);
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));
  ball::AttributeContext::iterator it = context->addAttributes(&attributes);
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);