histogramprovider.h 6.7 KB
/* Copyright (C) 2019
 *
 * This file is part of the osdev components suite
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
 */
#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