// ball_record.h                                                      -*-C++-*-
#ifndef INCLUDED_BALL_RECORD
#define INCLUDED_BALL_RECORD

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

//@PURPOSE: Provide a container for the fields and attributes of a log record.
//
//@CLASSES:
//  ball::Record: container for a log record's fields and attributes
//
//@SEE_ALSO: ball_recordattributes, ball_logger
//
//@DESCRIPTION: This component provides a single, unconstrained
// (value-semantic) attribute class, 'ball::Record', that is used to describe
// the properties of a logged message.
//
///Attributes
///----------
//..
//  Name                Type
//  ------------------  -----------------------------------
//  fixedFields         ball::RecordAttributes
//  userFields          ball::UserFields
//  attributes          bsl::vector<ball::ManagedAttribute>
//..
//: o 'fixedFields': mandatory log fields including timestamp, location,
//:   severity, process id, and the log message.
//:
//: o 'userFields': user-managed fields associated with a log record.  Note
//:    that use of these fields is deprecated and superseded by 'attributes'.
//:
//: o 'attributes': user-managed name/value pairs associated with a log record.
//
// 'ball::Record' aggregates a set of fixed fields and various user-defined
// fields and attributes into one record type, useful for transmitting a
// customized log record as a single instance rather than passing around
// individual attributes separately.  Note that this class is a pure attribute
// class with no constraints, other than the total memory required for the
// class.  Also note that this class is not thread-safe.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Use of 'ball::Record'
/// - - - - - - - - - - - - - - - - - - -
// The following example demonstrates how to create and set the properties of
// a 'ball::Record'.  Note that users of the 'ball' logging subsystem are not
// expected to create records directly.
//
// First we default create a 'ball::Record', 'record', and verify it has a
// default set of attributes:
//..
//  ball::Record record;
//
//  assert(ball::RecordAttributes() == record.fixedFields());
//  assert(0                        == record.customFields().length());
//..
// Then, we set the fixed fields of the record to contain a simple message:
//..
//  int                 processId = bdlsu::ProcessUtil::getProcessId();
//  bsls::Types::Uint64 threadId  = bslmt::ThreadUtil::selfIdAsUint64();
//
//  ball::RecordAttributes attributes(bdlt::CurrentTime::utc(), // time stamp
//                                    processId,                // process id
//                                    threadId,                 // thread id
//                                    __FILE__,                 // filename
//                                    __LINE__,                 // line number
//                                    "ExampleCategory",        // category
//                                    ball::Severity::e_WARN,   // severity
//                                    "Simple Test Message");   // message
//  record.setFixedFields(attributes);
//
//  assert(attributes == record.fixedFields());
//..
// Next, we add an additional attribute to the log record:
//..
//  record.addAttribute(ball::Attribute("myLib.name", "John Smith"));
//..
// Finally, we write the record to a stream:
//..
//  bsl::ostringstream output;
//  output << record << bsl::endl;
//..

#include <balscm_version.h>

#include <ball_countingallocator.h>
#include <ball_managedattribute.h>
#include <ball_recordattributes.h>
#include <ball_userfields.h>

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

#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_alignment.h>
#include <bsls_assert.h>

#include <bsl_iosfwd.h>
#include <bsl_vector.h>

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bslalg_typetraits.h>
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

namespace BloombergLP {
namespace ball {

                           // ============
                           // class Record
                           // ============

class Record {
    // This class provides a container for a set of fields that are appropriate
    // for a user-configurable log record.  The class contains a
    // 'RecordAttributes' object that in turn holds a fixed set of fields, a
    // 'ball::UserFields' object that holds a set of optional, user-defined
    // fields, and a set of attributes associated with this log record.  For
    // each of these three sub-containers there is an accessor for obtaining
    // the container value and a manipulator for changing that value.
    //
    // 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:
    // DATA
    CountingAllocator  d_allocator;     // memory allocator

    RecordAttributes   d_fixedFields;   // bytes used by fixed fields

    UserFields         d_userFields;    // bytes used by user fields

    bsl::vector<ManagedAttribute>
                       d_attributes;    // managed attributes

    bslma::Allocator  *d_allocator_p;   // allocator used to supply memory;
                                        // held but not own

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

  public:
    // CLASS METHODS
    static void deleteObject(const Record *object);
        // Destroy the specified '*object' and use the allocator held by
        // '*object' to deallocate its memory footprint.  The behavior is
        // undefined unless 'object' is the address of a valid log record.

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

    // CREATORS
    explicit Record(bslma::Allocator *basicAllocator = 0);
        // Create a log record having default values for its fixed fields and
        // its user-defined fields.  Optionally specify a 'basicAllocator' used
        // to supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.

    Record(const RecordAttributes&  fixedFields,
           const UserFields&        userFields,
           bslma::Allocator        *basicAllocator = 0);
        // Create a log record with fixed fields having the value of the
        // specified 'fixedFields' and user-defined fields having the value of
        // the specified 'userFields'.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the currently
        // installed default allocator is used.

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

    //! ~Record() = default;
        // Destroy this log record.

    // MANIPULATORS
    Record& operator=(const Record& rhs);
        // Assign to this log record the value of the specified 'rhs' log
        // record and return the reference to this modifiable record.

