// ball_fileobserver.h                                                -*-C++-*-
#ifndef INCLUDED_BALL_FILEOBSERVER
#define INCLUDED_BALL_FILEOBSERVER

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a thread-safe observer that logs to a file and to 'stdout'.
//
//@CLASSES:
//  ball::FileObserver: observer that writes log records to a file and 'stdout'
//
//@SEE_ALSO: ball_record, ball_context, ball_observer, ball_fileobserver2
//
//@DESCRIPTION: This component provides a concrete implementation of the
// 'ball::Observer' protocol, 'ball::FileObserver', for publishing log records
// to 'stdout' and, optionally, to a user-specified file.  The following
// inheritance hierarchy diagram shows the classes involved and their methods:
//..
//                ,------------------.
//               ( ball::FileObserver )
//                `------------------'
//                         |              ctor
//                         |              disableFileLogging
//                         |              disableTimeIntervalRotation
//                         |              disableSizeRotation
//                         |              disableStdoutLoggingPrefix
//                         |              disablePublishInLocalTime
//                         |              enableFileLogging
//                         |              enableStdoutLoggingPrefix
//                         |              enablePublishInLocalTime
//                         |              forceRotation
//                         |              rotateOnSize
//                         |              rotateOnTimeInterval
//                         |              setOnFileRotationCallback
//                         |              setStdoutThreshold
//                         |              setLogFormat
//                         |              suppressUniqueFileNameOnRotation
//                         |              getLogFormat
//                         |              isFileLoggingEnabled
//                         |              isStdoutLoggingPrefixEnabled
//                         |              isPublishInLocalTimeEnabled
//                         |              isSuppressUniqueFileNameOnRotation
//                         |              rotationLifetime
//                         |              rotationSize
//                         |              stdoutThreshold
//                         V
//                  ,--------------.
//                 ( ball::Observer )
//                  `--------------'
//                                        dtor
//                                        publish
//                                        releaseRecords
//..
// A 'ball::FileObserver' object processes the log records received through its
// 'publish' method by writing them to 'stdout' and, if so configured, to a
// user-specified file.  The format of published log records is
// user-configurable for both destinations, 'stdout' and file (see {Log Record
// Formatting} below).  Although logging to a file is initially disabled
// following construction, the most common use-case for 'ball::FileObserver' is
// to also log to a file, enabled by calling the 'enableFileLogging' method.
// In addition, a file observer may be configured to perform automatic log file
// rotation (see {Log File Rotation} below).
//
///File Observer Configuration Synopsis
///------------------------------------
// 'ball::FileObserver' offers several constructor arguments and manipulators
// that may be used to configure various aspects of a file observer object.
// These are summarized in the following tables along with the accessors that
// can be used to query the current state of the configuration.  Further
// details are provided in the following sections and the function-level
// documentation.
//..
// +-----------------------+-----------------------+
// | Aspect                | Constructor Arguments |
// +=======================+=======================+
// | Log Record Timestamps | publishInLocalTime    |
// +-----------------------+-----------------------+
// | 'stdout' Logging      | stdoutThreshold       |
// +-----------------------+-----------------------+
//
// +-------------+------------------------------------+
// | Aspect      | Related Methods                    |
// +=============+====================================+
// | Log Record  | setLogFormat                       |
// | Formatting  | enableStdoutLoggingPrefix          |
// |             | disableStdoutLoggingPrefix         |
// |             | getLogFormat                       |
// |             | isStdoutLoggingPrefixEnabled       |
// +-------------+------------------------------------+
// | Log Record  | enablePublishInLocalTime           |
// | Timestamps  | disablePublishInLocalTime          |
// |             | isPublishInLocalTimeEnabled        |
// |             |                                    |
// +-------------+------------------------------------+
// | File        | enableFileLogging                  |
// | Logging     | disableFileLogging                 |
// |             | isFileLoggingEnabled               |
// |             |                                    |
// +-------------+------------------------------------+
// | 'stdout'    | setStdoutThreshold                 |
// | Logging     | enableStdoutLoggingPrefix          |
// |             | disableStdoutLoggingPrefix         |
// |             | stdoutThreshold                    |
// |             | isStdoutLoggingPrefixEnabled       |
// +-------------+------------------------------------+
// | Log File    | rotateOnSize                       |
// | Rotation    | rotateOnTimeInterval               |
// |             | disableSizeRotation                |
// |             | disableTimeIntervalRotation        |
// |             | setOnFileRotationCallback          |
// |             | suppressUniqueFileNameOnRotation   |
// |             | rotationSize                       |
// |             | rotationLifetime                   |
// |             | isSuppressUniqueFileNameOnRotation |
// +-------------+------------------------------------+
//..
// In general, a 'ball::FileObserver' object can be dynamically configured
// throughout its lifetime (in particular, before or after being registered
// with a logger manager).  However, note that for 'ball::FileObserver',
// configuration changes that affect how records are logged (e.g.,
// 'enablePublishInLocalTime' and 'disablePublishInLocalTime') impact only
// those records that are published subsequent to making the configuration
// change.
//
///Log Record Formatting
///---------------------
// By default, the output format of published log records, whether written to
// 'stdout' or to a user-specified file, is:
//..
//  DATE_TIME PID:THREAD-ID SEVERITY FILE:LINE CATEGORY MESSAGE USER-FIELDS
//..
// where 'DATE' and 'TIME' are of the form 'DDMonYYYY' and 'HH:MM:SS.mmm',
// respectively ('Mon' being the 3-letter abbreviation for the month).  For
// example, a log record will have the following appearance when the default
// format is in effect (assuming that no user-defined fields are present):
//..
//  18MAY2005_18:58:12.076 7959:1 WARN ball_fileobserver.t.cpp:404 TEST hello!
//..
// The default format for records published to 'stdout' (only) can be shortened
// by calling 'disableStdoutLoggingPrefix'.  This method has the effect of
// reducing the above example message to the following when output to 'stdout':
//..
//  WARN ball_fileobserver.t.cpp:404 TEST hello!
//..
// For additional flexibility, the 'setLogFormat' method can be called to
// configure the format of published records.  This method takes two arguments:
// the first argument specifies the format of records logged to a file and the
// second applies to records that are logged to 'stdout'.  The respective
// formats are specified using 'printf'-style ('%'-prefixed) conversion
// specifications.  (See {'ball_recordstringformatter'} for information on how
// format specifications are defined and interpreted.)  For example, the
// following statement will force subsequent records to be logged in a format
// that is almost identical to the default long format except that the
// timestamp attribute will be written in ISO 8601 format:
//..
//  fileObserver.setLogFormat("\n%I %p:%t %s %f:%l %c %m %u\n",
//                            "\n%I %p:%t %s %f:%l %c %m %u\n");
//..
// Once a customized format is specified for 'stdout', calling
// 'disableStdoutLoggingPrefix' will switch to the default short format, i.e.,
// "\n%s %f:%l %c %m %u\n".  If 'enableStdoutLoggingPrefix' is subsequently
// called, the customized format specified in the most recent call to
// 'setLogFormat' will be reinstated.  Note that the observer emits newline
// characters at the beginning and at the end of a log record by default, so
// the user needs to add them explicitly to the format string to preserve this
// behavior.
//
// Note that in the sample long-form message above the timestamp has
// millisecond precision ('18MAY2005_18:58:12.076').  If microsecond precision
// is desired instead, consider using either the '%D' or '%O' format
// specification supported by 'ball_recordstringformatter'.
//
///Log Record Timestamps
///---------------------
// By default, the timestamp attributes of published records are written in UTC
// time (unless 'true' is supplied for the optional 'publishInLocalTime'
// constructor flag).  To write timestamps in local time instead, call the
// 'enablePublishInLocalTime' method.  Note that the local time offset is
// calculated using the UTC timestamp of each record.  To revert to UTC time,
// call the 'disablePublishInLocalTime' method.  Whether UTC time or local time
// is in effect can be queried via 'isPublishInLocalTimeEnabled'.
//
///Local Time Offset Calculations
/// - - - - - - - - - - - - - - -
// The calculation of the local time offset adds some overhead to the
// publication of each log record.  If this overhead is an issue, it can be
// mitigated by installing a high-performance local-time offset callback for
// 'bdlt::CurrentTime' in 'main'.  See {'bsls_systemtime'} for the details of
// installing such a callback and see {'baltzo_localtimeoffsetutil'} for an
// example facility.  Note that such callbacks can improve performance for all
// users of 'bdlt::CurrentTime', not just the 'ball' logger.
//
///Log Filename Patterns
///---------------------
// The 'enableFileLogging' method supports the use of '%'-escape sequences to
// specify log filenames.  The recognized sequences are as follows:
//..
//  %Y - current year   (4 digits with leading zeros)
//  %M - current month  (2 digits with leading zeros)
//  %D - current day    (2 digits with leading zeros)
//  %h - current hour   (2 digits with leading zeros)
//  %m - current minute (2 digits with leading zeros)
//  %s - current second (2 digits with leading zeros)
//  %T - current datetime, equivalent to "%Y%M%D_%h%m%s"
//  %p - process ID
//..
// The date and time elements of the derived filename are based on the time
// when the log file is created.  Furthermore, these elements are based on
// either UTC time or local time depending on the value returned by
// 'isPublishInLocalTimeEnabled'.  (See {Log Record Timestamps} for the
// similarity with the treatment of record timestamps.)
//
// For example, a log filename pattern of "task.log.%Y%M%D_%h%m%s" will yield
// the filename 'task.log.20110501_123000' if the file is created on
// 01-May-2011 at 12:30:00 local time (assuming 'enablePublishInLocalTime' was
// called).
//
///Log File Rotation
///-----------------
// A 'ball::FileObserver' may be configured to perform automatic rotation of
// log files based on simple file rotation conditions (or rules).
//
///File Rotation Conditions
/// - - - - - - - - - - - -
// Rotation rules may be established based on the size of the log file (i.e., a
// "rotation-on-size" rule), and a periodic time interval (i.e., a
// "rotation-on-time-interval" rule).  These rules are independently enabled by
// the 'rotateOnSize' and 'rotateOnTimeInterval' methods, respectively.  If
// both rules are in effect, log file rotation is performed whenever either
// rule applies.
//
///Rotated File Naming
///- - - - - - - - - -
// When a log file is rotated, a new filename is generated using the pattern
// supplied to 'enableFileLogging'.  If the file having the new name does not
// exist, the current log file is closed, and the logging continues to the new
// file.
//
// If the file having the new name already exits, then the behavior of the file
// rotation is further controlled by the flag set with
// 'suppressUniqueFileNameOnRotation':
//
//: o 'suppressUniqueFileNameOnRotation(false)' (*default* behavior)
//:   The current log filename is renamed by appending a timestamp in the form
//:   ".%Y%M%D_%h%m%s" where the timestamp indicates when the file being
//:   rotated was last opened (the time of either the last file rotation or the
//:   last call to 'enableFileLogging', whichever is most recent).  As with the
//:   timestamps of logged records, the timestamps appended to log filenames
//:   upon rotation will be in UTC time or local time depending on the value
//:   returned by 'isPublishInLocalTimeEnabled'.
//:
//: o 'suppressUniqueFileNameOnRotation(true)'
//:   The logging continues to the *current* log file, effectively suppressing
//:   log filename rotation.  This may happen when the log file pattern does
//:   not contain %-escape sequences indicating a time period, or the rotation
//:   interval is less than the time period encoded by %-escape sequences.  In
//:   order to rotate log files in this mode, the log file pattern MUST contain
//:   %-escape sequences that specify date and (optionally) time.  For example,
//:   the log filename pattern "app_%Y%M%D.log" will produce a single log file
//:   per calendar day (assuming, the rotation on time is enabled and the
//:   rotation happens at least once a day).
//
// The two tables below illustrate the names of old and new log files when a
// file rotation occurs.  We assume that the log file is rotated on 2011-May-21
// at 12:29:59 local time and that the last rotation occurred at 12:30:00 on
// the previous day.  We further assume that 'enablePublishInLocalTime' was
// called, so that all date and time elements are rendered in local time.
//
// The first table shows the name change (if any) of the (old) log file being
// rotated when the flag controlled by 'suppressUniqueFileNameOnRotation'
// is set to 'false':
//..
//  Disabled: 'suppressUniqueFileNameOnRotation'
//
//  For brevity:
//      <TS1> = 20210520_123000
//      <TS2> = 20210521_122959 (aka next day, about the same time)
//
//  +----------------+-----------------+----------------+----------------------
//  | Pattern        | Filename Before | Filename After | Rotated Filename
//  |                | Rotation        | Rotation       |
//  +----------------+-----------------+----------------+----------------------
//  | "a.log"        | a.log           | a.log          | a.log.<TS1>
//  | "a.log.%T"     | a.log.<TS1>     | a.log.<TS2>    | a.log.<TS1>
//  | "a.log.%Y%M"   | a.log.202105    | a.log.202105   | a.log.202105.<TS1>
//  | "a.log.%Y%M%D" | a.log.20210520  | a.log.20110521 | a.log.20210520
//  +----------------+-----------------+----------------+----------------------
//..
// Note that upon rotation a timestamp was appended to the name of the rotated
// file when the log pattern does not contain %-escape sequences indicating a
// time period ("a.log"), or the rotation period (in our case, one day) is less
// than the time period encoded in the pattern (in case of "a.log.%Y%M" the
// period is one month).
//
// The next table shows the rotated name when the flag controlled by
// 'suppressUniqueFileNameOnRotation' is set to 'true', and (possibly new) name
// of the (new) log file following rotation:
//..
//  Enabled: 'suppressUniqueFileNameOnRotation'
//
//  +----------------+-----------------+----------------+----------------------
//  | Pattern        | Filename Before | Filename After | Rotated Filename
//  |                | Rotation        | Rotation       |
//  +----------------+-----------------+----------------+----------------------
//  | "a.log"        | a.log           | a.log          | none
//  | "a.log.%T"     | a.log.<TS1>     | a.log.<TS2>    | a.log.<TS1>
//  | "a.log.%Y%M"   | a.log.202105    | a.log.202105   | none
//  | "a.log.%Y%M%D" | a.log.20210520  | a.log.20110521 | a.log.20210520
//  +----------------+-----------------+----------------+----------------------
//..
// Note that the original filename is reused when the log pattern does not
// contain %-escape sequences indicating a time period ("a.log"), or the
// rotation period (in our case, one day) is less than the time period encoded
// in the pattern (in case of "a.log.%Y%M" the period is one month).
//
// Also note, that in any cases, when the log pattern includes "%T", or encodes
// a time period that coincides the rotation period (in case of "a.log.%Y%M%D"
// the period is one day), then a unique name on each rotation is produced with
// the (local) time at which file rotation occurred embedded in the filename.
//
///Thread Safety
///-------------
// All methods of 'ball::FileObserver' are thread-safe, and can be called
// concurrently by multiple threads.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example: Basic Usage
/// - - - - - - - - - -
// First, we create a 'ball::LoggerManagerConfiguration' object, 'lmConfig',
// and set the logging "pass-through" level -- the level at which log records
// are published to registered observers -- to 'DEBUG':
//..
//  int main()
//  {
//      ball::LoggerManagerConfiguration lmConfig;
//      lmConfig.setDefaultThresholdLevelsIfValid(ball::Severity::e_DEBUG);
//..
// Next, create a 'ball::LoggerManagerScopedGuard' object whose constructor
// takes the configuration object just created.  The guard will initialize the
// logger manager singleton on creation and destroy the singleton upon
// destruction.  This guarantees that any resources used by the logger manager
// will be properly released when they are not needed:
//..
//      ball::LoggerManagerScopedGuard guard(lmConfig);
//      ball::LoggerManager& manager = ball::LoggerManager::singleton();
//..
// Next, we create a 'ball::FileObserver' object and register it with the
// 'ball' logging system;
//..
//      bsl::shared_ptr<ball::FileObserver> observer =
//                                      bsl::make_shared<ball::FileObserver>();
//      int rc = manager.registerObserver(observer, "default");
//      ASSERT(0 == rc);
//..
// The default format for outputting log records can be changed by calling the
// 'setLogFormat' method.  The statement below outputs record timestamps in ISO
// 8601 format to the log file and in 'bdlt'-style (default) format to
// 'stdout', where timestamps are output with millisecond precision in both
// cases:
//..
//      observer->setLogFormat("%I %p:%t %s %f:%l %c %m\n",
//                             "%d %p:%t %s %f:%l %c %m\n");
//..
// Note that both of the above format specifications omit user fields ('%u') in
// the output.  Also note that, unlike the default, this format does not emit a
// blank line between consecutive log messages.
//
// Henceforth, all messages that are published by the logging system will be
// transmitted to the 'publish' method of 'observer'.  By default, only the
// messages with a 'e_WARN', 'e_ERROR', or 'e_FATAL' severity will be logged to
// 'stdout':
//..
//      BALL_LOG_SET_CATEGORY("main")
//      BALL_LOG_INFO << "Will not be published on 'stdout'.";
//      BALL_LOG_WARN << "This warning *will* be published on 'stdout'.";
//..
// This default can be changed by specifying an optional argument to the
// 'ball::FileObserver' constructor or by calling the 'setStdoutThreshold'
// method:
//..
//      observer->setStdoutThreshold(ball::Severity::e_INFO);
//
//      BALL_LOG_DEBUG << "This debug message is not published on 'stdout'.";
//      BALL_LOG_INFO  << "This info message *will* be published on 'stdout'.";
//      BALL_LOG_WARN  << "This warning will be published on 'stdout'.";
//..
// The user can log all messages to a specified file and specify rotation rules
// based on the size of the log file or its lifetime:
//..
//      // Create and log records to a file named "/var/log/task/task.log".
//      observer->enableFileLogging("/var/log/task/task.log");
//
//      // Disable 'stdout' logging.
//      observer->setStdoutThreshold(ball::Severity::e_OFF);
//
//      // Rotate the file when its size becomes greater than or equal to 256
//      // megabytes.
//      observer->rotateOnSize(1024 * 256);
//
//      // Rotate the file every 24 hours.
//      observer->rotateOnTimeInterval(bdlt::DatetimeInterval(1));
//..
// Note that in this configuration the user may end up with multiple log files
// for any given day (because of the rotation-on-size rule).  This feature can
// be disabled dynamically later:
//..
//      observer->disableSizeRotation();
//      return 0;
//  }
//..

