|
BDE 4.39.x Production Release
|
Provide macros and utility functions to facilitate logging.
Provide macros and utility functions to facilitate logging.
e_TRACE severity levele_DEBUG severity levele_INFO severity levele_WARN severity levele_ERROR severity levele_FATAL severity levelSEV levele_TRACE log record using callback CBe_DEBUG log record using callback CBe_INFO log record using callback CBe_WARN log record using callback CBe_ERROR log record using callback CBe_FATAL log record using callback CBSEV log record using callbacke_TRACE record using printf formate_DEBUG record using printf formate_INFO record using printf formate_WARN record using printf formate_ERROR record using printf formate_FATAL record using printf formatSEV log record using printf formate_TRACE condition of executione_DEBUG condition of executione_INFO condition of executione_WARN condition of executione_ERROR condition of executione_FATAL condition of executionSEV conditione_TRACE block with the specified callbacke_DEBUG block with the specified callbacke_INFO block with the specified callbacke_WARN block with the specified callbacke_ERROR block with the specified CBe_FATAL block with the specified callbackSEV block with the specified CBSEV is severe enough for loggingThis component provides preprocessor macros and utility functions to facilitate use of the ball_loggermanager component. In particular, the macros defined herein greatly simplify the mechanics of generating log records. The utility functions provided in ball::Log are intended only for use by the macros and should not be called directly.
The macros defined herein pertain to the logger manager singleton only, and not to any non-singleton instances of ball::LoggerManager. In particular, the macros do not have any effect unless the logger manager singleton is initialized. Note that the flow of control may pass through a use of any of the macros before the logger manager singleton has been initialized or after it has been destroyed; however, control should not pass through any macro use during logger manager singleton initialization or destruction. See {ball_loggermanager |Logger Manager Singleton Initialization} for details on the recommended procedure for initializing the singleton.
By default, all category-setting macros in this component use hierarchical category creation. When a category is created hierarchically, its threshold levels are inherited from the existing non-default category whose name is the longest matching prefix of the new category's name. For example, if a category "X.Y" exists with custom threshold levels, creating a new category "X.Y.Z" hierarchically will cause "X.Y.Z" to inherit the threshold levels from "X.Y" rather than using the category manager's default threshold levels (static or callback-provided). This hierarchical inheritance provides a natural way to configure logging thresholds for related subsystems.
Threshold levels can also be inherited from orphaned settings. An orphaned setting is a set of threshold levels that has been configured for a category name that does not yet exist (via ball::CategoryManager::setThresholdLevelsHierarchically). When a category is later created with that name (or a name that has the orphaned setting's name as a prefix), it inherits from the orphaned setting. This allows pre-configuring thresholds for categories before any code that uses those categories has executed. Orphaned settings take precedence over existing parent categories when both match.
In contrast, non-hierarchical (or legacy) category creation always uses the category manager's default threshold levels (static or callback-provided) for new categories, regardless of whether parent categories exist. This was the original behavior of the standard category macros before hierarchical category creation was successfully sped up.
The standard macros (BALL_LOG_SET_CATEGORY, BALL_LOG_SET_DYNAMIC_CATEGORY, BALL_LOG_SET_CLASS_CATEGORY, and BALL_LOG_SET_NAMESPACE_CATEGORY) create categories hierarchically by default. For backward compatibility, legacy non-hierarchical versions of these macros are provided with the _LEGACY_ prefix (e.g., BALL_LOG_SET_LEGACY_CATEGORY). The legacy macros should only be used in existing code that depends on non-hierarchical behavior; new code should use the standard hierarchical macros.
Note that the _HIERARCHICALLY suffixed macros (BALL_LOG_SET_CATEGORY_HIERARCHICALLY, etc.) are deprecated and redundant, as the standard macros provide the same hierarchical behavior.
All macros defined in this component are thread-safe, and can be invoked concurrently by multiple threads.
Additionally, each use of a logging macro will create a distinct log record, and ball::Observer implementations (like those in ball) generally guarantee that output for different log records are not interleaved.
This section documents the preprocessor macros defined in this component.
The first three macros described below are used to define categories, at either block scope or class scope, to which records are logged by the C++ stream-based and printf-style logging macros (described further below).
The following macros are used to establish logging categories that have block scope:
BALL_LOG_SET_CATEGORY(CATEGORY): Set a category for logging to the specified CATEGORY (assumed to be of type convertible to const char *) using hierarchical category creation. On the first invocation of this macro in a code block, the ball::Log::setCategoryHierarchically method is invoked to retrieve the address of an appropriate category structure for its scope; subsequent invocations will use a cached address of the category. (See the function-level documentation of ball::Log::setCategoryHierarchically for more information.) This macro must be used at block scope, and can be used at most once in any given block (or else a compiler diagnostic will result). Note that this macro uses hierarchical category creation; use BALL_LOG_SET_LEGACY_CATEGORY for the legacy non-hierarchical behavior.BALL_LOG_SET_DYNAMIC_CATEGORY(CATEGORY): Set, on EACH invocation, a category for logging to the specified CATEGORY (assumed to be of type convertible to const char *) using hierarchical category creation. On EVERY invocation of this macro in a code block, the ball::Log::setCategoryHierarchically method is invoked to retrieve the address of an appropriate category structure for its scope; the address returned from ball::Log::setCategoryHierarchically is NOT cached for subsequent calls. (See the function-level documentation of ball::Log::setCategoryHierarchically for more information.) This macro must be used at block scope and can be used at most once in any given block (or else a compiler diagnostic will result). Note that this macro should be used to create categories that depend on RUN-TIME values only (e.g., LUW or UUID). Also note that this macro uses hierarchical category creation; use BALL_LOG_SET_LEGACY_DYNAMIC_CATEGORY for the legacy non-hierarchical behavior.BALL_LOG_SET_LEGACY_CATEGORY(CATEGORY): Set a category for logging to the specified CATEGORY (assumed to be of type convertible to const char *) using non-hierarchical category creation. On the first invocation of this macro in a code block, the ball::Log::setCategory method is invoked to retrieve the address of an appropriate category structure for its scope; subsequent invocations will use a cached address of the category. (See the function-level documentation of ball::Log::setCategory for more information.) This macro must be used at block scope, and can be used at most once in any given block (or else a compiler diagnostic will result). Note that this macro provides the legacy non-hierarchical behavior; new code should prefer BALL_LOG_SET_CATEGORY.BALL_LOG_SET_LEGACY_DYNAMIC_CATEGORY(CATEGORY): Set, on EACH invocation, a category for logging to the specified CATEGORY (assumed to be of type convertible to const char *) using non-hierarchical category creation. On EVERY invocation of this macro in a code block, the ball::Log::setCategory method is invoked to retrieve the address of an appropriate category structure for its scope; the address returned from ball::Log::setCategory is NOT cached for subsequent calls. (See the function-level documentation of ball::Log::setCategory for more information.) This macro must be used at block scope and can be used at most once in any given block (or else a compiler diagnostic will result). Note that this macro should be used to create categories that depend on RUN-TIME values only (e.g., LUW or UUID). Also note that this macro provides the legacy non-hierarchical behavior; new code should prefer BALL_LOG_SET_DYNAMIC_CATEGORY.There can be at most one use of BALL_LOG_SET_CATEGORY, BALL_LOG_SET_DYNAMIC_CATEGORY, BALL_LOG_SET_LEGACY_CATEGORY, or BALL_LOG_SET_LEGACY_DYNAMIC_CATEGORY in any given block (or else a compiler diagnostic will result). Note that categories that are set using these macros, including dynamic categories, are not destroyed until the logger manager singleton is destroyed.
The following macros are used to establish logging categories that have class scope:
BALL_LOG_SET_CLASS_CATEGORY(CATEGORY): Set a category for logging to the specified CATEGORY (assumed to be of type convertible to const char *) in the scope of the class within which this macro is used, using hierarchical category creation. Similar to BALL_LOG_SET_CATEGORY, the category is set once only, the first time that it is accessed (i.e., it is not a dynamic category). This macro must be used, at most once, within the definition of a class or class template (or else a compiler diagnostic will result). Note that use of this macro may occur in either a public, private, or protected section of a class's interface, although private should be preferred. Also note that this macro uses hierarchical category creation; use BALL_LOG_SET_LEGACY_CLASS_CATEGORY for the legacy non-hierarchical behavior.BALL_LOG_SET_LEGACY_CLASS_CATEGORY(CATEGORY): Set a category for logging to the specified CATEGORY (assumed to be of type convertible to const char *) in the scope of the class within which this macro is used, using non-hierarchical category creation. Similar to BALL_LOG_SET_LEGACY_CATEGORY, the category is set once only, the first time that it is accessed (i.e., it is not a dynamic category). This macro must be used, at most once, within the definition of a class or class template (or else a compiler diagnostic will result). Note that use of this macro may occur in either a public, private, or protected section of a class's interface, although private should be preferred. Also note that this macro provides the legacy non-hierarchical behavior; new code should prefer BALL_LOG_SET_CLASS_CATEGORY.Note that similar to block-scope categories (see BALL_LOG_SET_CATEGORY, BALL_LOG_SET_DYNAMIC_CATEGORY, BALL_LOG_SET_LEGACY_CATEGORY, and BALL_LOG_SET_LEGACY_DYNAMIC_CATEGORY), class-scope categories are not destroyed until the logger manager singleton is destroyed.
The following macros are used to establish logging categories that have namespace or global scope:
BALL_LOG_SET_NAMESPACE_CATEGORY(CATEGORY): Set a category for logging to the specified CATEGORY (assumed to be of type convertible to const char *) in the namespace (or global) scope within which this macro is used, using hierarchical category creation. Similar to BALL_LOG_SET_CATEGORY, the category is set once only, the first time that it is accessed (i.e., it is not a dynamic category). This macro may be used, in .cpp files only, at most once in any given namespace and at most once at global scope (or else a compiler diagnostic will result). Do NOT use this macro in .h files. Note that this macro uses hierarchical category creation; use BALL_LOG_SET_LEGACY_NAMESPACE_CATEGORY for the legacy non-hierarchical behavior.BALL_LOG_SET_LEGACY_NAMESPACE_CATEGORY(CATEGORY): Set a category for logging to the specified CATEGORY (assumed to be of type convertible to const char *) in the namespace (or global) scope within which this macro is used, using non-hierarchical category creation. Similar to BALL_LOG_SET_LEGACY_CATEGORY, the category is set once only, the first time that it is accessed (i.e., it is not a dynamic category). This macro may be used, in .cpp files only, at most once in any given namespace and at most once at global scope (or else a compiler diagnostic will result). Do NOT use this macro in .h files. Note that this macro provides the legacy non-hierarchical behavior; new code should prefer BALL_LOG_SET_NAMESPACE_CATEGORY.Note that similar to block-scope categories (see BALL_LOG_SET_CATEGORY, BALL_LOG_SET_DYNAMIC_CATEGORY, BALL_LOG_SET_LEGACY_CATEGORY, and BALL_LOG_SET_LEGACY_DYNAMIC_CATEGORY), namespace-scope categories are not destroyed until the logger manager singleton is destroyed.
Note that similar to block-scope categories (see BALL_LOG_SET_CATEGORY and BALL_LOG_SET_DYNAMIC_CATEGORY), namespace-scope categories are not destroyed until the logger manager singleton is destroyed.
!DEPRECATED! The following macros are deprecated and should not be used in new code. They were introduced when hierarchical category creation was significantly slower than non-hierarchical creation, providing an explicit opt-in for hierarchical behavior. Now that performance has been optimized and hierarchical category creation is the default, these macros are redundant with the standard category macros (BALL_LOG_SET_CATEGORY, BALL_LOG_SET_DYNAMIC_CATEGORY, BALL_LOG_SET_CLASS_CATEGORY, and BALL_LOG_SET_NAMESPACE_CATEGORY), which now use hierarchical creation by default. These deprecated macros are maintained only for backward compatibility with existing code.
BALL_LOG_SET_CATEGORY_HIERARCHICALLY(CATEGORY) !DEPRECATED! Use BALL_LOG_SET_CATEGORY(CATEGORY) instead. This macro is identical to BALL_LOG_SET_CATEGORY, which now uses hierarchical category creation by default.BALL_LOG_SET_DYNAMIC_CATEGORY_HIERARCHICALLY(CATEGORY) !DEPRECATED! Use BALL_LOG_SET_DYNAMIC_CATEGORY(CATEGORY) instead. This macro is identical to BALL_LOG_SET_DYNAMIC_CATEGORY, which now uses hierarchical category creation by default.BALL_LOG_SET_CLASS_CATEGORY_HIERARCHICALLY(CATEGORY) !DEPRECATED! Use BALL_LOG_SET_CLASS_CATEGORY(CATEGORY) instead. This macro is identical to BALL_LOG_SET_CLASS_CATEGORY, which now uses hierarchical category creation by default.BALL_LOG_SET_NAMESPACE_CATEGORY_HIERARCHICALLY(CATEGORY) !DEPRECATED! Use BALL_LOG_SET_NAMESPACE_CATEGORY(CATEGORY) instead. This macro is identical to BALL_LOG_SET_NAMESPACE_CATEGORY, which now uses hierarchical category creation by default.The following macros preserve the legacy non-hierarchical category creation behavior for backward compatibility. These macros use ball::Log::setCategory instead of ball::Log::setCategoryHierarchically, which means new categories will use the logger manager's default threshold levels rather than inheriting from parent categories. New code should prefer the standard macros (BALL_LOG_SET_CATEGORY, etc.) which use hierarchical category creation by default.
BALL_LOG_SET_LEGACY_CATEGORY(CATEGORY): Set a category for logging to the specified CATEGORY (assumed to be of type convertible to const char *) using non-hierarchical category creation. This macro provides the legacy behavior where ball::Log::setCategory is called instead of ball::Log::setCategoryHierarchically. This macro must be used at block scope, and can be used at most once in any given block (or else a compiler diagnostic will result).BALL_LOG_SET_LEGACY_DYNAMIC_CATEGORY(CATEGORY): Set, on EACH invocation, a category for logging to the specified CATEGORY using non-hierarchical category creation. This macro provides the legacy behavior where ball::Log::setCategory is called on every invocation instead of ball::Log::setCategoryHierarchically. This macro must be used at block scope and can be used at most once in any given block (or else a compiler diagnostic will result).BALL_LOG_SET_LEGACY_CLASS_CATEGORY(CATEGORY): Set a category for logging to the specified CATEGORY (assumed to be of type convertible to const char *) in the scope of the class within which this macro is used, using non-hierarchical category creation. This macro provides the legacy behavior where ball::Log::setCategory is called instead of ball::Log::setCategoryHierarchically. This macro must be used, at most once, within the definition of a class or class template (or else a compiler diagnostic will result).BALL_LOG_SET_LEGACY_NAMESPACE_CATEGORY(CATEGORY): Set a category for logging to the specified CATEGORY (assumed to be of type convertible to const char *) in the namespace (or global) scope within which this macro is used, using non-hierarchical category creation. This macro provides the legacy behavior where ball::Log::setCategory is called instead of ball::Log::setCategoryHierarchically. This macro may be used, in .cpp files only, at most once in any given namespace and at most once at global scope (or else a compiler diagnostic will result). Do NOT use this macro in .h files.The macros defined in this subsection are the ones that are actually used to produce log records. A use of any one of the logging macros requires that a logging category (as established by the macros defined above) be in scope at the point where the macro is used. Note that the formatted string that is generated for the message attribute of each log record includes the category that is in scope and the filename as established by the standard __FILE__ macro.
The code within any logging statement/code block must not produce any side effects because it may or may not be executed based on run-time configuration of the ball logging subsystem:
A set of macros based on C++ streams, BALL_LOG_TRACE, BALL_LOG_DEBUG, BALL_LOG_INFO, BALL_LOG_WARN, BALL_LOG_ERROR, and BALL_LOG_FATAL, are the ones most commonly used for logging. They have the following usage pattern:
A closely-related macro also based on C++ streams, BALL_LOG_STREAM, requires that the severity be explicitly supplied as an argument:
Another set of macros based on C++ streams, similar to BALL_LOG_TRACE, etc., allow the caller to specify a "callback" function that is passed the ball::UserFields * used to represent the user fields of a log record. BALL_LOGCB_TRACE, BALL_LOGCB_DEBUG, BALL_LOGCB_INFO, BALL_LOGCB_WARN, BALL_LOGCB_ERROR, and BALL_LOGCB_FATAL have the following usage pattern:
A closely-related macro also based on C++ streams, BALL_LOGCB_STREAM, requires that the severity be explicitly supplied as an argument:
The remaining macros are based on printf-style format specifications:
A closely-related printf-style macro, BALL_LOGVA, requires that the severity be explicitly supplied as an argument:
The following macros allow the caller to start a code block that will be conditionally executed depending on the current logging threshold of the category that is in scope of those macros:
A closely-related macro, BALL_LOG_STREAM_BLOCK, requires that the severity be explicitly supplied as an argument:
Another set of macros, similar to BALL_LOG_*_BLOCK, allow the caller to specify a "callback" function that is passed the ball::UserFields * used to represent the user fields of a log record:
A closely-related macro, BALL_LOGCB_STREAM_BLOCK, requires that the severity be explicitly supplied as an argument:
Within the logging code block a special macro, BALL_LOG_OUTPUT_STREAM, provides access to the log stream.
The following utility macro is intended for special-purpose use for fine-tuning logging behavior. A use of this macro requires that a logging category (as established by the macros defined above) be in scope at the point where the macro is used.
The following code fragments illustrate the standard pattern of macro usage.
The following trivial example shows how to use the logging macros to log messages at various levels of severity.
First, we initialize the log category within the context of this function. The logging macros such as BALL_LOG_ERROR will not compile unless a category has been specified in the current lexical scope:
Then, we record messages at various levels of severity. These messages will be conditionally written to the log depending on the current logging threshold of the category (configured using the ball::LoggerManager singleton):
Next, we demonstrate how to use proprietary code within logging macros. Suppose you want to add the content of a vector to the log trace:
Note that the code block will be conditionally executed depending on the current logging threshold of the category. The code within the block must not produce any side effects, because its execution depends on the current logging configuration. The special macro BALL_LOG_OUTPUT_STREAM provides access to the log stream within the block.
This example provides more detail on setting the log category in the current lexical scope. The following macro instantiation sets the category for logging to be "EQUITY.NASD" in the enclosing lexical scope:
Note that this macro must not be used at file scope and it can be used at most once in any given block (or else a compiler diagnostic will result). A different category may be established to override one that is in effect, but it must occur in a nested scope. In any case, a use of this macro (or of BALL_LOG_SET_DYNAMIC_CATEGORY) must be visible from within the lexical scope of every use of the log-generating macros. The following fragment of code shows how to set a different category in a nested inner block that hides a category set in an enclosing block:
Within logIt, a requisite logging category is visible at each of the locations marked by [*].
Note that BALL_LOG_SET_CATEGORY creates categories hierarchically: when a new category is created, its threshold levels are inherited from the orphaned threshold setting or existing category whose name is the longest matching prefix. In the example above, if "EQUITY.NASD" has been configured with specific threshold levels (either as an existing category or via setThresholdLevelsHierarchically before the category exists), "EQUITY.NASD.SUNW" will automatically inherit those thresholds rather than using the logger manager's default levels. This enables natural configuration of logging for related subsystems.
The preferred logging method we use, the iostream-style macros such as BALL_LOG_INFO, allow streaming via the bsl::ostream class and the C++ stream operator <<. An advantage the C++ streaming style has over the printf style output (shown below in example 4) is that complex types often have the operator<<(ostream&, const TYPE&) function overloaded so that they are able to be easily streamed to output. We demonstrate this here using C++ streaming to stream a bdlt::Date to output:
We are logging with category "EQUITY.NASD", which is configured for a pass-through level of e_INFO, from here on. We output a line using the BALL_LOG_INFO macro:
The above results in the following single-line message being output:
<ts> is the timestamp, <pid> is the process id, <tid> is the thread id, x.cpp is the expansion of the __FILE__ macro that is the name of the source file containing the call, 1161 is the line number of the call, and the trailing date following "settlement date" is the value of settle.
Next, we set the category to "EQUITY.NASD.SUNW". Because we are using BALL_LOG_SET_CATEGORY (which creates categories hierarchically), the new "EQUITY.NASD.SUNW" category automatically inherits threshold levels from the orphaned setting or existing category with the longest matching prefix ("EQUITY.NASD" in this case). Since "EQUITY.NASD" was configured with a pass-through level of e_INFO, the child category "EQUITY.NASD.SUNW" also has e_INFO pass-through, meaning e_WARN (which is more severe) also passes through:
The above results in the following message to category "EQUITY.NASD.SUNW":
Now, the category "EQUITY.NASD.SUNW" just went out of scope and category "EQUITY.NASD" is visible again, so it applies to the following:
Finally, the above results in the following single-line message being output:
The settlement date was appended to the message as a simple illustration of the added flexibility provided by the C++ stream-based macros. This last message was logged to category "EQUITY.NASD" at severity level ball::Severity::e_INFO.
The C++ stream-based macros, as opposed to the printf-style macros, ensure at compile-time that no run-time format mismatches will occur. Use of the stream-based logging style exclusively will likely lead to clearer, more maintainable code with fewer initial defects.
Note that all uses of the log-generating macros, both printf-style and C++ stream-based, must occur within function scope (i.e., not at file scope).
In the following example, we expand the logIt function (defined above) to log two messages using the BALL_LOGVA_INFO logging macro provided by this component. This variadic macro takes a format string and a variable-length series of arguments, similar to printf.
Because we can't easily printf complex types like bdlt::Date or bsl::string, we have to convert settleDate to a const char * ourselves. Note that all this additional work was unnecessary in Example 3 when we used the C++ iostream-style, rather than the printf-style, macros.
We set logging with category "EQUITY.NASD", which was configured for a pass-through severity level of e_INFO, and call BALL_LOGVA_INFO to print our trade:
The above results in the following single-line message being output to category "EQUITY.NASD.SUNW" at severity level ball::Severity::e_INFO:
In the above, <ts> is the timestamp, <pid> is the process id, <tid> is the thread id, x.cpp is the expansion of the __FILE__ macro that is the name of the source file containing the call, and 1256 is the line number of the call.
Note that the first argument supplied to the BALL_LOGVA_INFO macro is a printf-style format specification.
Next, we set the category to "EQUITY.NASD.SUNW", which is configured for a pass-through severity level of e_INFO:
The above results in the following single-line message to category "EQUITY.NASD.SUNW":
Now, the category "EQUITY.NASD.SUNW" just went out of scope and category "EQUITY.NASD" is visible again, so it applies to the following:
Finally, the above results in the following single-line message being output:
Logging must sometimes be controlled by parameters that are not available until run-time. The BALL_LOG_SET_DYNAMIC_CATEGORY macro sets a category each time it is invoked (unlike BALL_LOG_SET_CATEGORY, which sets a category only on the first invocation and uses the cached address of the category on subsequent invocations). The category name in the following processSecurity function is a combination of a static prefix and the (dynamic) exchange argument:
Now logging can be controlled independently for each exchange.
WARNING: Along with the added flexibility provided by dynamic categories comes the additional overhead of computing and setting a category on each invocation. Consequently, dynamic categories should be used SPARINGLY in most applications.
The following example demonstrates the use of attributes and rules to conditionally enable logging.
We start by defining a function, processData, that is passed data in a vector<char> and information about the user who sent the data. This example function performs no actual processing, but does log a single message at the ball::Severity::e_DEBUG threshold level. The processData function also adds the user information passed to this function to the thread's attribute context. We will use these attributes later, to create a logging rule that enables verbose logging only for a particular user.
We add our attributes using ball::ScopedAttribute, which adds an attribute container with one attribute to a list of containers. This is easy and efficient if the number of attributes is small, but should not be used if there are a large number of attributes. If motivated, we could use ball::DefaultAttributeContainer, which provides an efficient container for a large number of attributes, or even create a more efficient attribute container implementation specifically for these three attributes (uuid, luw, and terminalNumber). See ball_scopedattributes (plural) for an example of using a different attribute container, and ball_attributecontainer for an example of creating a custom attribute container.
In this simplified example we perform no actual processing, and simply log a message at the ball::Severity::e_DEBUG level.
Notice that if we were not using a "scoped" attribute container like that provided automatically by ball::ScopedAttribute (e.g., if we were using a local ball::DefaultAttributeContainer instead), then the container must be removed from the ball::AttributeContext before it is destroyed! See ball_scopedattributes (plural) for an example.
Next we demonstrate how to create a logging rule that sets the pass-through logging threshold to ball::Severity::e_TRACE (i.e., enables verbose logging) for a particular user when calling the processData function defined above.
We start by creating the singleton logger manager that we configure with the stream observer and a default configuration. We then call the processData function: This first call to processData will not result in any logged messages because processData logs its message at the ball::Severity::e_DEBUG level, which is below the default configured logging threshold.
Now we add a logging rule, setting the pass-through threshold to be ball::Severity::e_TRACE (i.e., enabling verbose logging) if the thread's context contains an attribute with name "mylibrary.uuid" with value 3938908. Note that we use the wild-card value * for the category so that the ball::Rule rule will apply to all categories. Also note that the computation of effective threshold levels for every category matching the pattern has considerable performance implications.
The final call to the processData function below, passes a uuid of 2171395 (not 3938908) so the logging rule we defined will not apply and no message will be logged.
The resulting logged output for this example looks like the following:
The following example demonstrates how to register a logging callback. The C++ stream-based macros that take a callback are particularly useful to seamlessly populate the user fields of a record, thus simplifying the logging line.
We define a callback function populateUsingPoint that appends to the specified fields the attributes of the point to log:
We now bind our callback function populateUsingPoint and the supplied point to a functor object we will pass to the logging callback. Note that the callback supplied to the logging macro must match the prototype void (*)(ball::UserFields *).
The following example demonstrates how to define and use logging categories that have class scope.
First, we define a class, Thing, for which we want to do class-scope logging. The use of the BALL_LOG_SET_CLASS_CATEGORY macro generates the requisite declarations within the definition of the class. We have used the macro in a private section of the interface, which should be preferred, but public (or protected) is fine, too:
Next, we define the inlineMethodThatLogs method inline within the header file and log to the class-scope category using BALL_LOG_TRACE. Since there is no other category in scope, the record is necessarily logged to the "PCKG.THING" category that is within the scope of the Thing class:
Now, we define the outOfLineMethodThatLogs method within the .cpp file. On each invocation, this method logs one record using BALL_LOG_TRACE. It logs to the "PCKG.THING" class-scope category if useClassCategory is true, and logs to the "X.Y.Z" block-scope category otherwise:
Finally, note that both block-scope and class-scope categories can be logged to within the same block. For example, the following block within a Thing method would first log to "PCKG.THING" then log to "X.Y.Z":
| #define BALL_LOG_CATEGORY (ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER)->category()) |
| #define BALL_LOG_DEBUG |
| #define BALL_LOG_DEBUG_BLOCK BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG) |
| #define BALL_LOG_END "" |
| #define BALL_LOG_ERROR |
| #define BALL_LOG_ERROR_BLOCK BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_ERROR) |
| #define BALL_LOG_FATAL |
| #define BALL_LOG_FATAL_BLOCK BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_FATAL) |
| #define BALL_LOG_INFO |
| #define BALL_LOG_INFO_BLOCK BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_INFO) |
| #define BALL_LOG_IS_ENABLED | ( | SEVERITY | ) |
| #define BALL_LOG_OUTPUT_STREAM (ball_log_lOg_StReAm.stream()) |
| #define BALL_LOG_RECORD (ball_log_lOg_StReAm.record()) |
| #define BALL_LOG_SET_CATEGORY | ( | CATEGORY | ) |
| #define BALL_LOG_SET_CATEGORY_HIERARCHICALLY | ( | CATEGORY | ) |
| #define BALL_LOG_SET_CLASS_CATEGORY | ( | CATEGORY | ) |
| #define BALL_LOG_SET_CLASS_CATEGORY_HIERARCHICALLY | ( | CATEGORY | ) |
| #define BALL_LOG_SET_DYNAMIC_CATEGORY | ( | CATEGORY | ) |
| #define BALL_LOG_SET_DYNAMIC_CATEGORY_HIERARCHICALLY | ( | CATEGORY | ) |
| #define BALL_LOG_SET_LEGACY_CATEGORY | ( | CATEGORY | ) |
| #define BALL_LOG_SET_LEGACY_CLASS_CATEGORY | ( | CATEGORY | ) |
| #define BALL_LOG_SET_LEGACY_DYNAMIC_CATEGORY | ( | CATEGORY | ) |
| #define BALL_LOG_SET_LEGACY_NAMESPACE_CATEGORY | ( | CATEGORY | ) |
| #define BALL_LOG_SET_NAMESPACE_CATEGORY | ( | CATEGORY | ) |
| #define BALL_LOG_SET_NAMESPACE_CATEGORY_HIERARCHICALLY | ( | CATEGORY | ) |
| #define BALL_LOG_STREAM | ( | SEVERITY | ) | BALL_LOG_STREAM_IMP((SEVERITY)) BALL_LOG_OUTPUT_STREAM |
| #define BALL_LOG_STREAM_BLOCK | ( | SEVERITY | ) | BALL_LOG_STREAM_IMP((SEVERITY)) |
| #define BALL_LOG_STREAM_CONST_IMP | ( | SEVERITY | ) |
| #define BALL_LOG_STREAM_IMP | ( | SEVERITY | ) |
| #define BALL_LOG_THRESHOLD (ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER)->threshold()) |
| #define BALL_LOG_TRACE |
| #define BALL_LOG_TRACE_BLOCK BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_TRACE) |
| #define BALL_LOG_WARN |
| #define BALL_LOG_WARN_BLOCK BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_WARN) |
| #define BALL_LOGCB_DEBUG | ( | CALLBACK | ) |
| #define BALL_LOGCB_DEBUG_BLOCK | ( | CALLBACK | ) |
| #define BALL_LOGCB_END "" |
| #define BALL_LOGCB_ERROR | ( | CALLBACK | ) |
| #define BALL_LOGCB_ERROR_BLOCK | ( | CALLBACK | ) |
| #define BALL_LOGCB_FATAL | ( | CALLBACK | ) |
| #define BALL_LOGCB_FATAL_BLOCK | ( | CALLBACK | ) |
| #define BALL_LOGCB_INFO | ( | CALLBACK | ) |
| #define BALL_LOGCB_INFO_BLOCK | ( | CALLBACK | ) |
| #define BALL_LOGCB_STREAM | ( | BALL_SEVERITY, | |
| CALLBACK | |||
| ) | BALL_LOGCB_STREAM_IMP((BALL_SEVERITY), (CALLBACK)) BALL_LOG_OUTPUT_STREAM |
| #define BALL_LOGCB_STREAM_BLOCK | ( | BALL_SEVERITY, | |
| CALLBACK | |||
| ) | BALL_LOGCB_STREAM_IMP((BALL_SEVERITY), (CALLBACK)) |
| #define BALL_LOGCB_STREAM_CONST_IMP | ( | SEVERITY, | |
| CALLBACK | |||
| ) |
| #define BALL_LOGCB_STREAM_IMP | ( | SEVERITY, | |
| CALLBACK | |||
| ) |
| #define BALL_LOGCB_TRACE | ( | CALLBACK | ) |
| #define BALL_LOGCB_TRACE_BLOCK | ( | CALLBACK | ) |
| #define BALL_LOGCB_WARN | ( | CALLBACK | ) |
| #define BALL_LOGCB_WARN_BLOCK | ( | CALLBACK | ) |
| #define BALL_LOGVA | ( | SEVERITY, | |
| ... | |||
| ) |
| #define BALL_LOGVA_CONST_IMP | ( | SEVERITY, | |
| ... | |||
| ) |
| #define BALL_LOGVA_DEBUG | ( | ... | ) | BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG, __VA_ARGS__) |
| #define BALL_LOGVA_ERROR | ( | ... | ) | BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_ERROR, __VA_ARGS__) |
| #define BALL_LOGVA_FATAL | ( | ... | ) | BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_FATAL, __VA_ARGS__) |
| #define BALL_LOGVA_INFO | ( | ... | ) | BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_INFO, __VA_ARGS__) |
| #define BALL_LOGVA_TRACE | ( | ... | ) | BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_TRACE, __VA_ARGS__) |
| #define BALL_LOGVA_WARN | ( | ... | ) | BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_WARN, __VA_ARGS__) |