// ball_log.h -*-C++-*- // ---------------------------------------------------------------------------- // NOTICE // // This component is not up to date with current BDE coding standards, and // should not be used as an example for new development. // ---------------------------------------------------------------------------- #ifndef INCLUDED_BALL_LOG #define INCLUDED_BALL_LOG #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide macros and utility functions to facilitate logging. // //@CLASSES: // ball::Log: namespace for logging utilities (for *internal* use only) // //@MACROS: // BALL_LOG_SET_CATEGORY(C): set a category for logging to the specified 'C' // BALL_LOG_SET_DYNAMIC_CATEGORY(C): set a run-time-dependent category // BALL_LOG_SET_CLASS_CATEGORY(C): set a category in the scope of a class // BALL_LOG_SET_NAMESPACE_CATEGORY(C): set a category in a namespace // BALL_LOG_SET_CATEGORY_HIERARCHICALLY(C): set a category hierarchically // BALL_LOG_SET_DYNAMIC_CATEGORY_HIERARCHICALLY(C): set a run-time category // BALL_LOG_SET_CLASS_CATEGORY_HIERARCHICALLY(C): set a class category // BALL_LOG_SET_NAMESPACE_CATEGORY_HIERARCHICALLY(C): set a namespace category // BALL_LOG_TRACE: produce a log record with the 'e_TRACE' severity level // BALL_LOG_DEBUG: produce a log record with the 'e_DEBUG' severity level // BALL_LOG_INFO: produce a log record with the 'e_INFO' severity level // BALL_LOG_WARN: produce a log record with the 'e_WARN' severity level // BALL_LOG_ERROR: produce a log record with the 'e_ERROR' severity level // BALL_LOG_FATAL: produce a log record with the 'e_FATAL' severity level // BALL_LOG_STREAM(SEV): produce a log record with the specified 'SEV' level // BALL_LOGCB_TRACE(CB): produce a 'e_TRACE' log record using callback 'CB' // BALL_LOGCB_DEBUG(CB): produce a 'e_DEBUG' log record using callback 'CB' // BALL_LOGCB_INFO(CB): produce an 'e_INFO' log record using callback 'CB' // BALL_LOGCB_WARN(CB): produce a 'e_WARN' log record using callback 'CB' // BALL_LOGCB_ERROR(CB): produce an 'e_ERROR' log record using callback 'CB' // BALL_LOGCB_FATAL(CB): produce a 'e_FATAL' log record using callback 'CB' // BALL_LOGCB_STREAM(SEV, CB): produce a 'SEV' log record using callback // BALL_LOGVA_TRACE(MSG, ...): produce 'e_TRACE' record using 'printf' format // BALL_LOGVA_DEBUG(MSG, ...): produce 'e_DEBUG' record using 'printf' format // BALL_LOGVA_INFO( MSG, ...): produce 'e_INFO' record using 'printf' format // BALL_LOGVA_WARN( MSG, ...): produce 'e_WARN' record using 'printf' format // BALL_LOGVA_ERROR(MSG, ...): produce 'e_ERROR' record using 'printf' format // BALL_LOGVA_FATAL(MSG, ...): produce 'e_FATAL' record using 'printf' format // BALL_LOGVA(SEV, MSG, ...): produce a 'SEV' log record using 'printf' format // BALL_LOG_TRACE_BLOCK: set code block with 'e_TRACE' condition of execution // BALL_LOG_DEBUG_BLOCK: set code block with 'e_DEBUG' condition of execution // BALL_LOG_INFO_BLOCK: set a code block with 'e_INFO' condition of execution // BALL_LOG_WARN_BLOCK: set a code block with 'e_WARN' condition of execution // BALL_LOG_ERROR_BLOCK: set code block with 'e_ERROR' condition of execution // BALL_LOG_FATAL_BLOCK: set code block with 'e_FATAL' condition of execution // BALL_LOG_STREAM_BLOCK(SEV): set a code block with 'SEV' condition // BALL_LOGCB_TRACE_BLOCK(CB): set 'e_TRACE' block with the specified callback // BALL_LOGCB_DEBUG_BLOCK(CB): set 'e_DEBUG' block with the specified callback // BALL_LOGCB_INFO_BLOCK(CB): set 'e_INFO' block with the specified callback // BALL_LOGCB_WARN_BLOCK(CB): set 'e_WARN' block with the specified callback // BALL_LOGCB_ERROR_BLOCK(CB): set an 'e_ERROR' block with the specified 'CB' // BALL_LOGCB_FATAL_BLOCK(CB): set 'e_FATAL' block with the specified callback // BALL_LOGCB_STREAM_BLOCK(SEV, CB): set a 'SEV' block with the specified 'CB' // BALL_LOG_IS_ENABLED(SEV): indicate if 'SEV' is severe enough for logging // //@SEE_ALSO: ball_loggermanager, ball_category, ball_severity, ball_record // //@DESCRIPTION: This 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. // ///Thread Safety ///------------- // All macros defined in this component are thread-safe, and can be invoked // concurrently by multiple threads. // ///Macro Reference ///--------------- // 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). // ///Macros for Defining Categories at Block Scope ///- - - - - - - - - - - - - - - - - - - - - - - // The following two 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 *'). 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). //: //: '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 *'). 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). // // There can be at most one use of either 'BALL_LOG_SET_CATEGORY' or // 'BALL_LOG_SET_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. // ///Macro for Defining Categories at Class Scope /// - - - - - - - - - - - - - - - - - - - - - - // The following macro is 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. 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. // // Note that similar to block-scope categories (see 'BALL_LOG_SET_CATEGORY' and // 'BALL_LOG_SET_DYNAMIC_CATEGORY'), class-scope categories are not destroyed // until the logger manager singleton is destroyed. // ///Macro for Defining Categories at Namespace or Global Scope /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // The following macro is 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. 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 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. // ///Macros for Defining Hierarchical Categories ///- - - - - - - - - - - - - - - - - - - - - - // The following macros are used to establish logging categories having // thresholds that are given by the existing non-default category whose name // matches the longest prefix of the new category's name (i.e., the threshold // levels of new categories are determined "hierarchically" from existing // categories rather than from the default threshold levels of the logger // manager singleton). // //: 'BALL_LOG_SET_CATEGORY_HIERARCHICALLY(CATEGORY)' //: Set a category for logging to the specified 'CATEGORY' (assumed to be //: of type convertible to 'const char *'). 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). //: //: 'BALL_LOG_SET_DYNAMIC_CATEGORY_HIERARCHICALLY(CATEGORY)': //: Set, *on* *EACH* *invocation*, a category for logging to the specified //: 'CATEGORY' (assumed to be of type convertible to 'const char *'). 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). //: //: 'BALL_LOG_SET_CLASS_CATEGORY_HIERARCHICALLY(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. Similar to //: 'BALL_LOG_SET_CATEGORY_HIERARCHICALLY', the category is set *once* //: only, using the 'ball::Log::setCategoryHierarchically' function, the //: first time that it is accessed (i.e., it is not a dynamic category). //: (See the function-level documentation for //: 'ball::Log::setCategoryHierarchically' for more information.) 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. //: //: 'BALL_LOG_SET_NAMESPACE_CATEGORY_HIERARCHICALLY(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. Similar to //: 'BALL_LOG_SET_CATEGORY_HIERARCHICALLY', the category is set *once* //: only, using the 'ball::Log::setCategoryHierarchically' function, the //: first time that it is accessed (i.e., it is not a dynamic category). //: (See the function-level documentation for //: 'ball::Log::setCategoryHierarchically' for more information.) 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. // ///Macros for Logging Records /// - - - - - - - - - - - - - // 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: //.. // BALL_LOG_INFO << ++i; // (!) May or may not be incremented // // BALL_LOG_TRACE_BLOCK { // processRequest(...); // (!) May or may not be called // } //.. // // 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: //.. // BALL_LOG_TRACE << X << Y ... ; // BALL_LOG_DEBUG << X << Y ... ; // BALL_LOG_INFO << X << Y ... ; // BALL_LOG_WARN << X << Y ... ; // BALL_LOG_ERROR << X << Y ... ; // BALL_LOG_FATAL << X << Y ... ; // where X, Y, ... represents any sequence of values for which // 'operator<<' is defined. The resulting formatted message string is // logged with the severity indicated by the name of the macro // (e.g., 'BALL_LOG_TRACE' logs with severity 'ball::Severity::e_TRACE'). //.. // A closely-related macro also based on C++ streams, 'BALL_LOG_STREAM', // requires that the severity be explicitly supplied as an argument: //.. // BALL_LOG_STREAM(SEVERITY) << X << Y ... ; // where X, Y, ... represents any sequence of values for which // 'operator<<' is defined. The resulting formatted message string is // logged with the specified 'SEVERITY'. //.. // 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: //.. // BALL_LOGCB_TRACE(CALLBACK) << X << Y ... ; // BALL_LOGCB_DEBUG(CALLBACK) << X << Y ... ; // BALL_LOGCB_INFO(CALLBACK) << X << Y ... ; // BALL_LOGCB_WARN(CALLBACK) << X << Y ... ; // BALL_LOGCB_ERROR(CALLBACK) << X << Y ... ; // BALL_LOGCB_FATAL(CALLBACK) << X << Y ... ; // where X, Y, ... represents any sequence of values for which // 'operator<<' is defined and 'CALLBACK' is a callback taking a // 'ball::UserFields *' as an argument. The resulting formatted message // string is logged with the severity indicated by the name of the macro // (e.g., 'BALL_LOGCB_ERROR' logs with severity // 'ball::Severity::e_ERROR'). The generated log record will contain the // 'ball::UserFields' representing user fields as populated by 'CALLBACK'. // Note that the callback supplied to the logging macro must match the // prototype 'void (*)(ball::UserFields *)'. //.. // A closely-related macro also based on C++ streams, 'BALL_LOGCB_STREAM', // requires that the severity be explicitly supplied as an argument: //.. // BALL_LOGCB_STREAM(SEVERITY, CALLBACK) << X << Y ... ; // where X, Y, ... represents any sequence of values for which // 'operator<<' is defined. The resulting formatted message string is // logged with the specified 'SEVERITY'. The generated log record will // contain the 'ball::UserFields' representing user fields as populated by // 'CALLBACK'. Note that the callback supplied to the logging macro must // match the prototype 'void (*)(ball::UserFields *)'. //.. // The remaining macros are based on 'printf'-style format specifications: //.. // BALL_LOGVA_TRACE(MSG, ...); // BALL_LOGVA_DEBUG(MSG, ...); // BALL_LOGVA_INFO( MSG, ...); // BALL_LOGVA_WARN( MSG, ...); // BALL_LOGVA_ERROR(MSG, ...); // BALL_LOGVA_FATAL(MSG, ...); // Format the specified '...' optional arguments, if any, according to the // 'printf'-style format specification in the specified 'MSG' (assumed to // be of type convertible to 'const char *') and log the resulting // formatted message string with the severity indicated by the name of the // macro (e.g., 'BALL_LOGVA_INFO' logs with severity // 'ball::Severity::e_INFO'). The behavior is undefined unless the number // and types of optional arguments are compatible with the format // specification in 'MSG'. Note that each use of these macros must be // terminated by a ';'. //.. // A closely-related 'printf'-style macro, 'BALL_LOGVA', requires that the // severity be explicitly supplied as an argument: //.. // BALL_LOGVA(SEVERITY, MSG, ...); // Format the specified '...' optional arguments, if any, according to the // 'printf'-style format specification in the specified 'MSG' (assumed to // be of type convertible to 'const char *') and log the resulting // formatted message string with the specified 'SEVERITY'. The behavior // is undefined unless the number and types of optional arguments are // compatible with the format specification in 'MSG'. Note that each use // of this macro must be terminated by a ';'. //.. // ///Macros for Logging Code Blocks /// - - - - - - - - - - - - - - - // 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: //.. // BALL_LOG_TRACE_BLOCK { ... } // BALL_LOG_DEBUG_BLOCK { ... } // BALL_LOG_INFO_BLOCK { ... } // BALL_LOG_WARN_BLOCK { ... } // BALL_LOG_ERROR_BLOCK { ... } // BALL_LOG_FATAL_BLOCK { ... } //.. // A closely-related macro, 'BALL_LOG_STREAM_BLOCK', requires that the severity // be explicitly supplied as an argument: //.. // BALL_LOG_STREAM_BLOCK(SEVERITY) { ... } //.. // 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: //.. // BALL_LOGCB_TRACE_BLOCK(CALLBACK) { ... } // BALL_LOGCB_DEBUG_BLOCK(CALLBACK) { ... } // BALL_LOGCB_INFO_BLOCK(CALLBACK) { ... } // BALL_LOGCB_WARN_BLOCK(CALLBACK) { ... } // BALL_LOGCB_ERROR_BLOCK(CALLBACK) { ... } // BALL_LOGCB_FATAL_BLOCK(CALLBACK) { ... } //.. // A closely-related macro, 'BALL_LOGCB_STREAM_BLOCK', requires that the // severity be explicitly supplied as an argument: //.. // BALL_LOGCB_STREAM_BLOCK(SEVERITY, CALLBACK) { ... } //.. // Within the logging code block a special macro, 'BALL_LOG_OUTPUT_STREAM', // provides access to the log stream. // ///Utility Macros /// - - - - - - - // 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. //.. // BALL_LOG_IS_ENABLED(SEVERITY) // Return 'true' if the specified 'SEVERITY' is at least as severe as any // of the threshold levels of the logging category that is in scope, and // 'false' otherwise. //.. // ///Usage ///----- // The following code fragments illustrate the standard pattern of macro usage. // ///Example 1: A Basic Logging Example /// - - - - - - - - - - - - - - - - - // 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: //.. // BALL_LOG_SET_CATEGORY("EXAMPLE.CATEGORY"); //.. // 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): //.. // BALL_LOG_FATAL << "Write this message to the log if the log threshold " // << "is above 'ball::Severity::e_FATAL' (i.e., 32)."; // // BALL_LOG_TRACE << "Write this message to the log if the log threshold " // << "is above 'ball::Severity::e_TRACE' (i.e., 192)."; //.. // 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: //.. // bsl::vector<int> myVector(4, 328); // BALL_LOG_TRACE_BLOCK { // BALL_LOG_OUTPUT_STREAM << "myVector = [ "; // unsigned int position = 0; // for (bsl::vector<int>::const_iterator it = myVector.begin(), // end = myVector.end(); // it != end; // ++it, ++position) { // BALL_LOG_OUTPUT_STREAM << position << ':' << *it << ' '; // } // BALL_LOG_OUTPUT_STREAM << ']'; // } //.. // 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. // ///Example 2: Setting the Current Log Category ///- - - - - - - - - - - - - - - - - - - - - - // 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: //.. // BALL_LOG_SET_CATEGORY("EQUITY.NASD") //.. // 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: //.. // void logIt() // { // BALL_LOG_SET_CATEGORY("EQUITY.NASD") // // // Logging to category "EQUITY.NASD" unless overridden in a nested // // block. // // [*] ... // // { // // [*] ... // // Still logging to category "EQUITY.NASD". // // BALL_LOG_SET_CATEGORY("EQUITY.NASD.SUNW") // // // Now logging to category "EQUITY.NASD.SUNW". // // [*] ... // } // // Again logging to category "EQUITY.NASD". // // [*] ... // } //.. // Within 'logIt', a requisite logging category is visible at each of the // locations marked by '[*]'. // ///Example 3: C++ I/O Streams-Style Logging Macros ///- - - - - - - - - - - - - - - - - - - - - - - - // 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: //.. // int lotSize = 400; // const char *ticker = "SUNW"; // double price = 5.65; // // // Trading on a market that settles 3 days in the future. // // bdlt::Date settle = bdlt::CurrentTime::local().date() + 3; // // BALL_LOG_SET_CATEGORY("EQUITY.NASD") //.. // 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: //.. // BALL_LOG_INFO << "[1] " << lotSize // << " shares of " << ticker // << " sold at " << price // << " settlement date " << settle; //.. // The above results in the following single-line message being output: //.. // <ts> <pid> <tid> INFO x.cpp 1161 EQUITY.NASD [1] 400 shares of SUNW sold // at 5.65 settlement date 17FEB2017 //.. // '<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", which has been defined with // 'ball::Administration::addCategory' with its pass-through level set to // 'e_INFO' and the trigger levels set at or above 'e_ERROR', so a level of // 'e_WARN' also passes through: //.. // { // BALL_LOG_SET_CATEGORY("EQUITY.NASD.SUNW") // // // Now logging with category "EQUITY.NASD.SUNW". // // BALL_LOG_WARN << "[2] " << lotSize // << " shares of " << ticker // << " sold at " << price // << " settlement date " << settle; // } //.. // The above results in the following message to category "EQUITY.NASD.SUNW": //.. // <ts> <pid> <tid> WARN x.cpp 1185 EQUITY.NASD.SUNW [2] 400 shares of SUNW // sold at 5.65 settlement date 17FEB2017 //.. // Now, the category "EQUITY.NASD.SUNW" just went out of scope and category // "EQUITY.NASD" is visible again, so it applies to the following: //.. // BALL_LOG_INFO << "[3] " << lotSize // << " shares of " << ticker // << " sold at " << price // << " settlement date " << settle; //.. // Finally, the above results in the following single-line message being // output: //.. // <ts> <pid> <tid> INFO x.cpp 1198 EQUITY.NASD [3] 400 shares of SUNW sold // at 5.65 settlement date 17FEB2017 //.. // 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). // ///Example 4: 'printf'-Style Output /// - - - - - - - - - - - - - - - - // 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'. //.. // int lotSize = 400; // const char *ticker = "SUNW"; // double price = 5.65; // // // Trading on a market that settles 3 days in the future. // // bdlt::Date settleDate = bdlt::CurrentTime::local().date() + 3; //.. // 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. //.. // bsl::ostringstream settleOss; // settleOss << settleDate; // const bsl::string& settleStr = settleOss.str(); // const char *settle = settleStr.c_str(); //.. // 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: //.. // BALL_LOG_SET_CATEGORY("EQUITY.NASD") // // BALL_LOGVA_INFO("[4] %d shares of %s sold at %f settlement date %s\n", // lotSize, ticker, price, settle); //.. // The above results in the following single-line message being output to // category "EQUITY.NASD.SUNW" at severity level 'ball::Severity::e_INFO': //.. // <ts> <pid> <tid> INFO x.cpp 1256 EQUITY.NASD [4] 400 shares of SUNW sold // at 5.650000 settlement date 17FEB2017 //.. // 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': //.. // { // BALL_LOG_SET_CATEGORY("EQUITY.NASD.SUNW") // // // Now logging with category "EQUITY.NASD.SUNW". // // BALL_LOGVA_WARN("[5] %d shares of %s sold at %f settlement date %s\n", // lotSize, ticker, price, settle); // } //.. // The above results in the following single-line message to category // "EQUITY.NASD.SUNW": //.. // <ts> <pid> <tid> WARN x.cpp 1281 EQUITY.NASD.SUNW [5] 400 shares of SUNW // sold at 5.650000 settlement date 17FEB2017 //.. // Now, the category "EQUITY.NASD.SUNW" just went out of scope and category // "EQUITY.NASD" is visible again, so it applies to the following: //.. // BALL_LOGVA_INFO("[6] %d shares of %s sold at %f settlement date %s\n", // lotSize, ticker, price, settle); //.. // Finally, the above results in the following single-line message being // output: //.. // <ts> <pid> <tid> INFO x.cpp 1294 EQUITY.NASD [6] 400 shares of SUNW sold // at 5.650000 settlement date 17FEB2017 //.. // ///Example 5: Dynamic Categories ///- - - - - - - - - - - - - - - // 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: //.. // void processSecurity(const char *security, const char *exchange) // { // bsl::string categoryName("EXCHANGE:"); // categoryName.append(exchange); // // BALL_LOG_SET_DYNAMIC_CATEGORY(categoryName.c_str()); // // BALL_LOG_TRACE << "processing: " << security; // // // ... // } //.. // 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. // ///Example 6: Rule-Based Logging ///- - - - - - - - - - - - - - - // 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. //.. // void processData(int uuid, // int luw, // int terminalNumber, // const bsl::vector<char>& data) // // Process the specified 'data' associated with the specified Bloomberg // // 'uuid', 'luw', and 'terminalNumber'. // { // (void)data; // suppress "unused" warning //.. // 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. //.. // // We use 'ball::ScopedAttribute' here because the number of // // attributes is relatively small. // // ball::ScopedAttribute uuidAttribute("mylibrary.uuid", uuid); // ball::ScopedAttribute luwAttribute("mylibrary.luw", luw); // ball::ScopedAttribute termNumAttribute("mylibrary.terminalNumber", // terminalNumber); //.. // In this simplified example we perform no actual processing, and simply log // a message at the 'ball::Severity::e_DEBUG' level. //.. // BALL_LOG_SET_CATEGORY("EXAMPLE.CATEGORY"); // // BALL_LOG_DEBUG << "An example message"; //.. // 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. //.. // ball::LoggerManagerConfiguration lmConfig; // ball::LoggerManagerScopedGuard lmGuard(lmConfig); // // bsl::shared_ptr<ball::StreamObserver> observer = // bsl::make_shared<ball::StreamObserver>(&bsl::cout); // // ball::LoggerManager::singleton().registerObserver(observer, "default"); // // BALL_LOG_SET_CATEGORY("EXAMPLE.CATEGORY"); // // bsl::vector<char> message; // // BALL_LOG_ERROR << "Processing the first message."; // processData(3938908, 2, 9001, message); //.. // 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. //.. // ball::Rule rule("*", 0, ball::Severity::e_TRACE, 0, 0); // rule.addAttribute(ball::ManagedAttribute("mylibrary.uuid", 3938908)); // ball::LoggerManager::singleton().addRule(rule); // // BALL_LOG_ERROR << "Processing the second message."; // processData(3938908, 2, 9001, message); //.. // 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. //.. // BALL_LOG_ERROR << "Processing the third message."; // processData(2171395, 2, 9001, message); //.. // The resulting logged output for this example looks like the following: //.. // ERROR example.cpp:105 EXAMPLE.CATEGORY Processing the first message. // ERROR example.cpp:117 EXAMPLE.CATEGORY Processing the second message. // DEBUG example.cpp:35 EXAMPLE.CATEGORY An example message // ERROR example.cpp:129 EXAMPLE.CATEGORY Processing the third message. //.. // ///Example 7: Logging Using a Callback ///- - - - - - - - - - - - - - - - - - // 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: //.. // void populateUsingPoint(ball::UserFields *fields, const Point& point) // // Append to the specified 'list' the name, x value, and y value of // // the specified 'point'. // { // fields->appendString(point.name()); // fields->appendInt64(point.x()); // fields->appendInt64(point.y()); // } // // int validatePoint(const Point& point) // { // BALL_LOG_SET_CATEGORY("EXAMPLE.CATEGORY"); //.. // 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 *)'. //.. // bsl::function <void(ball::UserFields *)> callback; // callback = bdlf::BindUtil::bind(&populateUsingPoint, // bdlf::PlaceHolders::_1, // point); // // int numErrors = 0; // if (point.x() > 255) { // BALL_LOGCB_ERROR(callback) << "X > 255"; // ++numErrors; // } // if (point.x() < -255) { // BALL_LOGCB_ERROR(callback) << "X < -255"; // ++numErrors; // } // if (point.y() > 255) { // BALL_LOGCB_ERROR(callback) << "Y > 255"; // ++numErrors; // } // if (point.y() < -255) { // BALL_LOGCB_ERROR(callback) << "Y < -255"; // ++numErrors; // } // return numErrors; // } //.. // ///Example 8: Class-Scope Logging /// - - - - - - - - - - - - - - - // 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: //.. // // pckg_thing.h // namespace pckg { // // class Thing { // // ... // // private: // BALL_LOG_SET_CLASS_CATEGORY("PCKG.THING"); // // public: // // ... // // // MANIPULATORS // void outOfLineMethodThatLogs(bool useClassCategory); // // Log to the class-scope category "PCKG.THING" if the specified // // 'useClassCategory' flag is 'true', and to the block-scope // // category "X.Y.Z" otherwise. // // // ... // // // ACCESSORS // void inlineMethodThatLogs() const; // // Log a record to the class-scope category "PCKG.THING". // }; //.. // 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: //.. // // ... // // // ACCESSORS // inline // void Thing::inlineMethodThatLogs() const // { // BALL_LOG_TRACE << "log to PCKG.THING"; // } // // } // close namespace pckg //.. // 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: //.. // // pckg_thing.cpp // namespace pckg { // // // ... // // // MANIPULATORS // void Thing::outOfLineMethodThatLogs(bool useClassCategory) // { // if (useClassCategory) { // BALL_LOG_TRACE << "log to PCKG.THING"; // } // else { // BALL_LOG_SET_CATEGORY("X.Y.Z"); // BALL_LOG_TRACE << "log to X.Y.Z"; // } // } // // } // close namespace pckg //.. // 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": //.. // { // BALL_LOG_TRACE << "log to PCKG.THING"; // // BALL_LOG_SET_CATEGORY("X.Y.Z"); // // BALL_LOG_TRACE << "log to X.Y.Z"; // } //.. #include <balscm_version.h> #include <ball_category.h> #include <ball_categorymanager.h> #include <ball_loggermanager.h> #include <ball_severity.h> #include <bslma_managedptr.h> #include <bsls_annotation.h> #include <bsls_keyword.h> #include <bsls_performancehint.h> #include <bsls_platform.h> #include <bsl_ostream.h> #include <bslmt_mutex.h> // ========================= // Logging Macro Definitions // ========================= #define BALL_LOG_CATEGORY \ (ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER)->category()) #define BALL_LOG_THRESHOLD \ (ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER)->threshold()) #define BALL_LOG_RECORD (ball_log_lOg_StReAm.record()) #define BALL_LOG_OUTPUT_STREAM (ball_log_lOg_StReAm.stream()) // =========================== // Block-Scope Category Macros // =========================== #define BALL_LOG_SET_CATEGORY(CATEGORY) \ static BloombergLP::ball::CategoryHolder BALL_LOG_CATEGORYHOLDER = { \ { BloombergLP::ball::CategoryHolder::e_UNINITIALIZED_CATEGORY }, \ { 0 }, { 0 } \ }; \ if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!BALL_LOG_CATEGORY)) { \ BSLS_PERFORMANCEHINT_UNLIKELY_HINT; \ BloombergLP::ball::Log::setCategory(&BALL_LOG_CATEGORYHOLDER, \ CATEGORY); \ } #define BALL_LOG_SET_CATEGORY_HIERARCHICALLY(CATEGORY) \ static BloombergLP::ball::CategoryHolder BALL_LOG_CATEGORYHOLDER = { \ { BloombergLP::ball::CategoryHolder::e_UNINITIALIZED_CATEGORY }, \ { 0 }, { 0 } \ }; \ if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!BALL_LOG_CATEGORY)) { \ BSLS_PERFORMANCEHINT_UNLIKELY_HINT; \ BloombergLP::ball::Log::setCategoryHierarchically( \ &BALL_LOG_CATEGORYHOLDER, \ CATEGORY); \ } #define BALL_LOG_SET_DYNAMIC_CATEGORY(CATEGORY) \ const BloombergLP::ball::Category *BALL_LOG_DYNAMIC_CATEGORY = \ BloombergLP::ball::Log::setCategory(CATEGORY); \ BloombergLP::ball::CategoryHolder BALL_LOG_CATEGORYHOLDER = { \ { BloombergLP::ball::CategoryHolder::e_DYNAMIC_CATEGORY }, \ { const_cast<BloombergLP::ball::Category *>( \ BALL_LOG_DYNAMIC_CATEGORY) }, \ { 0 } \ }; #define BALL_LOG_SET_DYNAMIC_CATEGORY_HIERARCHICALLY(CATEGORY) \ const BloombergLP::ball::Category *BALL_LOG_DYNAMIC_CATEGORY = \ BloombergLP::ball::Log::setCategoryHierarchically(CATEGORY); \ BloombergLP::ball::CategoryHolder BALL_LOG_CATEGORYHOLDER = { \ { BloombergLP::ball::CategoryHolder::e_DYNAMIC_CATEGORY }, \ { const_cast<BloombergLP::ball::Category *>( \ BALL_LOG_DYNAMIC_CATEGORY) }, \ { 0 } \ }; // =========================== // Class-Scope Category Macros // =========================== #define BALL_LOG_SET_CLASS_CATEGORY(CATEGORY) \ static \ const BloombergLP::ball::CategoryHolder *ball_log_getCategoryHolder( \ const BloombergLP::ball::CategoryHolder& categoryHolder) \ { \ return &categoryHolder; \ } \ static \ const BloombergLP::ball::CategoryHolder *ball_log_getCategoryHolder(int) \ { \ static BloombergLP::ball::CategoryHolder holder = { \ { BloombergLP::ball::CategoryHolder::e_UNINITIALIZED_CATEGORY }, \ { 0 }, { 0 } \ }; \ if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!holder.category())) { \ BSLS_PERFORMANCEHINT_UNLIKELY_HINT; \ BloombergLP::ball::Log::setCategory(&holder, CATEGORY); \ } \ return &holder; \ } \ enum { BALL_LOG_CATEGORYHOLDER = 0 } #define BALL_LOG_SET_CLASS_CATEGORY_HIERARCHICALLY(CATEGORY) \ static \ const BloombergLP::ball::CategoryHolder *ball_log_getCategoryHolder( \ const BloombergLP::ball::CategoryHolder& categoryHolder) \ { \ return &categoryHolder; \ } \ static \ const BloombergLP::ball::CategoryHolder *ball_log_getCategoryHolder(int) \ { \ static BloombergLP::ball::CategoryHolder holder = { \ { BloombergLP::ball::CategoryHolder::e_UNINITIALIZED_CATEGORY }, \ { 0 }, { 0 } \ }; \ if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!holder.category())) { \ BSLS_PERFORMANCEHINT_UNLIKELY_HINT; \ BloombergLP::ball::Log::setCategoryHierarchically(&holder, \ CATEGORY); \ } \ return &holder; \ } \ enum { BALL_LOG_CATEGORYHOLDER = 0 } // =============================== // Namespace-Scope Category Macros // =============================== #define BALL_LOG_SET_NAMESPACE_CATEGORY(CATEGORY) \ namespace { \ static \ const BloombergLP::ball::CategoryHolder *ball_log_getCategoryHolder(int) \ { \ static BloombergLP::ball::CategoryHolder holder = { \ { BloombergLP::ball::CategoryHolder::e_UNINITIALIZED_CATEGORY }, \ { 0 }, { 0 } \ }; \ if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!holder.category())) { \ BSLS_PERFORMANCEHINT_UNLIKELY_HINT; \ BloombergLP::ball::Log::setCategory(&holder, CATEGORY); \ } \ return &holder; \ } \ enum { BALL_LOG_CATEGORYHOLDER = 0 }; \ } #define BALL_LOG_SET_NAMESPACE_CATEGORY_HIERARCHICALLY(CATEGORY) \ namespace { \ static \ const BloombergLP::ball::CategoryHolder *ball_log_getCategoryHolder(int) \ { \ static BloombergLP::ball::CategoryHolder holder = { \ { BloombergLP::ball::CategoryHolder::e_UNINITIALIZED_CATEGORY }, \ { 0 }, { 0 } \ }; \ if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!holder.category())) { \ BSLS_PERFORMANCEHINT_UNLIKELY_HINT; \ BloombergLP::ball::Log::setCategoryHierarchically(&holder, \ CATEGORY); \ } \ return &holder; \ } \ enum { BALL_LOG_CATEGORYHOLDER = 0 }; \ } // ==================================== // Implementation Details: Do *NOT* Use // ==================================== // BALL_LOG_STREAM_CONST_IMP requires its argument to be a compile-time // constant. #define BALL_LOG_STREAM_CONST_IMP(SEVERITY) \ for (const BloombergLP::ball::CategoryHolder *ball_log_cAtEgOrYhOlDeR = \ BloombergLP::ball::Log::categoryHolderIfEnabled<(SEVERITY)>( \ ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER)); \ ball_log_cAtEgOrYhOlDeR; \ ) \ for (BloombergLP::ball::Log_Stream ball_log_lOg_StReAm( \ ball_log_cAtEgOrYhOlDeR->category(), \ __FILE__, \ __LINE__, \ (SEVERITY)); \ ball_log_cAtEgOrYhOlDeR; \ ball_log_cAtEgOrYhOlDeR = 0) // BALL_LOG_STREAM_IMP allows its argument to be calculated at run-time, at a // cost in performance. #define BALL_LOG_STREAM_IMP(SEVERITY) \ for (const BloombergLP::ball::CategoryHolder *ball_log_cAtEgOrYhOlDeR = \ ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER); \ ball_log_cAtEgOrYhOlDeR \ && ball_log_cAtEgOrYhOlDeR->threshold() >= (SEVERITY) \ && BloombergLP::ball::Log::isCategoryEnabled(ball_log_cAtEgOrYhOlDeR, \ (SEVERITY)); \ ) \ for (BloombergLP::ball::Log_Stream ball_log_lOg_StReAm( \ ball_log_cAtEgOrYhOlDeR->category(), \ __FILE__, \ __LINE__, \ (SEVERITY)); \ ball_log_cAtEgOrYhOlDeR; \ ball_log_cAtEgOrYhOlDeR = 0) // ======================= // C++ stream-based macros // ======================= #define BALL_LOG_STREAM(SEVERITY) \ BALL_LOG_STREAM_IMP((SEVERITY)) BALL_LOG_OUTPUT_STREAM #define BALL_LOG_TRACE \ BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_TRACE) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOG_DEBUG \ BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOG_INFO \ BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_INFO) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOG_WARN \ BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_WARN) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOG_ERROR \ BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_ERROR) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOG_FATAL \ BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_FATAL) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOG_STREAM_BLOCK(SEVERITY) BALL_LOG_STREAM_IMP((SEVERITY)) #define BALL_LOG_TRACE_BLOCK \ BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_TRACE) #define BALL_LOG_DEBUG_BLOCK \ BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG) #define BALL_LOG_INFO_BLOCK \ BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_INFO) #define BALL_LOG_WARN_BLOCK \ BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_WARN) #define BALL_LOG_ERROR_BLOCK \ BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_ERROR) #define BALL_LOG_FATAL_BLOCK \ BALL_LOG_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_FATAL) #define BALL_LOG_END "" // ==================================== // Implementation Details: Do *NOT* Use // ==================================== // BALL_LOGCB_STREAM_CONST_IMP requires its first argument to be a compile-time // constant. #define BALL_LOGCB_STREAM_CONST_IMP(SEVERITY, CALLBACK) \ for (const BloombergLP::ball::CategoryHolder *ball_log_cAtEgOrYhOlDeR = \ BloombergLP::ball::Log::categoryHolderIfEnabled<(SEVERITY)>( \ ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER)); \ ball_log_cAtEgOrYhOlDeR; \ ) \ for (BloombergLP::ball::Log_Stream ball_log_lOg_StReAm( \ ball_log_cAtEgOrYhOlDeR->category(), \ __FILE__, \ __LINE__, \ (SEVERITY)); \ ball_log_cAtEgOrYhOlDeR \ && (CALLBACK(&BALL_LOG_RECORD->customFields()), true); \ ball_log_cAtEgOrYhOlDeR = 0) // BALL_LOGCB_STREAM_IMP allows its first argument to be calculated at // run-time, at a cost in performance. #define BALL_LOGCB_STREAM_IMP(SEVERITY, CALLBACK) \ for (const BloombergLP::ball::CategoryHolder *ball_log_cAtEgOrYhOlDeR = \ ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER); \ ball_log_cAtEgOrYhOlDeR \ && ball_log_cAtEgOrYhOlDeR->threshold() >= (SEVERITY) \ && BloombergLP::ball::Log::isCategoryEnabled(ball_log_cAtEgOrYhOlDeR, \ (SEVERITY)); \ ) \ for (BloombergLP::ball::Log_Stream ball_log_lOg_StReAm( \ ball_log_cAtEgOrYhOlDeR->category(), \ __FILE__, \ __LINE__, \ (SEVERITY)); \ ball_log_cAtEgOrYhOlDeR \ && (CALLBACK(&BALL_LOG_RECORD->customFields()), true); \ ball_log_cAtEgOrYhOlDeR = 0) // ======================================== // C++ stream-based macros using a callback // ======================================== #define BALL_LOGCB_STREAM(BALL_SEVERITY, CALLBACK) \ BALL_LOGCB_STREAM_IMP((BALL_SEVERITY), (CALLBACK)) BALL_LOG_OUTPUT_STREAM #define BALL_LOGCB_TRACE(CALLBACK) \ BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_TRACE, \ (CALLBACK)) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOGCB_DEBUG(CALLBACK) \ BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG, \ (CALLBACK)) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOGCB_INFO(CALLBACK) \ BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_INFO, \ (CALLBACK)) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOGCB_WARN(CALLBACK) \ BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_WARN, \ (CALLBACK)) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOGCB_ERROR(CALLBACK) \ BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_ERROR, \ (CALLBACK)) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOGCB_FATAL(CALLBACK) \ BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_FATAL, \ (CALLBACK)) \ BALL_LOG_OUTPUT_STREAM #define BALL_LOGCB_STREAM_BLOCK(BALL_SEVERITY, CALLBACK) \ BALL_LOGCB_STREAM_IMP((BALL_SEVERITY), (CALLBACK)) #define BALL_LOGCB_TRACE_BLOCK(CALLBACK) \ BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_TRACE, \ (CALLBACK)) #define BALL_LOGCB_DEBUG_BLOCK(CALLBACK) \ BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG, \ (CALLBACK)) #define BALL_LOGCB_INFO_BLOCK(CALLBACK) \ BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_INFO, \ (CALLBACK)) #define BALL_LOGCB_WARN_BLOCK(CALLBACK) \ BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_WARN, \ (CALLBACK)) #define BALL_LOGCB_ERROR_BLOCK(CALLBACK) \ BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_ERROR, \ (CALLBACK)) #define BALL_LOGCB_FATAL_BLOCK(CALLBACK) \ BALL_LOGCB_STREAM_CONST_IMP(BloombergLP::ball::Severity::e_FATAL, \ (CALLBACK)) #define BALL_LOGCB_END "" // ==================================== // Implementation Details: Do *NOT* Use // ==================================== // BALL_LOGVA_CONST_IMP requires its first argument to be a compile-time // constant, while all the others may be variables. #define BALL_LOGVA_CONST_IMP(SEVERITY, ...) \ do { \ if (const BloombergLP::ball::CategoryHolder *ball_log_cAtEgOrYhOlDeR = \ BloombergLP::ball::Log::categoryHolderIfEnabled<(SEVERITY)>( \ ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER))) { \ BloombergLP::ball::Log_Formatter ball_log_fOrMaTtEr( \ ball_log_cAtEgOrYhOlDeR->category(), \ __FILE__, \ __LINE__, \ (SEVERITY)); \ BloombergLP::ball::Log::format(ball_log_fOrMaTtEr.messageBuffer(), \ ball_log_fOrMaTtEr.messageBufferLen(), \ __VA_ARGS__); \ } \ } while(0) // ===================== // 'printf'-style macros // ===================== // BALL_LOGVA allows all its arguments to be calculated at run-time, at a cost // in performance. #define BALL_LOGVA(SEVERITY, ...) \ do { \ const BloombergLP::ball::CategoryHolder *ball_log_cAtEgOrYhOlDeR = \ ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER); \ if (ball_log_cAtEgOrYhOlDeR->threshold() >= (SEVERITY) && \ BloombergLP::ball::Log::isCategoryEnabled(ball_log_cAtEgOrYhOlDeR, \ (SEVERITY))) { \ BloombergLP::ball::Log_Formatter ball_log_fOrMaTtEr( \ ball_log_cAtEgOrYhOlDeR->category(), \ __FILE__, \ __LINE__, \ (SEVERITY)); \ BloombergLP::ball::Log::format(ball_log_fOrMaTtEr.messageBuffer(), \ ball_log_fOrMaTtEr.messageBufferLen(), \ __VA_ARGS__); \ } \ } while(0) #define BALL_LOGVA_TRACE(...) \ BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_TRACE, __VA_ARGS__) #define BALL_LOGVA_DEBUG(...) \ BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_DEBUG, __VA_ARGS__) #define BALL_LOGVA_INFO( ...) \ BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_INFO, __VA_ARGS__) #define BALL_LOGVA_WARN( ...) \ BALL_LOGVA_CONST_IMP(BloombergLP::ball::Severity::e_WARN, __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__) // ============== // Utility Macros // ============== #define BALL_LOG_IS_ENABLED(SEVERITY) \ ((BALL_LOG_THRESHOLD >= (SEVERITY)) \ && BloombergLP::ball::Log::isCategoryEnabled( \ ball_log_getCategoryHolder(BALL_LOG_CATEGORYHOLDER), \ (SEVERITY))) namespace BloombergLP { namespace ball { // BDE_VERIFY pragma: push // BDE_VERIFY pragma: -AQQ01 // BDE_VERIFY pragma: -AQS01 // BDE_VERIFY pragma: -FB01 // BDE_VERIFY pragma: -KS00 // BDE_VERIFY pragma: -TR04 // BDE_VERIFY pragma: -TR17 // ======================= // CategoryHolder Accessor // ======================= inline const BloombergLP::ball::CategoryHolder* ball_log_getCategoryHolder( const BloombergLP::ball::CategoryHolder& categoryHolder) // Return the address of the specified 'categoryHolder'. Note that this // function facilitates consistent lookup of block-scope and class-scope // category holders (see "Logging Macro Reuse" in the "IMPLEMENTATION // NOTES" of the component implementation file for details). { return &categoryHolder; } // BDE_VERIFY pragma: pop class Record; // ========== // struct Log // ========== struct Log { // This 'struct' provides a namespace for a suite of utility functions that // simplify usage of the 'ball_loggermanager' component. The direct use // of these utility functions is *strongly* discouraged. // CLASS METHODS static int format(char *buffer, bsl::size_t numBytes, const char *format, ...) BSLS_ANNOTATION_PRINTF(3, 4); // Fill the specified 'buffer' with at most the specified 'numBytes' // characters produced by formatting the variable argument list // according to the specified 'printf'-style 'format' argument; return // the number of characters in the resulting formatted string. The // last character placed into 'buffer' is always a null terminator // (leaving at most 'numBytes - 1' bytes of formatted data). If // 'numBytes' is insufficient for the entire formatted string, this // method fills 'buffer' with the initial 'numBytes - 1' bytes of // formatted data followed by a null terminator and returns -1. Note // that with the exception of the return value, the behavior of this // function exactly matches that of the C99 function 'snprintf'. Also // note that 'snprintf' is not part of standard C++-98, so its // functionality is provided here. static Record *getRecord(const Category *category, const char *fileName, int lineNumber); // Return the address of a modifiable record having the specified // 'fileName' and 'lineNumber' attributes. The memory for the record // will be supplied by the allocator held by the logger manager // singleton if the specified 'category' is non-null, or by the // currently installed default allocator otherwise. The behavior is // undefined unless the logger manager singleton is initialized when // 'category' is non-null. Note that the returned 'Record' must // subsequently be supplied to a call to the 3-argument 'logMessage' // method. static void logMessage(const Category *category, int severity, const char *fileName, int lineNumber, const char *message); // Log a record containing the specified 'message' text, 'fileName', // 'lineNumber', 'severity', and the name of the specified 'category'. // (See the component-level documentation of 'ball_record' for more // information on the additional fields that are logged.) Store the // record in the buffer held by the logger if 'severity' is at least // as severe as the current "Record" threshold level of 'category'. // Pass the record directly to the registered observer if 'severity' // is at least as severe as the current "Pass" threshold level of // 'category'. Publish the entire contents of the buffer of the // logger if 'severity' is at least as severe as the current "Trigger" // threshold level of 'category'. Publish the entire contents of all // buffers of all loggers if 'severity' is at least as severe as the // current "Trigger-All" threshold level of 'category' (i.e., via the // callback supplied at construction of the logger manager singleton). // This method has no effect if 'category' is 0 or 'severity' is less // severe than each of the threshold levels of 'category'. The // behavior is undefined unless 'severity' is in the range '[1 .. 255]' // and the logger manager singleton is initialized when 'category' is // non-null. static void logMessage(const Category *category, int severity, Record *record); // Log the specified 'record' after setting its category attribute to // the specified 'category' and its severity attribute to the specified // 'severity'. (See the component-level documentation of 'ball_record' // for more information on the fields that are logged.) Store the // record in the buffer held by the logger if 'severity' is at least // as severe as the current "Record" threshold level of 'category'. // Pass the record directly to the registered observer if 'severity' // is at least as severe as the current "Pass" threshold level of // 'category'. Publish the entire contents of the buffer of the // logger if 'severity' is at least as severe as the current "Trigger" // threshold level of 'category'. Publish the entire contents of all // buffers of all loggers if 'severity' is at least as severe as the // current "Trigger-All" threshold level of 'category' (i.e., via the // callback supplied at construction of the logger manager singleton). // Finally, dispose of 'record'. This method has no effect (other than // disposing of 'record') if 'severity' is less severe than each of the // threshold levels of 'category'. The behavior is undefined unless // 'severity' is in the range '[1 .. 255]', 'record' was obtained by a // call to 'Log::getRecord', and, if 'category' is not 0, the logger // manager singleton is initialized. Note that 'record' will be // invalid after this method returns. static char *obtainMessageBuffer(bslmt::Mutex **mutex, int *bufferSize); // Block until access to the buffer used for formatting messages in // this thread of execution is available. Return the address of the // modifiable buffer to which this thread of execution has exclusive // access, load the address of the mutex that protects the buffer into // the specified '*mutex' address, and load the size (in bytes) of the // buffer into the specified 'bufferSize' address. The address remains // valid, and the buffer remains locked by this thread of execution, // until the 'Log::releaseMessageBuffer' method is called. The // behavior is undefined if this thread of execution currently holds a // lock on the buffer. Note that the buffer is intended to be used // *only* for formatting log messages immediately before a call to // 'Log::logMessage'; other use may adversely affect performance for // the entire program. static void releaseMessageBuffer(bslmt::Mutex *mutex); // Unlock the specified 'mutex' that guards the buffer used for // formatting messages in this thread of execution. The behavior is // undefined unless 'mutex' was obtained by a call to // 'Log::obtainMessageBuffer' and has not yet been unlocked. static bslma::ManagedPtr<char> obtainMessageBuffer(int *bufferSize); // Return a managed pointer that refers to the memory block to which // this thread of execution has exclusive access and load the size (in // bytes) of this buffer into the specified 'bufferSize' address. Note // that this method is intended for *internal* *use* only. static const Category *setCategory(const char *categoryName); // Return from the logger manager singleton's category registry the // address of the non-modifiable category having the specified // 'categoryName' if such a category exists, or if a new category // having 'categoryName' can be added to the registry (i.e., if the // registry has sufficient capacity to accommodate new entries); // otherwise, return the address of the non-modifiable *Default* // *Category*. Return 0 if the logger manager singleton is not // initialized. template <int t_SEVERITY> static const CategoryHolder *categoryHolderIfEnabled( const CategoryHolder *categoryHolder); // Return the specified 'categoryHolder' if the severity warrants // logging according to the specified 't_SEVERITY' and // 'categoryHolder', and 0 otherwise. static void setCategory(CategoryHolder *categoryHolder, const char *categoryName); // Load into the specified 'categoryHolder' the address of the // non-modifiable category having the specified 'categoryName' if such // a category exists, or if a new category having 'categoryName' can // be added to the registry (i.e., if the registry has sufficient // capacity to accommodate new entries); otherwise, load the address of // the non-modifiable *Default* *Category*. Also load into // 'categoryHolder' the maximum threshold level of the category // ultimately loaded into 'categoryHolder'. This method has no effect // if the logger manager singleton is not initialized. static const Category *setCategoryHierarchically(const char *categoryName); // Return from the logger manager singleton's category registry the // address of the non-modifiable category having the specified // 'categoryName', or, if no such category exists, add a new category // having 'categoryName' to the registry if possible (i.e., if the // registry has sufficient capacity to accommodate new entries); // otherwise, return the address of the non-modifiable *Default* // *Category*. If the logger manager singleton is not initialized, // return 0 with no effect. If a new category is created, it will have // the same threshold levels as the category in the logger manager // singleton whose name is the longest non-empty prefix of // 'categoryName' if such a category exists, and the threshold levels // will be set as if 'setCategory' had been called otherwise. static const Category *setCategoryHierarchically( CategoryHolder *categoryHolder, const char *categoryName); // Return from the logger manager singleton's category registry the // address of the non-modifiable category having the specified // 'categoryName', or, if no such category exists, add a new category // having 'categoryName' to the registry if possible (i.e., if the // registry has sufficient capacity to accommodate new entries); // otherwise, return the address of the non-modifiable *Default* // *Category*. If the logger manager singleton is not initialized, // return 0 with no effect. If a new category is created, it will have // the same threshold levels as the category in the logger manager // singleton whose name is the longest non-empty prefix of // 'categoryName' if such a category exists, and the threshold levels // will be set as if 'setCategory' had been called otherwise. If the // specified 'categoryHolder' is non-zero, load it with the address of // the returned category and the maximum threshold level of that // category, and link 'categoryHolder' to the other holders (if any) // that currently reference the category. Note that this method has // the same effect on the logger manager singleton's category registry // as the one-argument 'setCategoryHierarchically' regardless of // whether '0 == categoryHolder'. static bool isCategoryEnabled(const CategoryHolder *categoryHolder, int severity); // Return 'true' if logging to the category associated with the // specified 'categoryHolder' at the specified 'severity' is enabled, // or if 'Severity::e_WARN >= severity' and the logger manager // singleton is not initialized; return 'false' otherwise. }; // ================ // class Log_Stream // ================ class Log_Stream { // This class provides an aggregate of several objects relevant to the // logging of a message via the C++ stream-based macros: //.. // - record to be logged // - category to which to log the record // - severity at which to log the record // - stream to which the user log message is put //.. // As a side-effect of creating an object of this class, the record and // stream are also constructed. As a side-effect of destroying the // object, the record is logged. // // This class should *not* be used directly by client code. It is an // implementation detail of the macros provided by this component. // DATA const Category *d_category_p; // category to which record is logged // (held, not owned) Record *d_record_p; // logged record (held, not owned) const int d_severity; // severity at which record is logged bsl::ostream d_stream; // stream to which log message is put private: // NOT IMPLEMENTED Log_Stream(const Log_Stream&); Log_Stream& operator=(const Log_Stream&); public: // CREATORS Log_Stream(const Category *category, const char *fileName, int lineNumber, int severity); // Create a logging stream that holds (1) the specified 'category' and // 'severity', (2) a record that is created from the specified // 'fileName' and 'lineNumber', and (3) an 'bsl::ostream' to which the // log message is put. ~Log_Stream() BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(false); // Log the record held by this logging stream to the held category (as // returned by 'category') at the held severity (as returned by // 'severity') and destroy this logging stream. // MANIPULATORS Record *record(); // Return the address of the modifiable log record held by this logging // stream. The address remains valid until this logging stream is // destroyed. bsl::ostream& stream(); // Return a reference to the modifiable stream held by this logging // stream. The reference remains valid until this logging stream is // destroyed. // ACCESSORS const Category *category() const; // Return the address of the non-modifiable category held by this // logging stream. const Record *record() const; // Return the address of the non-modifiable log record held by this // logging stream. The address remains valid until this logging stream // is destroyed. int severity() const; // Return the severity held by this logging stream. }; // =================== // class Log_Formatter // =================== class Log_Formatter { // This class provides an aggregate of several objects relevant to the // logging of a message via the 'printf'-style macros: //.. // - record to be logged // - category to which to log the record // - severity at which to log the record // - buffer in which the user log message is formatted //.. // As a side-effect of creating an object of this class, the record is // constructed, and the buffer is obtained. As a side-effect of destroying // the object, the record is formatted, using the buffer, and logged. // // This class should *not* be used directly by client code. It is an // implementation detail of the macros provided by this component. // DATA const Category *d_category_p; // category to which record is // logged (held, not owned) Record *d_record_p; // logged record (held, not owned) const int d_severity; // severity at which record is // logged int d_bufferLen; // length of buffer bslma::ManagedPtr<char> d_buffer; // buffer for formatted user log // message private: // NOT IMPLEMENTED Log_Formatter(const Log_Formatter&); Log_Formatter& operator=(const Log_Formatter&); public: // CREATORS Log_Formatter(const Category *category, const char *fileName, int lineNumber, int severity); // Create a logging formatter that holds (1) the specified 'category' // and 'severity', (2) a record that is created from the specified // 'fileName' and 'lineNumber', and (3) a buffer into which the log // message is formatted. ~Log_Formatter(); // Log the record held by this logging formatter to the held category // (as returned by 'category') at the held severity (as returned by // 'severity'), and destroy this logging formatter. // MANIPULATORS char *messageBuffer(); // Return the address of the modifiable buffer held by this logging // formatter. The address remains valid until this logging formatter // is destroyed. Record *record(); // Return the address of the modifiable log record held by this logging // formatter. The address remains valid until this logging formatter // is destroyed. // ACCESSORS const Category *category() const; // Return the address of the non-modifiable category held by this // logging formatter. int messageBufferLen() const; // Return the length (in bytes) of the buffer held by this logging // formatter. const Record *record() const; // Return the address of the non-modifiable log record held by this // logging formatter. The address remains valid until this logging // formatter is destroyed. int severity() const; // Return the severity held by this logging formatter. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // ---------- // struct Log // ---------- // CLASS METHODS template <int t_SEVERITY> inline const CategoryHolder *Log::categoryHolderIfEnabled( const CategoryHolder *categoryHolder) { // The following condition is calculated at compile time so has no run-time // cost. Code from the branch not taken will not be generated. Note that // we expect TRACE and DEBUG messages not to be logged and thus they are // marked with unlikely performance hints. if (t_SEVERITY <= Severity::e_INFO) { if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(categoryHolder->threshold() >= t_SEVERITY)) { if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY( Log::isCategoryEnabled(categoryHolder, t_SEVERITY))) { return categoryHolder; // RETURN } else { BSLS_PERFORMANCEHINT_UNLIKELY_HINT; } BSLS_PERFORMANCEHINT_UNLIKELY_HINT; } } else { if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY( categoryHolder->threshold() >= t_SEVERITY)) { BSLS_PERFORMANCEHINT_UNLIKELY_HINT; if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY( Log::isCategoryEnabled(categoryHolder, t_SEVERITY))) { BSLS_PERFORMANCEHINT_UNLIKELY_HINT; return categoryHolder; // RETURN } } } return 0; } inline const Category *Log::setCategoryHierarchically(const char *categoryName) { return setCategoryHierarchically(0, categoryName); } // ---------------- // class Log_Stream // ---------------- // MANIPULATORS inline Record *Log_Stream::record() { return d_record_p; } inline bsl::ostream& Log_Stream::stream() { return d_stream; } // ACCESSORS inline const Category *Log_Stream::category() const { return d_category_p; } inline const Record *Log_Stream::record() const { return d_record_p; } inline int Log_Stream::severity() const { return d_severity; } // ------------------- // class Log_Formatter // ------------------- // MANIPULATORS inline char *Log_Formatter::messageBuffer() { return d_buffer.get(); } inline Record *Log_Formatter::record() { return d_record_p; } // ACCESSORS inline const Category *Log_Formatter::category() const { return d_category_p; } inline int Log_Formatter::messageBufferLen() const { return d_bufferLen; } inline const Record *Log_Formatter::record() const { return d_record_p; } inline int Log_Formatter::severity() const { return d_severity; } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2017 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------