#include <balscm_version.h>

#include <ball_fileobserver2.h>
#include <ball_observer.h>
#include <ball_recordstringformatter.h>
#include <ball_severity.h>

#include <bdlt_datetimeinterval.h>

#include <bslma_allocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_nestedtraitdeclaration.h>

#include <bslmt_mutex.h>

#include <bsls_libraryfeatures.h>

#include <bsl_memory.h>
#include <bsl_string.h>

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
#include <memory_resource>  // 'std::pmr::polymorphic_allocator'
#endif  // BSLS_LIBRARYFEATURES_HAS_CPP17_PMR

#include <string>           // 'std::string', 'std::pmr::string'

namespace BloombergLP {
namespace ball {

class Context;
class Record;

                          // ==================
                          // class FileObserver
                          // ==================

class FileObserver : public Observer {
    // This class implements the 'Observer' protocol.  The 'publish' method of
    // this class outputs the log records that it receives to 'stdout' and
    // optionally to a user-specified file.  This class is thread-safe;
    // different threads can operate on an object concurrently.  This class is
    // exception-neutral with no guarantee of rollback.  In no event is memory
    // leaked.

    // DATA
    RecordStringFormatter d_logFileFormatter;   // record formatter used when
                                                // logging to a file

    RecordStringFormatter d_stdoutFormatter;    // record formatter used when
                                                // logging to 'stdout'