    void clear();
        // Clear this log record by removing the user fields, attributes, and
        // clearing the fixed field's message buffer.  Note that this method is
        // tailored for efficient memory use within the 'ball' logging system.

    void addAttribute(const ball::Attribute& attribute);
        // Add a managed copy of the specified 'attribute' to the container of
        // attributes maintained by this log record.

    RecordAttributes& fixedFields();
        // Return the modifiable fixed fields of this log record.

    void setFixedFields(const RecordAttributes& fixedFields);
        // Set the fixed fields of this log record to the value of the
        // specified 'fixedFields'.

    void setCustomFields(const ball::UserFields& userFields);
        // !DEPRECATED!: Use log record attributes.
        // Set the custom user-defined fields of this log record to the value
        // of the specified 'userFields'.

    ball::UserFields& customFields();
        // !DEPRECATED!: Use log record attributes.
        // Return a reference providing modifiable access to the custom
        // user-defined fields of this log record.

    // ACCESSORS
    const RecordAttributes& fixedFields() const;
        // Return the non-modifiable fixed fields of this log record.

    const ball::UserFields& customFields() const;
        // !DEPRECATED!: Use log record attributes.
        // Return a reference providing non-modifiable access to the custom
        // user-defined fields of this log record.

    const bsl::vector<ball::ManagedAttribute>& attributes() const;
        // Return a reference providing non-modifiable access to the attributes
        // of this log record.

    int numAllocatedBytes() const;
        // Return the total number of bytes of dynamic memory allocated by
        // this log record object.  Note that this value does not include
        // 'sizeof *this'.

    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 Record& lhs, const Record& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' log records have the same
    // value, and 'false' otherwise.  Two log records have the same value if
    // the respective fixed fields have the same value and the respective
    // user-defined fields have the same value.

bool operator!=(const Record& lhs, const Record& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' log records do not have
    // the same value, and 'false' otherwise.  Two log records do not have the
    // same value if either the respective fixed fields or user-defined fields
    // do not have the same value.

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

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

                           // ------------
                           // class Record
                           // ------------

// CLASS METHODS
inline
void Record::deleteObject(const Record *object)
{
    object->d_allocator_p->deleteObjectRaw(object);
}

// CREATORS
inline
Record::Record(bslma::Allocator *basicAllocator)
: d_allocator(basicAllocator)
, d_fixedFields(&d_allocator)
, d_userFields(&d_allocator)
, d_attributes(&d_allocator)
, d_allocator_p(bslma::Default::allocator(basicAllocator))
{
}

inline
Record::Record(const RecordAttributes&  fixedFields,
               const ball::UserFields&  userFields,
               bslma::Allocator        *basicAllocator)
: d_allocator(basicAllocator)
, d_fixedFields(fixedFields, &d_allocator)
, d_userFields(userFields, &d_allocator)
, d_attributes(&d_allocator)
, d_allocator_p(bslma::Default::allocator(basicAllocator))
{
}

inline
Record::Record(const Record& original, bslma::Allocator *basicAllocator)
: d_allocator(basicAllocator)
, d_fixedFields(original.d_fixedFields, &d_allocator)
, d_userFields(original.d_userFields, &d_allocator)
, d_attributes(original.d_attributes, &d_allocator)
, d_allocator_p(bslma::Default::allocator(basicAllocator))
{
}

// MANIPULATORS
inline
Record& Record::operator=(const Record& rhs)
{
    if (this != &rhs) {
        d_fixedFields  = rhs.d_fixedFields;
        d_userFields   = rhs.d_userFields;
        d_attributes   = rhs.d_attributes;
    }
    return *this;
}

inline
void Record::addAttribute(const Attribute& attribute)
{
    d_attributes.push_back(ManagedAttribute(attribute));
}

inline
void Record::clear()
{
    fixedFields().clearMessage();
    customFields().removeAll();
    d_attributes.clear();
}

inline
RecordAttributes& Record::fixedFields()
{
    return d_fixedFields;
}

inline
void Record::setFixedFields(const RecordAttributes& fixedFields)
{
    d_fixedFields = fixedFields;
}

inline
void Record::setCustomFields(const ball::UserFields& userFields)
{
    d_userFields = userFields;
}

inline
ball::UserFields& Record::customFields()
{
    return d_userFields;
}

// ACCESSORS
inline
const RecordAttributes& Record::fixedFields() const
{
    return d_fixedFields;
}

inline
const ball::UserFields& Record::customFields() const
{
    return d_userFields;
}

inline
const bsl::vector<ball::ManagedAttribute>& Record::attributes() const
{
    return d_attributes;
}

inline
int Record::numAllocatedBytes() const
{
    return static_cast<int>(d_allocator.numBytesTotal());
}

}  // close package namespace

// FREE OPERATORS
inline
bool ball::operator==(const Record& lhs, const Record& rhs)
{
    return lhs.d_fixedFields  == rhs.d_fixedFields
        && lhs.d_userFields   == rhs.d_userFields
        && lhs.d_attributes   == rhs.d_attributes;
}

inline
bool ball::operator!=(const Record& lhs, const Record& rhs)
{
    return !(lhs == rhs);
}

inline
bsl::ostream& ball::operator<<(bsl::ostream& stream, const Record& record)
{
    return record.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 ----------------------------------