// bsls_log.h -*-C++-*- #ifndef INCLUDED_BSLS_LOG #define INCLUDED_BSLS_LOG #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a namespace for low-level logging functions. // //@CLASSES: // bsls::Log: namespace for low-level logging functions // //@MACROS: // BSLS_LOG: write log message using 'printf'-style format specification // BSLS_LOG_SIMPLE: write log message as an unformatted string // BSLS_LOG_FATAL: write fatal message using 'printf' format specification // BSLS_LOG_ERROR: write error message using 'printf' format specification // BSLS_LOG_WARN: write warning message using 'printf' format specification // BSLS_LOG_INFO: write info message using 'printf' format specification // BSLS_LOG_DEBUG: write debug message using 'printf' format specification // BSLS_LOG_TRACE: write trace message using 'printf' format specification // //@SEE_ALSO: bsls_logseverity // //@DESCRIPTION: This component provides a set of macros, along with a // namespace, 'bsls::Log', which contains a suite of utility functions for // logging in low-level code. The macros and functions in this component // provide a consistent interface for logging across different platforms // through the use of a global cross-platform log message handler function. // Users can customize the logging behavior by providing their own log message // handler function. Note that this component is intended to be used only when // a more fully-featured logger is not available. // ///Macro Reference ///--------------- // This section provides documentation for the macros defined in this component. //.. // BSLS_LOG(severity, ...) // If the specified 'severity' is at least as severe as // 'Log::severityThreshold', write a message having 'severity' to the // currently installed log message handler, which contains a formatted // string that would result from applying the 'printf'-style formatting // rules to the specified '...', using the first parameter as the format // string and any further parameters as the expected substitutions. If // 'severity' is less severe than 'severityThreshold' then this macro has // no effect. The file name and line number of the point of expansion of // the macro are automatically used as the file name and line number for // the log message. The behavior is undefined unless the first parameter // of '...' is a valid 'printf'-style format string, and all substitutions // needed by the format string are in the subsequent elements of '...'. // // BSLS_LOG_FATAL(...) // BSLS_LOG_ERROR(...) // BSLS_LOG_WARN(...) // BSLS_LOG_INFO(...) // BSLS_LOG_DEBUG(...) // BSLS_LOG_TRACE(...) // Instantiate the 'BSLS_LOG' macro with the severity appropriate for // the macros name. Note that this is syntactic sugar, to avoid the // complete text "bsls::LogSeverity::e_" being needed each time a message // is logged with 'BSLS_LOG'. // // BSLS_LOG_SIMPLE(severity, msg) // If the specified 'severity' is at least as severe as // 'Log::severityThreshold', write a message having 'severity' and the // specified 'msg' to the currently installed log message handler, with the // file name and line number of the point of expansion of the macro // automatically used as the file name and line number of the log. If // 'severity' is less severe than 'severityThreshold' then this macro has // no effect. //.. // ///Motivation ///---------- // Using the functionality of this component instead of writing messages // directly to 'stderr' has the following advantages: // //: o Users have the freedom to customize the default logging behavior. //: //: A user may want all logs to be automatically redirected to a file or may //: want to add some custom formatting or handling. These abilities are not //: available if the output stream is predetermined. //: //: o The logging mechanism behaves correctly on all platforms by default. //: //: Some platforms have particular restrictions on the use of the standard //: output streams. For example, Windows applications running in non-console //: mode do not have a concept of 'stdout' or 'stderr'; writing directly to //: either of these streams is known to hang the process when no console is //: attached. // ///Functionality ///------------- // This section describes the functionality provided by this component in more // detail. // ///Log Message Handler ///- - - - - - - - - - // The 'bsls::Log' class provides two 'static' methods, 'logMessageHandler' and // 'setLogMessageHandler', which can be used, respectively, to retrieve and set // the globally installed log message handler through which all log messages // are written. All log message handlers must follow the signature and // contract requirements of the 'bsls::Log::LogMessageHandler' 'typedef'. // // The log message handler 'bsls::Log::platformDefaultMessageHandler' is // installed by default as the global handler. This handler writes all log // messages to 'stderr', except in Windows non-console mode where the // destination of the 'OutputDebugString' function is used. // // In addition to the default handler, the log message handlers // 'bsls::Log::stdoutMessageHandler' and 'bsls::Log::stderrMessageHandler' are // provided as a set of simple handlers that write log messages to 'stdout' and // 'stderr', respectively. // ///Writing Log Messages /// - - - - - - - - - - // There are four ways to invoke the currently installed log message handler, // depending on the specific behavior required: The macro 'BSLS_LOG' allows a // formatted message to be written using a 'printf'-style format string. The // macro 'BSLS_LOG_SIMPLE' allows a simple, unformatted string to be written. // Both of the macros automatically use the file name and line number of the // point that the macro was invoked. The 'static' methods // 'bsls::Log::logFormattedMessage' and 'bsls::Log::logMessage' provide the // same functionality as 'BSLS_LOG' and 'BSLS_LOG_SIMPLE, respectively, except // that these two methods allow a file name and line number to be passed in as // parameters. This is described in table form as follows: //.. // .=========================================================================. // | Mechanism | Formatted | Automatic File & Line | // |=========================================================================| // | BSLS_LOG | YES | YES | // |--------------------------------|--------------|-------------------------| // | BSLS_LOG_[LEVEL] | YES | YES | // |--------------------------------|--------------|-------------------------| // | BSLS_LOG_SIMPLE | NO | YES | // |--------------------------------|--------------|-------------------------| // | bsls::Log::logFormattedMessage | YES | NO | // |--------------------------------|--------------|-------------------------| // | bsls::Log::logMessage | NO | NO | // `-------------------------------------------------------------------------' //.. // ///Log Severity and the Severity Threshold /// - - - - - - - - - - - - - - - - - - - // Clients submitting a message to 'bsls::Log' (either through a function or // one of the macros) either implicitly or explicitly provide a severity level // describing the relative importance of that message to clients. The possible // severity levels are FATAL, ERROR, WARNING, INFO, DEBUG, and TRACE (these are // enumerated in 'bsls_logseverity'). // // The severity of a logged message is used to determine whether the message // is published to the log using the currently installed 'LogMessageHandler' // callback. Also, typically a 'LogMessageHandler' callback implementation // will report a message's severity along side that message in the log. // // Clients can configure the severity threshold, at or above which a log // message will be published, using 'setSeverityThreshold'. For example: //.. // // Messages having 'e_WARN' or higher severity will be output to the log. // // bsls::Log::setSeverityThreshold(bsls::LogSeverity::e_WARN); //.. // ///Usage ///----- // This section illustrates the intended use of this component. // ///Example 1: Logging Formatted Messages ///- - - - - - - - - - - - - - - - - - - // Suppose that we want to write a formatted log message using 'printf'-style // format specifiers when the preconditions of a function are not met. The // 'BSLS_LOG' macro can be used for this purpose. // // First, we begin to define a function, 'add', which will return the sum of // two positive integer values: //.. // // myapp.cpp // // unsigned int add(int a, int b) // // Return the sum of the specified 'a' and the specified 'b'. The // // behavior is undefined unless 'a' and 'b' are not negative. // { //.. // // Now, we check the precondition of the function, and use the 'BSLS_LOG_ERROR' // macro to write a log message if one of the input parameters is less than 0: //.. // if(a < 0 || b < 0) { // BSLS_LOG_ERROR("Invalid input combination (%d, %d).", a, b); // return 0; // RETURN // } // // return static_cast<unsigned int>(a) + static_cast<unsigned int>(b); // } //.. // // Next, we may erroneously call the 'add' function with a negative argument: //.. // unsigned int x = add(3, -100); //.. // Finally, assuming the default log message handler is currently installed, we // observe the following output printed to 'stderr' or to the Windows debugger: //.. // ERROR myapp.cpp:8 Invalid input combination (3, -100). //.. // Note that an arbitrary string should never be passed to 'BSLS_LOG' as the // format string. If the string happens to contain 'printf'-style format // specifiers but the expected substitutions are not present, it will lead to // undefined behavior. #include <bsls_atomicoperations.h> #include <bsls_logseverity.h> #include <bsls_pointercastutil.h> #include <bsls_types.h> namespace BloombergLP { namespace bsls { // ========= // class Log // ========= class Log { // This class serves as a namespace containing a suite of utility functions // that allow low-level code to write log messages to a configurable, // globally controlled destination. public: // TYPES typedef void (*LogMessageHandler)(bsls::LogSeverity::Enum severity, const char *file, int line, const char *message); // The 'LogMessageHandler' 'typedef' represents a type of function that // handles log messages in an unspecified way (e.g., by writing the log // message to an output stream or to a file). Because they can be // called concurrently from multiple threads, log message handlers // must be thread-safe. While installed, handlers must exhibit only // defined behavior if the specified 'file' is a null-terminated // string, the specified 'line' is not negative, and the specified // 'message' is a null-terminated string. private: // CLASS DATA static bsls::AtomicOperations::AtomicTypes::Pointer s_logMessageHandler; // the currently // installed log // message handler // (not owned) static bsls::AtomicOperations::AtomicTypes::Int s_severityThreshold; // the current // severity // threshold public: // CLASS METHODS static void logFormattedMessage(bsls::LogSeverity::Enum severity, const char *file, int line, const char *format, ...); // If the specified 'severity' is at least as severe as // 'severityThrehold', invoke the currently installed log message // handler with 'severity' and the specified 'file' and 'line', as // well as a message string created by calling 'sprintf' on the // specified 'format' with the specified variadic arguments; otherwise // (if 'severity' is less severe), this operation has no effect. The // behavior is undefined unless '0 <= line', and 'format' is a valid // 'sprintf' format specification for the supplied variadic arguments. static void logMessage(bsls::LogSeverity::Enum severity, const char *file, int line, const char *message); // If the specified 'severity' is at least as severe as // 'severityThreshold', invoke the currently installed log message // handler with 'severity', as well as the specified 'file', 'line', // and 'message'; otherwise (if 'severity' is less severe), this // operation has no effect. The behavior is undefined unless // '0 <= line'. static Log::LogMessageHandler logMessageHandler(); // Return the address of the currently installed log message handler. static void platformDefaultMessageHandler( bsls::LogSeverity::Enum severity, const char *file, int line, const char *message); // Write, to a platform-specific destination, a string composed of the // specified 'severity', 'file' name, 'line' number, and 'message'. On // *non*-Windows systems, write the log record to the 'stderr' output // stream. On *Windows* systems: If the current process is running in // *console* *mode*, write the log record to the 'stderr' output // stream. If the current process is running in *non-console* *mode*, // write the log record to the Windows process debugger. The behavior // is undefined unless '0 <= line'. Note that this function is used as // the default log message handler. Also note that this function will // write the message irrespective of the current 'severityThreshold'. static void setLogMessageHandler(Log::LogMessageHandler handler); // Install the specified 'handler' as the current log message handler. static void setSeverityThreshold(bsls::LogSeverity::Enum severity); // Set the severity threshold at which log records are written by // 'logMessage' and 'logFormattedMessage' to the specified 'severity'. static bsls::LogSeverity::Enum severityThreshold(); // Return the currently configured severity threshold at or above which // records are written by 'logMessage' and 'logFormattedMessage'. static void stderrMessageHandler(bsls::LogSeverity::Enum severity, const char *file, int line, const char *message); // Write, to the 'stderr' output stream, a string composed of the // specified 'severity', 'file' name, 'line' number, and the 'message'. // The behavior is undefined unless '0 <= line'. Note that this // function provides an implementation of the 'LogMessageHandler' // function prototype, and will write the message irrespective of the // current 'severityThreshold'. static void stdoutMessageHandler(bsls::LogSeverity::Enum severity, const char *file, int line, const char *message); // Write, to the 'stdout' output stream, a string composed of the // specified 'severity', 'file' name, 'line' number, and the 'message'. // The behavior is undefined unless '0 <= line'. Note that this // function provides an implementation of the 'LogMessageHandler' // function prototype, and will write the message irrespective of the // current 'severityThreshold'. }; } // close package namespace } // close enterprise namespace // ========================== // BSLS_LOG Macro Definitions // ========================== #define BSLS_LOG(severity, ...) \ do { \ if (severity <= BloombergLP::bsls::Log::severityThreshold()) { \ BloombergLP::bsls::Log::logFormattedMessage((severity), \ __FILE__, \ __LINE__, \ __VA_ARGS__); \ } \ } while(false) #define BSLS_LOG_FATAL(...) BSLS_LOG(BloombergLP::bsls::LogSeverity::e_FATAL,\ __VA_ARGS__) #define BSLS_LOG_ERROR(...) BSLS_LOG(BloombergLP::bsls::LogSeverity::e_ERROR,\ __VA_ARGS__) #define BSLS_LOG_WARN(...) BSLS_LOG(BloombergLP::bsls::LogSeverity::e_WARN, \ __VA_ARGS__) #define BSLS_LOG_INFO(...) BSLS_LOG(BloombergLP::bsls::LogSeverity::e_INFO, \ __VA_ARGS__) #define BSLS_LOG_DEBUG(...) BSLS_LOG(BloombergLP::bsls::LogSeverity::e_DEBUG,\ __VA_ARGS__) #define BSLS_LOG_TRACE(...) BSLS_LOG(BloombergLP::bsls::LogSeverity::e_TRACE,\ __VA_ARGS__) #define BSLS_LOG_SIMPLE(severity, msg) \ (BloombergLP::bsls::Log::logMessage((severity), __FILE__, __LINE__, (msg))) namespace BloombergLP { namespace bsls { // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // ========= // class Log // ========= // CLASS METHODS inline void Log::logMessage(bsls::LogSeverity::Enum severity, const char *file, int line, const char *message) { if (severity <= severityThreshold()) { (logMessageHandler())(severity, file, line, message); } } inline Log::LogMessageHandler Log::logMessageHandler() { return PointerCastUtil::cast<LogMessageHandler>( bsls::AtomicOperations::getPtrAcquire(&s_logMessageHandler)); } inline void Log::setLogMessageHandler(Log::LogMessageHandler handler) { bsls::AtomicOperations::setPtrRelease( &s_logMessageHandler, PointerCastUtil::cast<void *>(handler)); } inline void Log::setSeverityThreshold(bsls::LogSeverity::Enum severity) { bsls::AtomicOperations::setIntRelaxed(&s_severityThreshold, severity); } inline bsls::LogSeverity::Enum Log::severityThreshold() { return static_cast<bsls::LogSeverity::Enum>( bsls::AtomicOperations::getIntRelaxed(&s_severityThreshold)); } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2013 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 ----------------------------------