    Severity::Level       d_stdoutThreshold;    // minimum severity for records
                                                // logged to 'stdout'

    bool                  d_useRegularFormatOnStdoutFlag;
                                                // 'true' if records published
                                                // to 'stdout' in regular
                                                // (long) format, otherwise
                                                // short format is used

    bool                  d_publishInLocalTime; // 'true' if timestamps of
                                                // records are output in local
                                                // time, otherwise UTC time

    bool                  d_userFieldsLoggingFlag;
                                                // 'true' if user-defined
                                                // fields published
                                                // (!DEPRECATED!)

    bsl::string           d_stdoutLongFormat;   // initially set to default
                                                // long format for records
                                                // printed to 'stdout'; updated
                                                // by 'setLogFormat'

    bsl::string           d_stdoutShortFormat;  // default short format for
                                                // records printed to 'stdout'

    mutable bslmt::Mutex  d_mutex;              // serialize operations

    FileObserver2         d_fileObserver2;      // forward most operations to
                                                // this object

  private:
    // NOT IMPLEMENTED
    FileObserver(const FileObserver&);
    FileObserver& operator=(const FileObserver&);

  public:
    // TYPES
    typedef FileObserver2::OnFileRotationCallback OnFileRotationCallback;
        // 'OnFileRotationCallback' is an alias for a user-supplied callback
        // function that is invoked after the file observer attempts to rotate
        // its log file.  The callback takes two arguments: (1) an integer
        // status value where 0 indicates a new log file was successfully
        // created and a non-zero value indicates an error occurred during
        // rotation, and (2) a string that provides the name of the rotated log
        // file if the rotation was successful.  E.g.:
        //..
        //  void onLogFileRotation(int                rotationStatus,
        //                         const bsl::string& rotatedLogFileName);
        //..

    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(FileObserver, bslma::UsesBslmaAllocator);

