Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component ball_loggerfunctorpayloads
[Package ball]

Provide a suite of logger manager singleton functor payloads. More...

Namespaces

namespace  ball

Detailed Description

Outline
Purpose:
Provide a suite of logger manager singleton functor payloads.
Classes:
ball::LoggerFunctorPayloads namespace for logger manager functor payloads
See also:
Component ball_loggermanagerconfiguration, Component ball_loggermanager
Description:
This component provides a namespace, ball::LoggerFunctorPayloads, containing a suite of functions, each of which may be used as the function body "payload" of one of the various bsl::function functors used as callbacks in the ball_loggermanager component. Each function provides a specific customization or convenience enhancement to the basic logger functionality.
WARNING: Note that all functions defined in this component are intended for use with the logger manager singleton only, and not with any non-singleton instances of ball::LoggerManager; in particular, a precondition of each of the functions is that the logger manager singleton must be initialized and not in the process of being shut down.
The lone function defined in ball::LoggerFunctorPayloads at this time, loadParentCategoryThresholdValues, has signature:
  (int *, int *, int *, int *, const char *, char)
The initial five types in this signature match the following typedef from ball::LoggerManager (and ball::LoggerManagerConfiguration):
  typedef bsl::function<void(int *, int *, int *, int *, const char *)>
                                              DefaultThresholdLevelsCallback;
      // 'DefaultThresholdLevelsCallback' is the type of the functor that
      // determines default threshold levels for categories added to the
      // registry by the 'setCategory(const char *)' method.
