// 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:
//
// * 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.
// * 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
//
// /// Return the sum of the specified `a` and the specified `b`.  The
// /// behavior is undefined unless `a` and `b` are not negative.
// unsigned int add(int a, int b)
// {
// ```
//
// 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
                         // =========

/// 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.
class Log {

  public:
    // TYPES

    /// 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.
    typedef void (*LogMessageHandler)(bsls::LogSeverity::Enum  severity,
                                      const char              *file,
                                      int                      line,
                                      const char              *message);

  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

    /// 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 logFormattedMessage(bsls::LogSeverity::Enum  severity,
                                    const char              *file,
                                    int                      line,
                                    const char              *format,
                                    ...);

    /// 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 void logMessage(bsls::LogSeverity::Enum  severity,
                           const char              *file,
                           int                      line,
                           const char              *message);

    /// Return the address of the currently installed log message handler.
    static Log::LogMessageHandler logMessageHandler();

    /// 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 platformDefaultMessageHandler(
                                             bsls::LogSeverity::Enum  severity,
                                             const char              *file,
                                             int                      line,
                                             const char              *message);

    /// Install the specified `handler` as the current log message handler.
    static void setLogMessageHandler(Log::LogMessageHandler handler);

    /// Set the severity threshold at which log records are written by
    /// `logMessage` and `logFormattedMessage` to the specified `severity`.
    static void setSeverityThreshold(bsls::LogSeverity::Enum severity);

    /// Return the currently configured severity threshold at or above which
    /// records are written by `logMessage` and `logFormattedMessage`.
    static bsls::LogSeverity::Enum severityThreshold();

    /// 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 stderrMessageHandler(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`.
    static void stdoutMessageHandler(bsls::LogSeverity::Enum  severity,
                                     const char              *file,
                                     int                      line,
                                     const char              *message);
};

}  // 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 ----------------------------------