    // CREATORS
    FileObserver();
    explicit FileObserver(bslma::Allocator *basicAllocator);
    explicit FileObserver(Severity::Level   stdoutThreshold,
                          bslma::Allocator *basicAllocator  = 0);
        // Create a file observer that publishes log records to 'stdout' if
        // their severity is at least as severe as the optionally specified
        // 'stdoutThreshold' level, and has file logging initially disabled.
        // If 'stdoutThreshold' is not specified, log records are published to
        // 'stdout' if their severity is at least as severe as
        // 'Severity::e_WARN'.  Optionally specify a 'basicAllocator' used to
        // supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.  Note that 'isPublishInLocalTimeEnabled'
        // returns 'false' following construction indicating that the timestamp
        // attribute of published records will be written in UTC time (see
        // 'enablePublishInLocalTime').  Also note that independent default
        // record formats are in effect for 'stdout' and file logging (see
        // 'setLogFormat').

    FileObserver(Severity::Level   stdoutThreshold,
                 bool              publishInLocalTime,
                 bslma::Allocator *basicAllocator = 0);
        // Create a file observer that publishes log records to 'stdout' if
        // their severity is at least as severe as the specified
        // 'stdoutThreshold' level, and has file logging initially disabled.
        // The timestamp attribute of published records is written in local
        // time if the specified 'publishInLocalTime' flag is 'true', and in
        // UTC time otherwise.  Optionally specify a 'basicAllocator' used to
        // supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.  Note that following construction
        // independent default record formats are in effect for 'stdout' and
        // file logging (see 'setLogFormat').

