// ball_recordattributes.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_RECORDATTRIBUTES
#define INCLUDED_BALL_RECORDATTRIBUTES

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

//@PURPOSE: Provide a container for a fixed set of fields suitable for logging.
//
//@CLASSES:
//  ball::RecordAttributes: container for a fixed set of log fields
//
//@SEE_ALSO: ball_record
//
//@DESCRIPTION: This component defines a container for aggregating a fixed set
// of fields intrinsically appropriate for logging.  Using
// 'ball::RecordAttributes', a logger can transmit log message text together
// with relevant auxiliary information (e.g., timestamp, filename, line number,
// etc.) as a single instance, rather than passing around individual attributes
// separately.
//
// The attributes held by 'ball::RecordAttributes' are given in the following
// table:
//..
//     Attribute        Type               Description               Default
//     ----------   -------------   ------------------------------   --------
//     timestamp    bdlt::Datetime  creation date and time           (*Note*)
//     processID    int             process id of creator            0
//     threadID     Uint64          thread id of creator             0
//     fileName     string          file where created  (__FILE__)   ""
//     lineNumber   int             line number in file (__LINE__)   0
//     category     string          category of logged record        ""
//     severity     int             severity of logged record        0
//     message      string          log message text                 ""
//..
// *Note*: The default value given to the timestamp attribute is implementation
// defined.  (See the 'bdlt_datetime' component-level documentation for more
// information.)
//
// For each attribute, there is a method to access its value and a method to
// change its value.  E.g., for the timestamp attribute, there is the
// 'timestamp' accessor and the 'setTimestamp' manipulator.  Note, that for the
// message attribute, there is 'message' accessor which is *deprecated*, use
// the 'messageRef' accessor instead.  The class also provides the ability to
// stream an object (whose class must support the 'operator<<') into the
// message attribute using 'messageStreamBuf' method (see the usage example-2).
// The default values listed in the table above are the values given to the
// respective attributes by the default constructor of
// 'ball::RecordAttributes'.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Syntax
///- - - - - - - - -
// The 'ball::RecordAttributes' class holds sufficient information on which to
// base a rudimentary logging, tracing, or reporting facility.  The following
// code fragments illustrate the essentials of working with these attributes.
//
// Assume that our example is part of a financial application using categories
// and message severities as follows:
//..
//      const char *Category[] = { "Bonds", "Equities", "Futures" };
//      enum { INFO, WARN, BUY, SELL };
//..
// First define a 'ball::RecordAttributes' object with each attribute
// initialized to its default value:
//..
//      ball::RecordAttributes attributes;
//..
// Next set each of the attributes to some meaningful value:
//..
//      bdlt::Datetime now;
//      bdlt::EpochUtil::convertFromTimeT(&now, time(0));
//      attributes.setTimestamp(now);                // current time
//      attributes.setProcessID(getpid());
//      attributes.setThreadID((bsls::Types::Uint64) pthread_self());
//      attributes.setFileName(__FILE__);
//      attributes.setLineNumber(__LINE__);
//      attributes.setCategory(Category[2]);         // "Futures"
//      attributes.setSeverity(WARN);
//      attributes.setMessage("sugar up (locust infestations on the rise)");
//..
// The message in this example briefly informs that something interesting may
// be happening with respect to sugar futures.  In general, the message
// attribute can contain an arbitrary amount of information.
//
// Now that the sample 'ball::RecordAttributes' object has been populated with
// the desired information, it can be passed to a function, stored in a
// database, cached in a container of 'ball::RecordAttributes' objects, etc.
// For the purposes of this illustration, we'll simply format and stream
// selected attributes to a specified 'ostream' using the following function:
//..
//      void printMessage(ostream&                      stream,
//                        const ball::RecordAttributes& attributes)
//      {
//          using namespace std;
//          stream << "\tTimestamp: " << attributes.timestamp()  << endl;
//          stream << "\tCategory:  " << attributes.category()   << endl;
//          stream << "\tMessage:   " << attributes.messageRef() << endl;
//          stream << endl;
//      }
//..
// The following call:
//..
//      printMessage(bsl::cout, attributes);
//..
// prints these attributes to 'stdout':
//..
//              Timestamp: 19JAN2004_23:07:38.000
//              Category:  Futures
//              Message:   sugar up (locust infestations on the rise)
//..
//
///Example 2: Streaming Data Into a Message Attribute
/// - - - - - - - - - - - - - - - - - - - - - - - - -
// Following example demonstrates how an object of a class supporting 'ostream'
// operation ('operator<<') can be streamed into the message attribute.
// Suppose we want to stream objects of the following class.
//..
//    class Information
//    {
//      private:
//        bsl::string d_heading;
//        bsl::string d_contents;
//
//      public:
//        Information(const char *heading, const char *contents);
//        const bsl::string& heading() const;
//        const bsl::string& contents() const;
//    };
//..
// The component containing the 'Information' must provide 'operator<<'.  Here
// is a possible implementation.
//..
//    bsl::ostream& operator<<(bsl::ostream&      stream,
//                             const Information& information)
//    {
//        stream << information.heading() << endl;
//        stream << '\t';
//        stream << information.contents() << endl;
//        return stream;
//    }
//..
// The following function streams an 'Information' object into the message
// attribute of a 'ball::RecordAttributes' object.
//..
//    void streamInformationIntoMessageAttribute(
//                                         ball::RecordAttributes& attributes,
//                                         const Information&      information)
//    {
//        // First clear the message attributes.
//        attributes.clearMessage();
//
//        // Create an 'ostream' from message stream buffer.
//        bsl::ostream os(&attributes.messageStreamBuf());
//
//        // Now stream the information object into the created ostream.
//        // This will set the message attribute of 'attributes' to the
//        // streamed contents.
//        os << information;
//    }
//..

