stats_mgr.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. // Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #ifndef STATSMGR_H
  15. #define STATSMGR_H
  16. #include <stats/observation.h>
  17. #include <stats/context.h>
  18. #include <boost/noncopyable.hpp>
  19. #include <map>
  20. #include <string>
  21. #include <vector>
  22. #include <sstream>
  23. namespace isc {
  24. namespace stats {
  25. /// @brief Statistics Manager class
  26. ///
  27. /// StatsMgr is a singleton class that represents a subsystem that manages
  28. /// collection, storage and reporting of various types of statistics.
  29. /// It is also the intended API for both core code and hooks.
  30. ///
  31. /// As of May 2015, Tomek ran performance benchmarks (see unit-tests in
  32. /// stats_mgr_unittest.cc with performance in their names) and it seems
  33. /// the code is able to register ~2.5-3 million observations per second, even
  34. /// with 1000 different statistics recored. That seems sufficient for now,
  35. /// so there is no immediate need to develop any multi-threading solutions
  36. /// for now. However, should this decision be revised in the future, the
  37. /// best place for it would to be modify @ref addObservation method here.
  38. /// It's the common code point that all new observations must pass through.
  39. /// One possible way to enable multi-threading would be to run a separate
  40. /// thread handling collection. The main thread would call @ref addValue and
  41. /// @ref setValue methods that would end up calling @ref addObservation.
  42. /// That method would pass the data to separate thread to be collected and
  43. /// would immediately return. Further processing would be mostly as it
  44. /// is today, except happening in a separate thread. One unsolved issue in
  45. /// this approach is how to extract data, but that will remain unsolvable
  46. /// until we get the control socket implementation.
  47. ///
  48. /// Statistics Manager does not use logging by design. The reasons are:
  49. /// - performance impact (logging every observation would degrade performance
  50. /// significantly. While it's possible to log on sufficiently high debug
  51. /// level, such a log would be not that useful)
  52. /// - dependency (statistics are intended to be a lightweight library, adding
  53. /// dependency on libkea-log, which has its own dependecies, including
  54. /// external log4cplus, is against 'lightweight' design)
  55. /// - if logging of specific statistics is warranted, it is recommended to
  56. /// add log entries in the code that calls StatsMgr.
  57. /// - enabling logging in StatsMgr does not offer fine tuning. It would be
  58. /// either all or nothing. Adding logging entries only when necessary
  59. /// in the code that uses StatsMgr gives better granularity.
  60. ///
  61. /// If this decision is revisited in the futere, the most universal places
  62. /// for adding logging have been marked in @ref addValueInternal and
  63. /// @ref setValueInternal.
  64. class StatsMgr : public boost::noncopyable {
  65. public:
  66. /// @brief Statistics Manager accessor method.
  67. static StatsMgr& instance();
  68. /// @defgroup producer_methods Methods are used by data producers.
  69. ///
  70. /// @brief The following methods are used by data producers:
  71. ///
  72. /// @{
  73. /// @brief Records absolute integer observation.
  74. ///
  75. /// @param name name of the observation
  76. /// @param value integer value observed
  77. /// @throw InvalidStatType if statistic is not integer
  78. void setValue(const std::string& name, const int64_t value);
  79. /// @brief Records absolute floating point observation.
  80. ///
  81. /// @param name name of the observation
  82. /// @param value floating point value observed
  83. /// @throw InvalidStatType if statistic is not fp
  84. void setValue(const std::string& name, const double value);
  85. /// @brief Records absolute duration observation.
  86. ///
  87. /// @param name name of the observation
  88. /// @param value duration value observed
  89. /// @throw InvalidStatType if statistic is not time duration
  90. void setValue(const std::string& name, const StatsDuration& value);
  91. /// @brief Records absolute string observation.
  92. ///
  93. /// @param name name of the observation
  94. /// @param value string value observed
  95. /// @throw InvalidStatType if statistic is not a string
  96. void setValue(const std::string& name, const std::string& value);
  97. /// @brief Records incremental integer observation.
  98. ///
  99. /// @param name name of the observation
  100. /// @param value integer value observed
  101. /// @throw InvalidStatType if statistic is not integer
  102. void addValue(const std::string& name, const int64_t value);
  103. /// @brief Records incremental floating point observation.
  104. ///
  105. /// @param name name of the observation
  106. /// @param value floating point value observed
  107. /// @throw InvalidStatType if statistic is not fp
  108. void addValue(const std::string& name, const double value);
  109. /// @brief Records incremental duration observation.
  110. ///
  111. /// @param name name of the observation
  112. /// @param value duration value observed
  113. /// @throw InvalidStatType if statistic is not time duration
  114. void addValue(const std::string& name, const StatsDuration& time);
  115. /// @brief Records incremental string observation.
  116. ///
  117. /// @param name name of the observation
  118. /// @param value string value observed
  119. /// @throw InvalidStatType if statistic is not a string
  120. void addValue(const std::string& name, const std::string& value);
  121. /// @brief Determines maximum age of samples.
  122. ///
  123. /// Specifies that statistic name should be stored not as a single value,
  124. /// but rather as a set of values. duration determines the timespan.
  125. /// Samples older than duration will be discarded. This is time-constrained
  126. /// approach. For sample count constrained approach, see @ref
  127. /// setMaxSampleCount() below.
  128. ///
  129. /// @todo: Not implemented.
  130. ///
  131. /// Example: to set a statistic to keep observations for the last 5 minutes,
  132. /// call setMaxSampleAge("incoming-packets", time_duration(0,5,0,0));
  133. /// to revert statistic to a single value, call:
  134. /// setMaxSampleAge("incoming-packets" time_duration(0,0,0,0))
  135. void setMaxSampleAge(const std::string& name, const StatsDuration& duration);
  136. /// @brief Determines how many samples of a given statistic should be kept.
  137. ///
  138. /// Specifies that statistic name should be stored not as single value, but
  139. /// rather as a set of values. In this form, at most max_samples will be kept.
  140. /// When adding max_samples+1 sample, the oldest sample will be discarded.
  141. ///
  142. /// @todo: Not implemented.
  143. ///
  144. /// Example:
  145. /// To set a statistic to keep the last 100 observations, call:
  146. /// setMaxSampleCount("incoming-packets", 100);
  147. void setMaxSampleCount(const std::string& name, uint32_t max_samples);
  148. /// @}
  149. /// @defgroup consumer_methods Methods are used by data consumers.
  150. ///
  151. /// @brief The following methods are used by data consumers:
  152. ///
  153. /// @{
  154. /// @brief Resets specified statistic.
  155. ///
  156. /// This is a convenience function and is equivalent to setValue(name,
  157. /// neutral_value), where neutral_value is 0, 0.0 or "".
  158. /// @param name name of the statistic to be reset.
  159. /// @return true if successful, false if there's no such statistic
  160. bool reset(const std::string& name);
  161. /// @brief Removes specified statistic.
  162. ///
  163. /// @param name name of the statistic to be removed.
  164. /// @return true if successful, false if there's no such statistic
  165. bool del(const std::string& name);
  166. /// @brief Resets all collected statistics back to zero.
  167. void resetAll();
  168. /// @brief Removes all collected statistics.
  169. void removeAll();
  170. /// @brief Returns number of available statistics.
  171. ///
  172. /// @return number of recorded statistics.
  173. size_t count() const;
  174. /// @brief Returns a single statistic as a JSON structure.
  175. ///
  176. /// @return JSON structures representing a single statistic
  177. isc::data::ConstElementPtr get(const std::string& name) const;
  178. /// @brief Returns all statistics as a JSON structure.
  179. ///
  180. /// @return JSON structures representing all statistics
  181. isc::data::ConstElementPtr getAll() const;
  182. /// @}
  183. /// @brief Returns an observation.
  184. ///
  185. /// Used in testing only. Production code should use @ref get() method.
  186. /// @param name name of the statistic
  187. /// @return Pointer to the Observation object
  188. ObservationPtr getObservation(const std::string& name) const;
  189. /// @brief Generates statistic name in a given context
  190. ///
  191. /// Example:
  192. /// @code
  193. /// generateName("subnet", 123, "received-packets");
  194. /// @endcode
  195. /// will return subnet[123].received-packets. Any printable type
  196. /// can be used as index.
  197. ///
  198. /// @tparam Type any type that can be used to index contexts
  199. /// @param context name of the context (e.g. 'subnet')
  200. /// @param index value used for indexing contexts (e.g. subnet_id)
  201. /// @param stat_name name of the statistic
  202. /// @return returns full statistic name in form context[index].stat_name
  203. template<typename Type>
  204. static std::string generateName(const std::string& context, Type index,
  205. const std::string& stat_name) {
  206. std::stringstream name;
  207. name << context << "[" << index << "]." << stat_name;
  208. return (name.str());
  209. }
  210. private:
  211. /// @brief Sets a given statistic to specified value (internal version).
  212. ///
  213. /// This template method sets statistic identified by name to a value
  214. /// specified by value. This internal method is used by public @ref setValue
  215. /// methods.
  216. ///
  217. /// @tparam DataType one of int64_t, double, StatsDuration or string
  218. /// @param name name of the statistic
  219. /// @param value specified statistic will be set to this value
  220. /// @throw InvalidStatType is statistic exists and has a different type.
  221. template<typename DataType>
  222. void setValueInternal(const std::string& name, DataType value) {
  223. // If we want to log each observation, here would be the best place for it.
  224. ObservationPtr stat = getObservation(name);
  225. if (stat) {
  226. stat->setValue(value);
  227. } else {
  228. stat.reset(new Observation(name, value));
  229. addObservation(stat);
  230. }
  231. }
  232. /// @brief Adds specified value to a given statistic (internal version).
  233. ///
  234. /// This template method adds specified value to a given statistic (identified
  235. /// by name to a value). This internal method is used by public @ref setValue
  236. /// methods.
  237. ///
  238. /// @tparam DataType one of int64_t, double, StatsDuration or string
  239. /// @param name name of the statistic
  240. /// @param value specified statistic will be set to this value
  241. /// @throw InvalidStatType is statistic exists and has a different type.
  242. template<typename DataType>
  243. void addValueInternal(const std::string& name, DataType value) {
  244. // If we want to log each observation, here would be the best place for it.
  245. ObservationPtr existing = getObservation(name);
  246. if (!existing) {
  247. // We tried to add to a non-existing statistic. We can recover from
  248. // that. Simply add the new incremental value as a new statistic and
  249. // we're done.
  250. setValue(name, value);
  251. return;
  252. } else {
  253. // Let's hope it is of correct type. If not, the underlying
  254. // addValue() method will throw.
  255. existing->addValue(value);
  256. }
  257. }
  258. /// @brief Private constructor.
  259. /// StatsMgr is a singleton. It should be accessed using @ref instance
  260. /// method.
  261. StatsMgr();
  262. /// @brief Adds a new observation.
  263. ///
  264. /// That's an utility method used by public @ref setValue() and
  265. /// @ref addValue() methods.
  266. /// @param obs observation
  267. void addObservation(const ObservationPtr& o);
  268. /// @brief Tries to delete an observation.
  269. ///
  270. /// @param name of the statistic to be deleted
  271. /// @return true if deleted, false if not found
  272. bool deleteObservation(const std::string& name);
  273. // This is a global context. All statistics will initially be stored here.
  274. StatContextPtr global_;
  275. };
  276. };
  277. };
  278. #endif // STATS_MGR