    ~FileObserver();
        // Close the log file of this file observer if file logging is enabled,
        // and destroy this file observer.

    // MANIPULATORS
    void disableFileLogging();
        // Disable file logging for this file observer.  This method has no
        // effect if file logging is not enabled.  Note that records
        // subsequently received through the 'publish' method of this file
        // observer may still be logged to 'stdout' after calling this method.

    void disableLifetimeRotation();
        // Disable log file rotation based on a periodic time interval for this
        // file observer.  This method has no effect if
        // rotation-on-time-interval is not enabled.
        //
        // !DEPRECATED!: Use 'disableTimeIntervalRotation' instead.

    void disableTimeIntervalRotation();
        // Disable log file rotation based on a periodic time interval for this
        // file observer.  This method has no effect if
        // rotation-on-time-interval is not enabled.

    void disableSizeRotation();
        // Disable log file rotation based on log file size for this file
        // observer.  This method has no effect if rotation-on-size is not
        // enabled.

    void disableStdoutLoggingPrefix();
        // Disable this file observer from using the long output format when
        // logging to 'stdout'.  Henceforth, this file observer will use the
        // default short output format ("\n%s %f:%l %c %m %u\n") when logging
        // to 'stdout'.  This method has no effect if the long output format
        // for 'stdout' logging is not enabled.  Note that this method omits
        // the "%d %p:%t " prefix from the default long output format.

    void disableUserFieldsLogging();
        // Disable the logging of user-defined fields by this file observer.
        // This method has no effect if logging of user-defined fields is not
        // enabled, or if a format string other than the default one is in
        // effect.
        //
        // !DEPRECATED!: Use 'setLogFormat' instead.

    void disablePublishInLocalTime();
        // Disable publishing of the timestamp attribute of records in local
        // time by this file observer; henceforth, timestamps will be in UTC
        // time.  This method has no effect if publishing in local time is not
        // enabled.  Note that this method also affects log filenames (see {Log
        // Filename Patterns}).