The purpose of the trailing char argument is discussed in the following section, the Usage example, and the loadParentCategoryThresholdValues function-level documentation.
Support for Hierarchical Category Names:
The ball logging toolkit does not explicitly support any structure in the registry of category names; each unique sequence of characters defines a unique category that is, from the logger's perspective, a "peer" to all other categories. The toolkit does, however, provide several callback mechanisms that facilitate customized naming conventions.
In particular, the ball::LoggerManager singleton can be configured with a DefaultThresholdLevelsCallback (see the typedef definition above) that, if not null, is invoked to populate the four threshold levels whenever the user creates a category having the "default" thresholds. This component provides the function loadParentCategoryThresholdValues that can be used as the payload to DefaultThresholdLevelsCallback. loadParentCategoryThresholdValues populates its four category threshold level arguments from a specified "child" category name and a char delimiter by searching the category registry for a name that is "the most proximate parent category", assuming a hierarchical naming convention that uses the delimiter character. If such a parent category is found, the "child" category will receive the threshold levels of the parent. If no such parent exists, the new category will receive the standard "default" thresholds that it would have gotten had the callback been null.
Usage:
This section illustrates intended use of this component.
Example: Basic Usage:
The following code snippets illustrate how to use this component's loadParentCategoryThresholdValues method to allow a newly-created "child" category to inherit the logging threshold levels from its most proximate parent category (if such a category already exists). Note that the category "hierarchy" is by naming convention only, but that the callback makes it simple for the user to impose hierarchical meaning on names that are, from the logger's perspective, peers. In this example, we will choose the dot character (.) as the hierarchy delimiter; to the logger itself, . is not special.
To keep this example transparent, we will create and inspect several categories within main directly; some categories will be "declared" to be "parent" categories, and we will set the threshold levels explicitly, while other categories will act as "children", which is to say that they will obtain their threshold levels through the callback mechanism. In a more realistic example, there would be no explicit distinction between "parent" and "child" categories, but rather as categories are dynamically administered by the user, newly created categories would pick up the changes made to existing parents. As a practical matter, the beginning of the function main constitute the "usage" to install the callback; the rest of this example merely illustrates the consequences of that.
First, we load the logger manager configuration with the desired "payload" function, ball::LoggerFunctorPayloads::loadParentCategoryThresholdValues, and use the trailing char argument delimiter, set to the value ., which will be bound into the functor and supplied back to the payload on each invocation.
  // myapp.cpp
  int main()
  {
      using namespace bdlf::PlaceHolders;

      ball::LoggerManagerConfiguration configuration;
      char delimiter = '.';
      configuration.setDefaultThresholdLevelsCallback(
          bdlf::BindUtil::bind(
             &ball::LoggerFunctorPayloads::loadParentCategoryThresholdValues,
             _1,
             _2,
             _3,
             _4,
             _5,
             delimiter));
Then, we initialize the logger manager, using the configuration defined above:
      ball::LoggerManagerScopedGuard guard(configuration);
The above code is all that the user needs to do to customize the logger to "inherit" thresholds from parents. The rest of this example illustrates the consequences of having installed myCallback. For convenience in what follows, we define a reference, manager, to the singleton logger manager. We now create two "parent" categories named "EQUITY.MARKET" and "EQUITY.GRAPHICS", and give them arbitrary but distinct threshold levels. We also set the default levels to distinct values in order to be able to verify exactly where "child" levels have come from later on.
      manager.setDefaultThresholdLevels(128, 96, 64, 32);

      manager.addCategory("EQUITY.MARKET", 127, 95, 63, 31);
      manager.addCategory("EQUITY.GRAPHICS", 129, 97, 65, 33);
Note that the call to addCategory, which takes the four int threshold arguments, does not invoke the callback at all, but rather -- assuming that the named category does not yet exist -- sets the thresholds to the specified values directly.
We can use the logger manager interface to verify that the levels have been set. First, we use the lookupCategory method to obtain the two parent categories (here assigned p1 and p2).
      const ball::Category *p1 = manager.lookupCategory("EQUITY.MARKET");
      const ball::Category *p2 = manager.lookupCategory("EQUITY.GRAPHICS");
Next, we can use the appropriate ball::Category accessor methods to assert the expected results. Recall that the ordered sequence of levels is "Record", "Pass", "Trigger", and "TriggerAll".
      assert(127 == p1->recordLevel());
      assert( 95 == p1->passLevel());
      assert( 63 == p1->triggerLevel());
      assert( 31 == p1->triggerAllLevel());

      assert(129 == p2->recordLevel());
      assert( 97 == p2->passLevel());
      assert( 65 == p2->triggerLevel());
      assert( 33 == p2->triggerAllLevel());
Now, we will add several "child" categories using the setCategory method taking a single argument, the char* category name. This method uses the callback in determining the "default" threshold levels to use. The six statements are numbered for subsequent discussion.
      manager.setCategory("EQUITY.MARKET.NYSE");                      // (1)
      manager.setCategory("EQUITY.MARKET.NASDAQ");                    // (2)
      manager.setCategory("EQUITY.GRAPHICS.MATH.FACTORIAL");          // (3)
      manager.setCategory("EQUITY.GRAPHICS.MATH.ACKERMANN");          // (4)
      manager.setCategory("EQUITY.GRAPHICS.MATH");                    // (5)
      manager.setCategory("EQUITY");                                  // (6)
Note that all six calls to setCategory will succeed in adding new categories to the registry. Calls (1)-(5) will "find" their parent's names and "inherit" the parent's levels. Call (6), however, will not find a parent category, and so will receive the default threshold levels, just as if there were no callback installed.
Note also that, although in this "static" (i.e., unadministered) example there is no significance to the order in which the above categories are created, in general (e.g., when categories are being dynamically administered) the order of creation does matter. If line (5) were executed before line (4) then the call on line (4) would find the "EQUITY.GRAPHICS.MATH" category as its "parent" and inherit those threshold levels. If, before line (4) executed, the thresholds of "EQUITY.GRAPHICS.MATH" were changed, then "EQUITY.GRAPHICS.MATH.FACTORIAL" and "EQUITY.GRAPHICS.MATH.ACKERMANN" would have different threshold levels despite their equivalent standing in the category hierarchy.
Let us now verify some of the 24 threshold levels that have been set by the above calls. We will verify the results of lines (1), (3), and (6) above.
      const ball::Category *c1, *c3, *c6;

      c1 =  manager.lookupCategory("EQUITY.MARKET.NYSE");
      assert(127 == c1->recordLevel());
      assert( 95 == c1->passLevel());
      assert( 63 == c1->triggerLevel());
      assert( 31 == c1->triggerAllLevel());

      c3 =  manager.lookupCategory("EQUITY.GRAPHICS.MATH.FACTORIAL");
      assert(129 == c3->recordLevel());
      assert( 97 == c3->passLevel());
      assert( 65 == c3->triggerLevel());
      assert( 33 == c3->triggerAllLevel());

      c6 =  manager.lookupCategory("EQUITY");
      assert(128 == c6->recordLevel());
      assert( 96 == c6->passLevel());
      assert( 64 == c6->triggerLevel());
      assert( 32 == c6->triggerAllLevel());

      return 0;
  }