// balm_collector.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_BALM_COLLECTOR
#define INCLUDED_BALM_COLLECTOR

#include <bsls_ident.h>
BSLS_IDENT("$Id: balm_collector.h,v 1.7 2008/04/16 20:00:49 hversche Exp $")

//@PURPOSE: Provide a container for collecting and aggregating metric values.
//
//@CLASSES:
//   balm::Collector: a container for collecting and aggregating metric values
//
//@SEE_ALSO: balm_collectorrepository, balm_metric
//
//@DESCRIPTION: This component provides a class for collecting and aggregating
// the values of a metric.  The collector records the number of times an event
// occurs as well as an associated measurement value.  This component does
// *not* define what constitutes an event or what the value measures.  The
// collector manages, in a thread-safe manner, the count of event occurrences
// and the aggregated minimum, maximum, and total of the measured metric
// value.  This collector class provides operations to update the aggregated
// value, a 'load' operator to populate a 'balm::MetricRecord' with the current
// state of the collector, a 'reset' method to reset the current state of the
// collector, and finally a combined 'loadAndReset' method that performs both
// a load and a reset as an atomic operation.  Note that in practice, most
// clients should not need to access a 'balm::Collector' directly, but instead
// use it through another type (see 'balm_metric').
//
///Alternative Systems for Telemetry
///---------------------------------
// Bloomberg software may alternatively use the GUTS telemetry API, which is
// integrated into Bloomberg infrastructure.
//
///Thread Safety
///-------------
// 'balm::Collector' is fully *thread-safe*, meaning that all non-creator
// operations on a given instance can be safely invoked simultaneously from
// multiple threads.
//
///Usage
///-----
// The following example creates a 'balm::Collector', modifies its values, then
// collects a 'balm::MetricRecord'.
//
// We start by creating a 'balm::MetricId' object by hand, but in practice, an
// id should be obtained from a 'balm::MetricRegistry' object (such as the one
// owned by a 'balm::MetricsManager'):
//..
//  balm::Category           myCategory("MyCategory");
//  balm::MetricDescription  description(&myCategory, "MyMetric");
//  balm::MetricId           myMetric(&description);
//..
// Now we create a 'balm::Collector' object for 'myMetric' and use the 'update'
// method to update its collected value:
//..
//  balm::Collector collector(myMetric);
//
//  collector.update(1.0);
//  collector.update(3.0);
//..
// The collector accumulated the values 1 and 3.  The result should have a
// count of 2, a total of 4 (3 + 1), a max of 3 (max(3, 1)), and a min of 1
// (min(3, 1)).
//..
//  balm::MetricRecord record;
//  collector.loadAndReset(&record);
//
//      assert(myMetric == record.metricId());
//      assert(2        == record.count());
//      assert(4        == record.total());
//      assert(1.0      == record.min());
//      assert(3.0      == record.max());
//..

#include <balscm_version.h>

#include <balm_metricrecord.h>
#include <balm_metricid.h>

#include <bslmt_mutex.h>
#include <bslmt_lockguard.h>

#include <bsl_algorithm.h>

namespace BloombergLP {


namespace balm {

                              // ===============
                              // class Collector
                              // ===============

class Collector {
    // This class provides a mechanism for collecting and aggregating the
    // value of a metric over a period of time.  The collector contains a
    // 'MetricRecord' object that holds the identity of the metric being
    // collected, the number of times an event occurred, and the total,
    // minimum, and maximum aggregates of the associated measurement value.
    // The default value for the count is 0, the default value for the total
    // is 0.0, the default minimum value is 'MetricRecord::k_DEFAULT_MIN', and
    // the default maximum value is 'MetricRecord::k_DEFAULT_MAX'.

    // DATA
    MetricRecord         d_record;  // the recorded metric information
    mutable bslmt::Mutex d_lock;    // record synchronization mechanism

    // NOT IMPLEMENTED
    Collector(const Collector&);
    Collector& operator=(const Collector&);

  public:
     // CREATORS
    Collector(const MetricId& metricId);
        // Create a collector for a metric having the specified 'metricId',
        // and having an initial count of 0, total of 0.0, min of
        // 'MetricRecord::k_DEFAULT_MIN', and max of
        // 'MetricRecord::k_DEFAULT_MAX'.

    ~Collector();
        // Destroy this object.