    int enableFileLogging(const char *logFilenamePattern);
        // Enable logging of all records published to this file observer to a
        // file whose name is derived from the specified 'logFilenamePattern'.
        // Return 0 on success, a positive value if file logging is already
        // enabled (with no effect), and a negative value otherwise.  The
        // basename of 'logFilenamePattern' may contain '%'-escape sequences
        // that are interpreted as follows:
        //..
        //   %Y - current year   (4 digits with leading zeros)
        //   %M - current month  (2 digits with leading zeros)
        //   %D - current day    (2 digits with leading zeros)
        //   %h - current hour   (2 digits with leading zeros)
        //   %m - current minute (2 digits with leading zeros)
        //   %s - current second (2 digits with leading zeros)
        //   %T - current datetime, equivalent to "%Y%M%D_%h%m%s"
        //   %p - process ID
        //..
        // Each time a log file is opened by this file observer (upon a
        // successful call to this method and following each log file
        // rotation), the name of the new log file is derived from
        // 'logFilenamePattern' by interpreting the above recognized '%'-escape
        // sequences.  If 'isPublishInLocalTimeEnabled' returns 'true', the
        // '%'-escape sequences related to time will be substituted with local
        // time values, and UTC time values otherwise.  See {Log Filename
        // Patterns}.

    int enableFileLogging(const char *logFilenamePattern,
                          bool        appendTimestampFlag);
        // Enable logging of all records published to this file observer to a
        // file whose name is derived from the specified 'logFilenamePattern'
        // and append a timestamp to the log filename if the specified
        // 'appendTimestampFlag' is 'true'.  Return 0 on success, a positive
        // value if file logging is already enabled (with no effect), and a
        // negative value otherwise.  If the 'appendTimestampFlag' is 'true'
        // and 'logFilenamePattern' does not contain any '%'-escape sequences,
        // this method behaves as if ".%T" is appended to 'logFilenamePattern'.
        //
        // !DEPRECATED!: Use 'enableFileLogging(logFilenamePattern)' instead
        // (use the ".%T" pattern to replicate 'true == appendTimestampFlag'
        // behavior).

    void enableStdoutLoggingPrefix();
        // Enable this file observer to use the long output format when logging
        // to 'stdout'.  Henceforth, this file observer will use the output
        // format for 'stdout' logging that was set by the most recent call to
        // 'setLogFormat', or the default long output format
        // ("\n%d %p:%t %s %f:%l %c %m %u\n") if 'setLogFormat' has not yet
        // been called.  This method has no effect if the long output format
        // for 'stdout' logging is already enabled.

    void enableUserFieldsLogging();
        // Enable the logging of user-defined fields by this file observer.
        // This method has no effect if logging of user-defined fields is
        // already enabled, or if a format string other than the default one is
        // in effect.
        //
        // !DEPRECATED!: Use 'setLogFormat' instead.

    void enablePublishInLocalTime();
        // Enable publishing of the timestamp attribute of records in local
        // time by this file observer.  This method has no effect if publishing
        // in local time is already enabled.  Note that this method also
        // affects log filenames (see {Log Filename Patterns}).

    void publish(const Record& record, const Context& context);
        // Process the specified log 'record' having the specified publishing
        // 'context' by writing 'record' and 'context' to the current log file
        // if file logging is enabled for this file observer, and to 'stdout'
        // if the severity of 'record' is at least as severe as the value
        // returned by 'stdoutThreshold'.

    void publish(const bsl::shared_ptr<const Record>& record,
                 const Context&                       context);
        // Process the record referenced by the specified 'record' shared
        // pointer having the specified publishing 'context' by writing the
        // record and 'context' to the current log file if file logging is
        // enabled for this file observer, and to 'stdout' if the severity of
        // 'record' is at least as severe as the value returned by
        // 'stdoutThreshold'.

    void releaseRecords();
        // Discard any shared references to 'Record' objects that were supplied
        // to the 'publish' method, and are held by this observer.  Note that
        // this operation should be called if resources underlying the
        // previously provided shared pointers must be released.

    void forceRotation();
        // Forcefully perform a log file rotation by this file observer.  Close
        // the current log file, rename the log file if necessary, and open a
        // new log file.  This method has no effect if file logging is not
        // enabled.  See {Rotated File Naming} for details on filenames of
        // rotated log files.

    void rotateOnLifetime(const bdlt::DatetimeInterval& timeInterval);
        // Set this file observer to perform a periodic log file rotation at
        // multiples of the specified 'timeInterval'.  The behavior is
        // undefined unless '0 < timeInterval.totalMilliseconds()'.  This rule
        // replaces any rotation-on-time-interval rule currently in effect.
        //
        // !DEPRECATED!: Use 'rotateOnTimeInterval' instead.

    void rotateOnSize(int size);
        // Set this file observer to perform log file rotation when the size of
        // the file exceeds the specified 'size' (in kilobytes).  This rule
        // replaces any rotation-on-size rule currently in effect.  The
        // behavior is undefined unless 'size > 0'.