#include <balscm_version.h>

#include <bdlsb_memoutstreambuf.h>

#include <bdlt_datetime.h>

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

#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_performancehint.h>
#include <bsls_platform.h>
#include <bsls_types.h>

#ifdef BSLS_PLATFORM_OS_WINDOWS
#include <bsl_ostream.h>

#else
#include <bsl_iosfwd.h>
#endif
#include <bsl_string.h>

namespace BloombergLP {
namespace ball {

                        // ======================
                        // class RecordAttributes
                        // ======================

class RecordAttributes {
    // This class provides a container for a fixed set of attributes
    // appropriate for logging.  For each attribute in this class (e.g.,
    // 'category'), there is an accessor for obtaining the attribute's value
    // (the 'category' accessor) and a manipulator for changing the attribute's
    // value (the 'setCategory' manipulator).
    //
    // Additionally, this class supports a complete set of *value* *semantic*
    // operations, including copy construction, assignment and equality
    // comparison, and 'ostream' printing.  A precise operational definition of
    // when two instances have the same value can be found in the description
    // of 'operator==' for the class.  This class is *exception* *neutral* with
    // no guarantee of rollback: If an exception is thrown during the
    // invocation of a method on a pre-existing instance, the object is left in
    // a valid state, but its value is undefined.  In no event is memory
    // leaked.  Finally, *aliasing* (e.g., using all or part of an object as
    // both source and destination) is supported in all cases.

    // PRIVATE TYPES
    typedef bsls::Types::Uint64 Uint64;

    // PRIVATE CONSTANTS
    enum {
        k_RESET_MESSAGE_STREAM_CAPACITY = 256  // maximum capacity above which
                                               // the message stream is reset
                                               // (and not rewound)
    };

    // DATA
    bdlt::Datetime   d_timestamp;    // creation date and time
    int              d_processID;    // process id of creator
    Uint64           d_threadID;     // thread id of creator
    bsl::string      d_fileName;     // name of file where created (__FILE__)
    int              d_lineNumber;   // line number of said file   (__LINE__)
    bsl::string      d_category;     // category of log record
    int              d_severity;     // severity of log record

    bdlsb::MemOutStreamBuf d_messageStreamBuf;  // stream buffer associated
                                                // with the message attribute

