histogramprovider.h 7.57 KB
/* ****************************************************************************
 * Copyright 2019 Open Systems Development BV                                 *
 *                                                                            *
 * Permission is hereby granted, free of charge, to any person obtaining a    *
 * copy of this software and associated documentation files (the "Software"), *
 * to deal in the Software without restriction, including without limitation  *
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,   *
 * and/or sell copies of the Software, and to permit persons to whom the      *
 * Software is furnished to do so, subject to the following conditions:       *
 *                                                                            *
 * The above copyright notice and this permission notice shall be included in *
 * all copies or substantial portions of the Software.                        *
 *                                                                            *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    *
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    *
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        *
 * DEALINGS IN THE SOFTWARE.                                                  *
 * ***************************************************************************/
#ifndef OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAMPROVIDER_H
#define OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAMPROVIDER_H

// std
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>

#include "compat-c++14.h"

// mlogic::common
#include "sharedreaderlock.h"

#include "histogram.h"

namespace osdev {
namespace components {
namespace mqtt {
namespace measurement {

/**
 * @brief This class provides the user with Entry class for putting measurements in a histogram.
 * @tparam TContext A tag type that is used to give the HistogramProvider a user defined context.
 * @tparam TValue Defines the value type that the histograms of this provider use.
 * @tparam Buckets The number of buckets to use on creation of a Histogram. The default number of buckets is 10.
 *
 * The TValue type must define the following publically visible members.
 *
 * value_type                - The underlying value type used in the histogram
 * const value_type minValue - The minimum value of the histogram
 * const value_type maxValue - The maximum value of the histogram
 * const char* unit          - The value unit.
 */
template <typename TContext, typename TValue, std::size_t Buckets = 10>
class HistogramProvider
{
public:
    using HistogramType = Histogram<typename TValue::value_type, Buckets>;

    /**
     * @brief Get the HistogramProvider instance.
     */
    static HistogramProvider& instance();

    /**
     * @brief Add a value to the histogram identified by id.
     * @param id Identifies the histogram to add the value to.
     * @param value The value to add.
     */
    void addValue(const std::string& id, typename TValue::value_type value);

    /**
     * @brief Log the histogram identified by id via the ILogger framework.
     * @param id The histogram to log. If no histogram is found no work is done.
     */
    void log(const std::string& id);

    /**
     * @brief Log the histograms via the ILogger framework.
     * @param onlyChanged When set to true only log histograms that have changed. Default is false.
     */
    void logAll(bool onlyChanged = false);

    /**
     * @brief Reset all the histograms.
     * @param logBeforeReset Flag that indicates whether the histogram needs to be logged before reset. Default is true.
     * @note Only changed histograms are logged.
     */
    void resetAll(bool logBeforeReset = true);

    /**
     * @return The logOnDestroy flag value.
     */
    bool logOnDestroy() const { return m_logOnDestroy; }

    /**
     * @brief Set the logOnDestroy flag.
     * @param logOnDest Log on destruction when true, do not log when set to false.
     */
    void setLogOnDestroy(bool logOnDest) { m_logOnDestroy = logOnDest; }

private:
    HistogramProvider();

    /**
     * @brief On destruction the provider will log all its histogram information to stdout.
     * Since the destruction will happen as one of the last things in the process, stdout is
     * used for logging.
     */
    ~HistogramProvider();

    void log(const HistogramType& hist, bool onlyChanged);

    SharedReaderLock m_sharedLock;
    std::unordered_map<std::string, std::unique_ptr<HistogramType>> m_histograms;
    bool m_logOnDestroy; ///< Default is true.
};

// static
template <typename TContext, typename TValue, std::size_t Buckets>
HistogramProvider<TContext, TValue, Buckets>& HistogramProvider<TContext, TValue, Buckets>::instance()
{
    static HistogramProvider<TContext, TValue, Buckets> s_provider;
    return s_provider;
}

template <typename TContext, typename TValue, std::size_t Buckets>
void HistogramProvider<TContext, TValue, Buckets>::addValue(const std::string& id, typename TValue::value_type value)
{
    OSDEV_COMPONENTS_SHAREDLOCK_SCOPE(m_sharedLock);
    auto it = m_histograms.find(id);
    if (m_histograms.end() == it) {
        OSDEV_COMPONENTS_EXCLUSIVELOCK_SCOPE(m_sharedLock);
        constexpr TValue val;
        it = (m_histograms.emplace(std::make_pair(id, std::make_unique<HistogramType>(id, val.minValue, val.maxValue, val.unit)))).first;
    }
    it->second->setValue(value);
}

template <typename TContext, typename TValue, std::size_t Buckets>
void HistogramProvider<TContext, TValue, Buckets>::log(const std::string& id)
{
    OSDEV_COMPONENTS_SHAREDLOCK_SCOPE(m_sharedLock);
    auto it = m_histograms.find(id);
    if (m_histograms.end() != it) {
        log(*(it->second), false);
    }
}

template <typename TContext, typename TValue, std::size_t Buckets>
void HistogramProvider<TContext, TValue, Buckets>::logAll(bool onlyChanged)
{
    OSDEV_COMPONENTS_SHAREDLOCK_SCOPE(m_sharedLock);
    for (const auto& h : m_histograms) {
        log(*h.second, onlyChanged);
    }
}

template <typename TContext, typename TValue, std::size_t Buckets>
void HistogramProvider<TContext, TValue, Buckets>::resetAll(bool logBeforeReset)
{
    OSDEV_COMPONENTS_SHAREDLOCK_SCOPE(m_sharedLock);
    for (const auto& h : m_histograms) {
        if (logBeforeReset) {
            log(*h.second, true); // only log the histograms that have changed
        }
        h.second->reset();
    }
}

template <typename TContext, typename TValue, std::size_t Buckets>
HistogramProvider<TContext, TValue, Buckets>::HistogramProvider()
    : m_sharedLock()
    , m_histograms()
    , m_logOnDestroy(true)
{
}

template <typename TContext, typename TValue, std::size_t Buckets>
HistogramProvider<TContext, TValue, Buckets>::~HistogramProvider()
{
    if (m_logOnDestroy) {
        for (const auto& h : m_histograms) {
            std::cout << *h.second << std::endl;
        }
    }
}

template <typename TContext, typename TValue, std::size_t Buckets>
void HistogramProvider<TContext, TValue, Buckets>::log(const HistogramType& hist, bool onlyChanged)
{
    if ((onlyChanged && hist.isUpdated()) || !onlyChanged) {
        MLOGIC_COMMON_INFO("HistogramProvider", "%1", hist);
    }
}

}       // End namespace measurement
}       // End namespace mqtt
}       // End namespace components
}       // End namespace osdev

#endif  // OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAMPROVIDER_H