    void rotateOnTimeInterval(const bdlt::DatetimeInterval& interval);
    void rotateOnTimeInterval(const bdlt::DatetimeInterval& interval,
                              const bdlt::Datetime&         startTime);
        // Set this file observer to perform a periodic log file rotation at
        // multiples of the specified 'interval'.  Optionally specify a
        // 'startTime' indicating the datetime to use as the starting point for
        // computing the periodic rotation schedule.  If
        // 'isPublishInLocalTimeEnabled' is 'true', the 'startTime' is
        // interpreted as local time, and as a UTC time otherwise.  If
        // 'startTime' is not specified, the current time is used.  This rule
        // replaces any rotation-on-time-interval rule currently in effect.
        // The behavior is undefined unless '0 < interval.totalMilliseconds()'.
        // Note that 'startTime' may be a fixed time in the past; e.g., a
        // reference time of 'bdlt::Datetime(1, 1, 1)' and an interval of 24
        // hours would configure a periodic rotation at midnight each day.

    void setOnFileRotationCallback(
                             const OnFileRotationCallback& onRotationCallback);
        // Set the specified 'onRotationCallback' to be invoked after each time
        // this file observer attempts to perform a log file rotation.  The
        // behavior is undefined if the supplied function calls either
        // 'setOnFileRotationCallback', 'forceRotation', or 'publish' on this
        // file observer (i.e., the supplied callback should *not* attempt to
        // write to the 'ball' log).

    void setStdoutThreshold(Severity::Level stdoutThreshold);
        // Set the minimum severity of records logged to 'stdout' by this file
        // observer to the specified 'stdoutThreshold' level.  Note that if the
        // value of 'stdoutThreshold' is 'Severity::e_OFF', logging to 'stdout'
        // is disabled.

    void setLogFormat(const char *logFileFormat, const char *stdoutFormat);
        // Set the format specifications for log records written to the log
        // file and to 'stdout' to the specified 'logFileFormat' and
        // 'stdoutFormat', respectively.  If the default short output format is
        // currently in effect for logging to 'stdout', this method has the
        // effect of calling 'enableStdoutLoggingPrefix' (see that method and
        // 'disableStdoutLoggingPrefix').  See {Log Record Formatting} for
        // details on the syntax of format specifications.  Note that default
        // formats are in effect following construction until this method is
        // called ("\n%d %p:%t %s %f:%l %c %m %u\n" for both file and 'stdout'
        // logging).  Also note that the observer emits newline characters at
        // the beginning and at the end of a log record by default, so the user
        // needs to add them explicitly to the format string to preserve this
        // behavior.

    void suppressUniqueFileNameOnRotation(bool suppress);
        // Suppress generating a unique log file name upon rotation if the
        // specified 'suppress' is 'true', and generate a unique filename
        // otherwise.  See {Rotated File Naming} for details.

    // ACCESSORS
    bslma::Allocator *allocator() const;
        // Return the memory allocator used by this object.

    void getLogFormat(const char **logFileFormat,
                      const char **stdoutFormat) const;
        // Load the format specification for log records written by this file
        // observer to the log file into the specified '*logFileFormat' address
        // and the format specification for log records written to 'stdout'
        // into the specified '*stdoutFormat' address.  See {Log Record
        // Formatting} for details on the syntax of format specifications.

    bool isFileLoggingEnabled() const;
    bool isFileLoggingEnabled(bsl::string *result) const;
    bool isFileLoggingEnabled(std::string *result) const;
        // Return 'true' if file logging is enabled for this file observer, and
        // 'false' otherwise.  Load the optionally specified 'result' with the
        // name of the current log file if file logging is enabled, and leave
        // 'result' unmodified otherwise.  Note that records received through
        // the 'publish' method of this file observer may still be logged to
        // 'stdout' when this method returns 'false'.

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
    bool isFileLoggingEnabled(std::pmr::string *result) const;
        // Return 'true' if file logging is enabled for this file observer, and
        // 'false' otherwise.  Load the specified 'result' with the name of the
        // current log file if file logging is enabled, and leave 'result'
        // unmodified otherwise.  Note that records received through the
        // 'publish' method of this file observer may still be logged to
        // 'stdout' when this method returns 'false'.
#endif  // BSLS_LIBRARYFEATURES_HAS_CPP17_PMR

    bool isStdoutLoggingPrefixEnabled() const;
        // Return 'true' if this file observer uses the long output format when
        // writing to 'stdout', and 'false' otherwise (in which case the
        // default short output format is used).  See
        // 'enableStdoutLoggingPrefix' and 'disableStdoutLoggingPrefix'.

    bool isUserFieldsLoggingEnabled() const;
        // Return 'true' if the logging of user-defined fields is enabled for
        // this file observer, and 'false' otherwise.
        //
        // !DEPRECATED!: Do not use.

    bool isPublishInLocalTimeEnabled() const;
        // Return 'true' if this file observer writes the timestamp attribute
        // of records that it publishes in local time, and 'false' otherwise
        // (in which case timestamps are written in UTC time).  Note that the
        // value returned by this method also affects log filenames (see {Log
        // Filename Patterns}).