    // FRIENDS
    friend bool operator==(const RecordAttributes&, const RecordAttributes&);

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(RecordAttributes,
                                   bslma::UsesBslmaAllocator);

    // CREATORS
    explicit RecordAttributes(bslma::Allocator *basicAllocator = 0);
        // Create a record attributes object with all attributes having default
        // values.  Optionally specify a 'basicAllocator' used to supply
        // memory.  If 'basicAllocator' is 0, the currently installed default
        // allocator is used.

    RecordAttributes(const bdlt::Datetime&  timestamp,
                     int                    processID,
                     bsls::Types::Uint64    threadID,
                     const char            *fileName,
                     int                    lineNumber,
                     const char            *category,
                     int                    severity,
                     const char            *message,
                     bslma::Allocator      *basicAllocator = 0);
        // Create a record attributes object having the specified 'timestamp',
        // 'processID', 'threadID', 'fileName', 'lineNumber', 'category',
        // 'severity' and 'message' values, respectively.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.  The behavior is
        // undefined if any 'const char *' argument is null.

    RecordAttributes(const RecordAttributes&  original,
                     bslma::Allocator        *basicAllocator = 0);
        // Create a record attributes object having the value of the specified
        // 'original' record attributes object.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.

    //! ~RecordAttributes() = default;
        // Destroy this record attributes object.

    // MANIPULATORS
    RecordAttributes& operator=(const RecordAttributes& rhs);
        // Assign to this record attributes object the value of the specified
        // 'rhs' record attributes object.

    void clearMessage();
        // Set the message attribute of this record attributes object to the
        // empty string.

    bdlsb::MemOutStreamBuf& messageStreamBuf();
        // Return a reference to the modifiable stream buffer associated with
        // the message attribute of this record attributes object.

    void setCategory(const char *category);
        // Set the category attribute of this record attributes object to the
        // specified (non-null) 'category'.

    void setFileName(const char *fileName);
        // Set the filename attribute of this record attributes object to the
        // specified (non-null) 'fileName'.

    void setLineNumber(int lineNumber);
        // Set the line number attribute of this record attributes object to
        // the specified 'lineNumber'.

    void setMessage(const char *message);
        // Set the message attribute of this record attributes object to the
        // specified (non-null) 'message'.

    void setProcessID(int processID);
        // Set the processID attribute of this record attributes object to the
        // specified 'processID'.

    void setSeverity(int severity);
        // Set the severity attribute of this record attributes object to the
        // specified 'severity'.

    void setThreadID(bsls::Types::Uint64 threadID);
        // Set the threadID attribute of this record attributes object to the
        // specified 'threadID'.

    void setTimestamp(const bdlt::Datetime& timestamp);
        // Set the timestamp attribute of this record attributes object to the
        // specified 'timestamp'.

    // ACCESSORS
    const char *category() const;
        // Return the category attribute of this record attributes object.

    const char *fileName() const;
        // Return the filename attribute of this record attributes object.

    int lineNumber() const;
        // Return the line number attribute of this record attributes object.

    const char *message() const;
        // Return the message attribute of this record attributes object.  Note
        // that this method will return a truncated message if it contains
        // embedded null ('\0') characters; see 'messageRef' for an alternative
        // to this method.  **Warning:** This method is *not* const thread-safe,
        // and cannot be safely called concurrently.
        //
        // !DEPRECATED!: Use 'messageRef' instead.

    bslstl::StringRef messageRef() const;
        // Return a string reference providing non-modifiable access to the
        // message attribute of this record attributes object.  Note that the
        // returned string reference is not null-terminated, and may contain
        // null ('\0') characters.

    int processID() const;
        // Return the processID attribute of this record attributes object.

    int severity() const;
        // Return the severity attribute of this record attributes object.

    bsls::Types::Uint64 threadID() const;
        // Return the threadID attribute of this record attributes object.

    const bdlt::Datetime& timestamp() const;
        // Return the timestamp attribute of this record attributes object.

