123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- // Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- #ifndef STATSMGR_H
- #define STATSMGR_H
- #include <stats/observation.h>
- #include <stats/context.h>
- #include <boost/noncopyable.hpp>
- #include <map>
- #include <string>
- #include <vector>
- #include <sstream>
- namespace isc {
- namespace stats {
- /// @brief Statistics Manager class
- ///
- /// StatsMgr is a singleton class that represents a subsystem that manages
- /// collection, storage and reporting of various types of statistics.
- /// It is also the intended API for both core code and hooks.
- ///
- /// As of May 2015, Tomek ran performance benchmarks (see unit-tests in
- /// stats_mgr_unittest.cc with performance in their names) and it seems
- /// the code is able to register ~2.5-3 million observations per second, even
- /// with 1000 different statistics recored. That seems sufficient for now,
- /// so there is no immediate need to develop any multi-threading solutions
- /// for now. However, should this decision be revised in the future, the
- /// best place for it would to be modify @ref addObservation method here.
- /// It's the common code point that all new observations must pass through.
- /// One possible way to enable multi-threading would be to run a separate
- /// thread handling collection. The main thread would call @ref addValue and
- /// @ref setValue methods that would end up calling @ref addObservation.
- /// That method would pass the data to separate thread to be collected and
- /// would immediately return. Further processing would be mostly as it
- /// is today, except happening in a separate thread. One unsolved issue in
- /// this approach is how to extract data, but that will remain unsolvable
- /// until we get the control socket implementation.
- ///
- /// Statistics Manager does not use logging by design. The reasons are:
- /// - performance impact (logging every observation would degrade performance
- /// significantly. While it's possible to log on sufficiently high debug
- /// level, such a log would be not that useful)
- /// - dependency (statistics are intended to be a lightweight library, adding
- /// dependency on libkea-log, which has its own dependecies, including
- /// external log4cplus, is against 'lightweight' design)
- /// - if logging of specific statistics is warranted, it is recommended to
- /// add log entries in the code that calls StatsMgr.
- /// - enabling logging in StatsMgr does not offer fine tuning. It would be
- /// either all or nothing. Adding logging entries only when necessary
- /// in the code that uses StatsMgr gives better granularity.
- ///
- /// If this decision is revisited in the futere, the most universal places
- /// for adding logging have been marked in @ref addValueInternal and
- /// @ref setValueInternal.
- class StatsMgr : public boost::noncopyable {
- public:
- /// @brief Statistics Manager accessor method.
- static StatsMgr& instance();
- /// @defgroup producer_methods Methods are used by data producers.
- ///
- /// @brief The following methods are used by data producers:
- ///
- /// @{
- /// @brief Records absolute integer observation.
- ///
- /// @param name name of the observation
- /// @param value integer value observed
- /// @throw InvalidStatType if statistic is not integer
- void setValue(const std::string& name, const int64_t value);
- /// @brief Records absolute floating point observation.
- ///
- /// @param name name of the observation
- /// @param value floating point value observed
- /// @throw InvalidStatType if statistic is not fp
- void setValue(const std::string& name, const double value);
- /// @brief Records absolute duration observation.
- ///
- /// @param name name of the observation
- /// @param value duration value observed
- /// @throw InvalidStatType if statistic is not time duration
- void setValue(const std::string& name, const StatsDuration& value);
- /// @brief Records absolute string observation.
- ///
- /// @param name name of the observation
- /// @param value string value observed
- /// @throw InvalidStatType if statistic is not a string
- void setValue(const std::string& name, const std::string& value);
- /// @brief Records incremental integer observation.
- ///
- /// @param name name of the observation
- /// @param value integer value observed
- /// @throw InvalidStatType if statistic is not integer
- void addValue(const std::string& name, const int64_t value);
- /// @brief Records incremental floating point observation.
- ///
- /// @param name name of the observation
- /// @param value floating point value observed
- /// @throw InvalidStatType if statistic is not fp
- void addValue(const std::string& name, const double value);
- /// @brief Records incremental duration observation.
- ///
- /// @param name name of the observation
- /// @param value duration value observed
- /// @throw InvalidStatType if statistic is not time duration
- void addValue(const std::string& name, const StatsDuration& time);
- /// @brief Records incremental string observation.
- ///
- /// @param name name of the observation
- /// @param value string value observed
- /// @throw InvalidStatType if statistic is not a string
- void addValue(const std::string& name, const std::string& value);
- /// @brief Determines maximum age of samples.
- ///
- /// Specifies that statistic name should be stored not as a single value,
- /// but rather as a set of values. duration determines the timespan.
- /// Samples older than duration will be discarded. This is time-constrained
- /// approach. For sample count constrained approach, see @ref
- /// setMaxSampleCount() below.
- ///
- /// @todo: Not implemented.
- ///
- /// Example: to set a statistic to keep observations for the last 5 minutes,
- /// call setMaxSampleAge("incoming-packets", time_duration(0,5,0,0));
- /// to revert statistic to a single value, call:
- /// setMaxSampleAge("incoming-packets" time_duration(0,0,0,0))
- void setMaxSampleAge(const std::string& name, const StatsDuration& duration);
- /// @brief Determines how many samples of a given statistic should be kept.
- ///
- /// Specifies that statistic name should be stored not as single value, but
- /// rather as a set of values. In this form, at most max_samples will be kept.
- /// When adding max_samples+1 sample, the oldest sample will be discarded.
- ///
- /// @todo: Not implemented.
- ///
- /// Example:
- /// To set a statistic to keep the last 100 observations, call:
- /// setMaxSampleCount("incoming-packets", 100);
- void setMaxSampleCount(const std::string& name, uint32_t max_samples);
- /// @}
- /// @defgroup consumer_methods Methods are used by data consumers.
- ///
- /// @brief The following methods are used by data consumers:
- ///
- /// @{
- /// @brief Resets specified statistic.
- ///
- /// This is a convenience function and is equivalent to setValue(name,
- /// neutral_value), where neutral_value is 0, 0.0 or "".
- /// @param name name of the statistic to be reset.
- /// @return true if successful, false if there's no such statistic
- bool reset(const std::string& name);
- /// @brief Removes specified statistic.
- ///
- /// @param name name of the statistic to be removed.
- /// @return true if successful, false if there's no such statistic
- bool del(const std::string& name);
- /// @brief Resets all collected statistics back to zero.
- void resetAll();
- /// @brief Removes all collected statistics.
- void removeAll();
- /// @brief Returns number of available statistics.
- ///
- /// @return number of recorded statistics.
- size_t count() const;
- /// @brief Returns a single statistic as a JSON structure.
- ///
- /// @return JSON structures representing a single statistic
- isc::data::ConstElementPtr get(const std::string& name) const;
- /// @brief Returns all statistics as a JSON structure.
- ///
- /// @return JSON structures representing all statistics
- isc::data::ConstElementPtr getAll() const;
- /// @}
- /// @brief Returns an observation.
- ///
- /// Used in testing only. Production code should use @ref get() method.
- /// @param name name of the statistic
- /// @return Pointer to the Observation object
- ObservationPtr getObservation(const std::string& name) const;
- /// @brief Generates statistic name in a given context
- ///
- /// Example:
- /// @code
- /// generateName("subnet", 123, "received-packets");
- /// @endcode
- /// will return subnet[123].received-packets. Any printable type
- /// can be used as index.
- ///
- /// @tparam Type any type that can be used to index contexts
- /// @param context name of the context (e.g. 'subnet')
- /// @param index value used for indexing contexts (e.g. subnet_id)
- /// @param stat_name name of the statistic
- /// @return returns full statistic name in form context[index].stat_name
- template<typename Type>
- static std::string generateName(const std::string& context, Type index,
- const std::string& stat_name) {
- std::stringstream name;
- name << context << "[" << index << "]." << stat_name;
- return (name.str());
- }
- private:
- /// @brief Sets a given statistic to specified value (internal version).
- ///
- /// This template method sets statistic identified by name to a value
- /// specified by value. This internal method is used by public @ref setValue
- /// methods.
- ///
- /// @tparam DataType one of int64_t, double, StatsDuration or string
- /// @param name name of the statistic
- /// @param value specified statistic will be set to this value
- /// @throw InvalidStatType is statistic exists and has a different type.
- template<typename DataType>
- void setValueInternal(const std::string& name, DataType value) {
- // If we want to log each observation, here would be the best place for it.
- ObservationPtr stat = getObservation(name);
- if (stat) {
- stat->setValue(value);
- } else {
- stat.reset(new Observation(name, value));
- addObservation(stat);
- }
- }
- /// @brief Adds specified value to a given statistic (internal version).
- ///
- /// This template method adds specified value to a given statistic (identified
- /// by name to a value). This internal method is used by public @ref setValue
- /// methods.
- ///
- /// @tparam DataType one of int64_t, double, StatsDuration or string
- /// @param name name of the statistic
- /// @param value specified statistic will be set to this value
- /// @throw InvalidStatType is statistic exists and has a different type.
- template<typename DataType>
- void addValueInternal(const std::string& name, DataType value) {
- // If we want to log each observation, here would be the best place for it.
- ObservationPtr existing = getObservation(name);
- if (!existing) {
- // We tried to add to a non-existing statistic. We can recover from
- // that. Simply add the new incremental value as a new statistic and
- // we're done.
- setValue(name, value);
- return;
- } else {
- // Let's hope it is of correct type. If not, the underlying
- // addValue() method will throw.
- existing->addValue(value);
- }
- }
- /// @brief Private constructor.
- /// StatsMgr is a singleton. It should be accessed using @ref instance
- /// method.
- StatsMgr();
- /// @brief Adds a new observation.
- ///
- /// That's an utility method used by public @ref setValue() and
- /// @ref addValue() methods.
- /// @param obs observation
- void addObservation(const ObservationPtr& o);
- /// @brief Tries to delete an observation.
- ///
- /// @param name of the statistic to be deleted
- /// @return true if deleted, false if not found
- bool deleteObservation(const std::string& name);
- // This is a global context. All statistics will initially be stored here.
- StatContextPtr global_;
- };
- };
- };
- #endif // STATS_MGR
|