    // MANIPULATORS
    void reset();
        // Reset the count, total, minimum, and maximum values of the metric
        // being collected to their default states.  After this operation, the
        // count and total values will be 0, the minimum value will be
        // 'MetricRecord::k_DEFAULT_MIN', and the maximum value will be
        // 'MetricRecord::k_DEFAULT_MAX'.

    void loadAndReset(MetricRecord *record);
        // Load into the specified 'record' the id of the metric being
        // collected as well as the current count, total, minimum, and maximum
        // aggregated values for that metric; then reset the count, total,
        // minimum, and maximum values to their default states.  After this
        // operation, the count and total values will be 0, the minimum value
        // will be 'MetricRecord::k_DEFAULT_MIN', and the maximum value will be
        // 'MetricRecord::k_DEFAULT_MAX'.  Note that this operation is
        // logically equivalent to calling the 'load' and then the 'reset'
        // methods except that it is performed as a single atomic operation.

    void update(double value);
        // Increment the event count by 1, add the specified 'value' to the
        // total, if 'value' is less than the minimum value, set 'value' to be
        // the minimum value, and if 'value' is greater than the maximum
        // value, set 'value' to be the maximum value.

    void accumulateCountTotalMinMax(int    count,
                                    double total,
                                    double min,
                                    double max);
        // Increment the event count by the specified 'count', add the
        // specified 'total' to the accumulated total, if specified 'min' is
        // less than the minimum value, set 'min' to be the minimum value, and
        // if specified 'max' is greater than the maximum value, set 'max' to
        // be the maximum value.

    void setCountTotalMinMax(int count, double total, double min, double max);
        // Set the event count to the specified 'count', the total aggregate to
        // the specified 'total', the minimum aggregate to the specified 'min'
        // and the maximum aggregate to the specified 'max'.

    // ACCESSORS
    const MetricId& metricId() const;
        // Return a reference to the non-modifiable 'MetricId' object
        // identifying the metric for which this object collects values.

    void load(MetricRecord *record) const;
        // Load into the specified 'record' the id of the metric being
        // collected, as well as the current count, total, minimum, and
        // maximum aggregated values for the metric.
};

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

                              // ---------------
                              // class Collector
                              // ---------------

// CREATORS
inline
Collector::Collector(const MetricId& metricId)
: d_record(metricId)
, d_lock()
{
}

inline
Collector::~Collector()
{
}

// MANIPULATORS
inline
void Collector::reset()
{
    bslmt::LockGuard<bslmt::Mutex> guard(&d_lock);
    d_record.count() = 0;
    d_record.total() = 0.0;
    d_record.min()   = MetricRecord::k_DEFAULT_MIN;
    d_record.max()   = MetricRecord::k_DEFAULT_MAX;
}

inline
void Collector::loadAndReset(MetricRecord *record)
{
    bslmt::LockGuard<bslmt::Mutex> guard(&d_lock);
    *record          = d_record;
    d_record.count() = 0;
    d_record.total() = 0.0;
    d_record.min()   = MetricRecord::k_DEFAULT_MIN;
    d_record.max()   = MetricRecord::k_DEFAULT_MAX;
}

inline
void Collector::update(double value)
{
    bslmt::LockGuard<bslmt::Mutex> guard(&d_lock);
    ++d_record.count();
    d_record.total() += value;
    d_record.min()   =  bsl::min(d_record.min(), value);
    d_record.max()   =  bsl::max(d_record.max(), value);
}

inline
void Collector::accumulateCountTotalMinMax(int    count,
                                           double total,
                                           double min,
                                           double max)
{
    bslmt::LockGuard<bslmt::Mutex> guard(&d_lock);
    d_record.count() += count;
    d_record.total() += total;
    d_record.min()   =  bsl::min(d_record.min(), min);
    d_record.max()   =  bsl::max(d_record.max(), max);
}

inline
void Collector::setCountTotalMinMax(int    count,
                                    double total,
                                    double min,
                                    double max)
{
    bslmt::LockGuard<bslmt::Mutex> guard(&d_lock);
    d_record.count() = count;
    d_record.total() = total;
    d_record.min()   = min;
    d_record.max()   = max;
}

// ACCESSORS
inline
const MetricId& Collector::metricId() const
{
    return d_record.metricId();
}

inline
void Collector::load(MetricRecord *record) const
{
    bslmt::LockGuard<bslmt::Mutex> guard(&d_lock);
    *record = d_record;
}
}  // 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 ----------------------------------