    const bdlsb::MemOutStreamBuf& messageStreamBuf() const;
        // Return a reference to the non-modifiable stream buffer associated
        // with the message attribute of this record attributes object.

    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
        // Format this object to the specified output 'stream' at the
        // optionally specified indentation 'level' and return a reference to
        // the modifiable 'stream'.  If 'level' is specified, optionally
        // specify 'spacesPerLevel', the number of spaces per indentation level
        // for this and all of its nested objects.  Each line is indented by
        // the absolute value of 'level * spacesPerLevel'.  If 'level' is
        // negative, suppress indentation of the first line.  If
        // 'spacesPerLevel' is negative, suppress line breaks and format the
        // entire output on one line.  If 'stream' is initially invalid, this
        // operation has no effect.

};

// FREE OPERATORS
bool operator==(const RecordAttributes& lhs, const RecordAttributes& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' record attributes objects
    // have the same value, and 'false' otherwise.  Two record attributes
    // objects have the same value if each respective pair of attributes have
    // the same value.

inline
bool operator!=(const RecordAttributes& lhs, const RecordAttributes& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' record attributes objects
    // do not have the same value, and 'false' otherwise.  Two record
    // attributes objects do not have the same value if one or more respective
    // attributes differ in value.

inline
bsl::ostream& operator<<(bsl::ostream& stream, const RecordAttributes& object);
    // Format the members of the specified 'object' to the specified output
    // 'stream' and return a reference to the modifiable 'stream'.

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

                        // ----------------------
                        // class RecordAttributes
                        // ----------------------

// MANIPULATORS
inline
void RecordAttributes::clearMessage()
{
    // Note that the stream buffer holding the message attribute has initial
    // capacity of 256 bytes (by implementation).  Reset those stream buffers
    // that are bigger than the default and "rewind" those that are smaller or
    // equal.
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(
            k_RESET_MESSAGE_STREAM_CAPACITY < d_messageStreamBuf.capacity())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        d_messageStreamBuf.reset();
    }
    else {
        d_messageStreamBuf.pubseekpos(0);
    }
}

inline
bdlsb::MemOutStreamBuf& RecordAttributes::messageStreamBuf()
{
    return d_messageStreamBuf;
}

inline
void RecordAttributes::setCategory(const char *category)
{
    d_category = category;
}

inline
void RecordAttributes::setFileName(const char *fileName)
{
    d_fileName = fileName;
}

inline
void RecordAttributes::setLineNumber(int lineNumber)
{
    d_lineNumber = lineNumber;
}

inline
void RecordAttributes::setProcessID(int processID)
{
    d_processID = processID;
}

inline
void RecordAttributes::setSeverity(int severity)
{
    d_severity = severity;
}

inline
void RecordAttributes::setThreadID(bsls::Types::Uint64 threadID)
{
    d_threadID = threadID;
}

inline
void RecordAttributes::setTimestamp(const bdlt::Datetime& timestamp)
{
    d_timestamp = timestamp;
}

// ACCESSORS
inline
const char *RecordAttributes::category() const
{
    return d_category.c_str();
}

inline
const char *RecordAttributes::fileName() const
{
    return d_fileName.c_str();
}

inline
int RecordAttributes::lineNumber() const
{
    return d_lineNumber;
}

inline
int RecordAttributes::processID() const
{
    return d_processID;
}

inline
int RecordAttributes::severity() const
{
    return d_severity;
}

inline
bsls::Types::Uint64 RecordAttributes::threadID() const
{
    return d_threadID;
}

inline
const bdlsb::MemOutStreamBuf& RecordAttributes::messageStreamBuf() const
{
    return d_messageStreamBuf;
}

inline
const bdlt::Datetime& RecordAttributes::timestamp() const
{
    return d_timestamp;
}

}  // close package namespace

// FREE OPERATORS
inline
bool ball::operator!=(const RecordAttributes& lhs, const RecordAttributes& rhs)
{
    return !(lhs == rhs);
}

inline
bsl::ostream& ball::operator<<(bsl::ostream&           stream,
                               const RecordAttributes& object)
{
    return object.print(stream, 0, -1);
}

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