    bool isSuppressUniqueFileNameOnRotation() const;
        // Return 'true' if the log filename uniqueness check on rotation is
        // suppressed, and false otherwise.

    bdlt::DatetimeInterval localTimeOffset() const;
        // Return the difference between the local time and UTC time in effect
        // when this file observer was constructed.  Note that this value
        // remains unchanged during the lifetime of this object and therefore
        // may become incorrect when the difference between the local time and
        // UTC time changes (e.g., when transitioning into or out of daylight
        // savings time).
        //
        // !DEPRECATED!: Use 'bdlt::LocalTimeOffset' instead.

    bdlt::DatetimeInterval rotationLifetime() const;
        // Return the lifetime of the log file that will trigger a file
        // rotation by this file observer if rotation-on-lifetime is in effect,
        // and a 0 time interval otherwise.

    int rotationSize() const;
        // Return the size (in kilobytes) of the log file that will trigger a
        // file rotation by this file observer if rotation-on-size is in
        // effect, and 0 otherwise.

    Severity::Level stdoutThreshold() const;
        // Return the minimum severity of records that will be logged to
        // 'stdout' by this file observer.  Note that records with a threshold
        // less severe than 'stdoutThreshold()' may still be output to the log
        // file if file logging is enabled.
};

// ============================================================================
//                              INLINE DEFINITIONS
// ============================================================================

                          // ------------------
                          // class FileObserver
                          // ------------------

// MANIPULATORS
inline
void FileObserver::disableFileLogging()
{
    d_fileObserver2.disableFileLogging();
}

inline
void FileObserver::disableLifetimeRotation()
{
    d_fileObserver2.disableTimeIntervalRotation();
}

inline
void FileObserver::disableSizeRotation()
{
    d_fileObserver2.disableSizeRotation();
}

inline
void FileObserver::disableTimeIntervalRotation()
{
    d_fileObserver2.disableTimeIntervalRotation();
}

inline
int FileObserver::enableFileLogging(const char *logFilenamePattern)
{
    return d_fileObserver2.enableFileLogging(logFilenamePattern);
}

inline
int FileObserver::enableFileLogging(const char *logFilenamePattern,
                                    bool        appendTimestampFlag)
{
    return d_fileObserver2.enableFileLogging(logFilenamePattern,
                                             appendTimestampFlag);
}

inline
void FileObserver::forceRotation()
{
    d_fileObserver2.forceRotation();
}

inline
void FileObserver::publish(const bsl::shared_ptr<const Record>& record,
                           const Context&                       context)
{
    publish(*record, context);
}

inline
void FileObserver::releaseRecords()
{
}

inline
void FileObserver::rotateOnLifetime(const bdlt::DatetimeInterval& timeInterval)
{
    d_fileObserver2.rotateOnTimeInterval(timeInterval);
}

inline
void FileObserver::rotateOnSize(int size)
{
    d_fileObserver2.rotateOnSize(size);
}

inline
void FileObserver::rotateOnTimeInterval(const bdlt::DatetimeInterval& interval)
{
    d_fileObserver2.rotateOnTimeInterval(interval);
}

inline
void FileObserver::rotateOnTimeInterval(
                                       const bdlt::DatetimeInterval& interval,
                                       const bdlt::Datetime&         startTime)
{
    d_fileObserver2.rotateOnTimeInterval(interval, startTime);
}

inline
void FileObserver::setOnFileRotationCallback(
                              const OnFileRotationCallback& onRotationCallback)
{
    d_fileObserver2.setOnFileRotationCallback(onRotationCallback);
}

inline
void FileObserver::suppressUniqueFileNameOnRotation(bool suppress)
{
    d_fileObserver2.suppressUniqueFileNameOnRotation(suppress);
}

// ACCESSORS
inline
bool FileObserver::isFileLoggingEnabled() const
{
    return d_fileObserver2.isFileLoggingEnabled();
}

inline
bool FileObserver::isFileLoggingEnabled(bsl::string *result) const
{
    return d_fileObserver2.isFileLoggingEnabled(result);
}

inline
bool FileObserver::isFileLoggingEnabled(std::string *result) const
{
    return d_fileObserver2.isFileLoggingEnabled(result);
}

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
inline
bool FileObserver::isFileLoggingEnabled(std::pmr::string *result) const
{
    return d_fileObserver2.isFileLoggingEnabled(result);
}
#endif  //BSLS_LIBRARYFEATURES_HAS_CPP17_PMR

inline
bool FileObserver::isSuppressUniqueFileNameOnRotation() const
{
    return d_fileObserver2.isSuppressUniqueFileNameOnRotation();
}

inline
bdlt::DatetimeInterval FileObserver::localTimeOffset() const
{
    return d_fileObserver2.localTimeOffset();
}

inline
bdlt::DatetimeInterval FileObserver::rotationLifetime() const
{
    return d_fileObserver2.rotationLifetime();
}

inline
int FileObserver::rotationSize() const
{
    return d_fileObserver2.rotationSize();
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 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 ----------------------------------