4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 // Stefan Bund <g0dil@berlios.de>
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 \brief Statistics public header */
26 #ifndef HH_SENF_Utils_Statistics_
27 #define HH_SENF_Utils_Statistics_ 1
33 #include <boost/iterator/transform_iterator.hpp>
34 #include <boost/range/iterator_range.hpp>
35 #include <boost/utility.hpp>
36 #include <boost/ptr_container/ptr_vector.hpp>
37 #include <boost/signals.hpp>
38 #include "Exception.hh"
39 #include <senf/Utils/Logger/Logger.hh>
40 #include <senf/Utils/Console/Console.hh>
42 //#include "Statistics.mpp"
43 ///////////////////////////////hh.p////////////////////////////////////////
47 /** \defgroup senf_statistics Statistics
49 The statistics functionality has two parts:
51 \li the senf::Statistics class
52 \li statistics sources
54 Each senf::Statistics instance collects information about a single parameter. Which
55 parameter is set up by connecting the Statistics instance with an arbitrary statistics
58 %Statistics sources are <a href="http://www.boost.org/doc/libs/release/doc/html/signals.html">
59 Boost Signals</a> which are emitted periodically to provide new data.
65 /** \brief Internal: Generic Statistics collection */
68 typedef std::map<unsigned, Collector> Children;
71 typedef Children::value_type & first_argument_type;
72 typedef Collector & result_type;
73 result_type operator()(first_argument_type i) const;
76 typedef boost::transform_iterator<Transform,Children::iterator> ValueIterator;
81 ///////////////////////////////////////////////////////////////////////////
84 typedef boost::iterator_range<ValueIterator> CollectorRange;
86 /** \brief Output connection interface
88 This class is returned from senf::StatisticsBase::output() and the derived
89 <tt>output()</tt> implementations to allow connecting an output with an arbitrary
92 There are two types of targets:
93 \li <em>Externally managed targets</em>. These targets live outside of the statistics
94 module, just a reference to those targets is saved. The target should derive from
95 <tt>boost::signals::trackable</tt> to ensure they are automatically disconnected
97 \li <em>Internally managed targets</em>. Those targets are owned by the statistics
98 module and will be destroyed when the statistics class is destroyed.
100 Externally managed targets are passed by non-const reference to connect(), internally
101 managed targets are passed using <tt>std::auto_ptr</tt>.
103 A target is any callable object which takes three float values as argument: The current
104 minimum, average and maximum value.
107 // Simple function as statistics target
108 void collect(float min, float avg, float max)
114 void operator()(float min, float avg, float max, float dev)
119 \ingroup senf_statistics
121 template <class Owner>
125 template <class Target> Owner & connect(Target & target,
126 std::string label="") const;
127 ///< Connect externally managed target
128 template <class PTarget> Owner & connect(std::auto_ptr<PTarget> target,
129 std::string label="") const;
130 ///< Connect internally managed target
131 Owner & noconnect() const; ///< Don't connect the output
132 senf::console::ScopedDirectory<> & dir() const;
133 ///< Get target's console directory
138 OutputProxy(Owner * owner, OutputEntry * entry);
139 template <class OtherOwner>
140 OutputProxy(Owner * owner, OutputProxy<OtherOwner> const & other);
144 OutputEntry * entry_;
146 template <class OtherOwner> friend class OutputProxy;
149 ///////////////////////////////////////////////////////////////////////////
150 ///\name Accessing the current value
153 float min() const; ///< Last min value entered
154 float avg() const; ///< Last avg value entered
155 float max() const; ///< Last max value entered
156 float dev() const; ///< Last dev value entered
158 virtual unsigned rank() const; ///< Return collectors rank value
159 /**< \returns number of basic values collected into each new
160 value by this collector. */
163 ///////////////////////////////////////////////////////////////////////////
164 ///\name Child collectors
167 Collector & operator[](unsigned rank);
168 ///< Get child collector
169 /**< This member will return a reference to the collector
170 collecting \a rank values.
171 \param[in] rank Number of values the requested
172 collector collects into each combined value.
173 \throws InvalidRankException if \a rank is not a valid
174 registered rank value. */
175 Collector const & operator[](unsigned rank) const;
176 ///< Get child collector
177 /**< This member will return a const reference to the
178 collector collecting \a rank values.
179 \param[in] rank Number of values the requested
180 collector collects into each combined value.
181 \throws InvalidRankException if \a rank is not a valid
182 registered rank value. */
183 CollectorRange collectors(); ///< List all child collectors
184 /**< \returns iterator range of child collector
187 Collector & collect(unsigned rank); ///< Register a new collector
188 /**< Adds a collector collecting \a rank values into each
190 \param[in] rank number of values to collect
191 \returns Reference to new collector
192 \throws DuplicateRankException if a collector
193 collecting \a rank values already exists. */
195 Statistics & base(); ///< Get base statistics object
196 /**< Returns the base statistics object. If this is
197 a child collector, this will return the outermost
198 statistics object, otherwise it will return
201 std::string path() const; ///< Get the path to this collector
202 /**< Returns the '-'-separated list of collectors up to
203 here. If this is the basic statistics object, the value
204 is empty, otherwise it is built by joining the
208 ///////////////////////////////////////////////////////////////////////////
209 ///\name Result generation
211 OutputProxy<StatisticsBase> output(unsigned n = 1u);
213 /**< This call will request the collector to output
214 statistics build by averaging the last \a n
215 values. This output is generated for every new value in
216 the collector. The output signal can be connected to an
217 arbitrary target using the returned proxy. Example:
219 stats.output(4u).connect(
220 senf::StatisticsLogger());
222 \param[in] n size of sliding average window */
225 ///////////////////////////////////////////////////////////////////////////
228 struct InvalidRankException : public senf::Exception
229 { InvalidRankException() : senf::Exception("Invalid rank value") {} };
231 struct DuplicateRankException : public senf::Exception
232 { DuplicateRankException() : senf::Exception("Duplicate rank value") {} };
234 void consoleList(unsigned level, std::ostream & os) const;
238 virtual ~StatisticsBase();
239 void enter(unsigned n, float min, float avg, float max, float dev);
242 virtual Statistics & v_base() = 0;
243 virtual std::string v_path() const = 0;
245 void generateOutput();
258 QueueEntry() : min(), avg(), max(), dev() {}
259 QueueEntry(float min_, float avg_, float max_, float dev_)
260 : min(min_), avg(avg_), max(max_), dev(dev_) {}
262 typedef std::deque<QueueEntry> Queue;
268 explicit TargetBase(std::string const & label_) : label (label_) {}
269 virtual ~TargetBase() {};
273 template <class PTarget>
274 struct Target : public TargetBase
276 boost::scoped_ptr<PTarget> target_;
277 Target(std::auto_ptr<PTarget> target, std::string const & label)
278 : TargetBase (label), target_ (target.release()) {}
279 explicit Target(std::string const & label)
280 : TargetBase (label), target_ (0) {}
284 explicit OutputEntry(unsigned n_);
285 OutputEntry(const OutputEntry& other);
286 OutputEntry& operator=(const OutputEntry& other);
289 void consoleList(std::ostream & os);
297 boost::signal<void(float,float,float,float)> signal;
298 boost::ptr_vector<TargetBase> targets_;
300 senf::console::ScopedDirectory<> dir;
302 typedef std::map<unsigned, OutputEntry> OutputMap;
304 unsigned maxQueueLen_;
307 /** \brief Collect statistics and generate %log messages
309 The Statistics class collects information about a single value. The assumption is, that
310 Statistics::operator() is called \e periodically to supply new data.
312 Each data entry is comprised of a minimum, average and maximum value. These values should
313 describe the value of whatever parameter is analyzed by a Statistics instance over the last
314 period. The length of the period is defined by the interval, at which data is entered.
316 The Statistics class combines successive data values into ever larger groups. These groups
317 form a tree where each node combines a fixed number of data values of it's parent node. An
320 Let us assume, that Statistics::operator() is called every 100ms. We can than build a chain
322 \li The basic statistics module provides information with a resolution of 1/10th of a second
323 \li A collector collecting 10 values provides information with 1 second resolution
324 \li The next collector collects 60 values and provides a resolution of 1 minute
325 \li Again the next collector collects 60 values and provides a resolution of 1 hour
328 This way, we create a hierarchy of values. Each collector manages a minimum, average and
329 maximum value always created over it's the last complete interval.
331 It is possible to have more than one collector based on the same basic interval. In above
332 scenario, we might want to add another collector which for example collects information
333 with a 100 second scale. This is possible and changes the list of collectors into a tree.
335 Now to turn all this into code:
338 senf::ppi::module::RateAnalyzer rateAnalyzer;
339 senf::Statistics packetStats;
341 rateAnalyzer.signals.packetsPerSecond.connect(boost::ref(packetStats));
343 packetStats // called 10 times / second
344 .collect(10u) // seconds
345 .collect(60u) // minutes
346 .collect(60u); // hours
348 packetStats[10u].collect(100u); // 100 seconds
350 rateAnalyzer.startStatistics(senf::ClockService::milliseconds(100u));
353 This code will collect the statistics as described in the Example above. However, no output
356 For every collector, any number of outputs may be defined. Each output consists of the
357 number of values to calculate a sliding average over and an identifying label.
359 Lets say, we want to produce the following outputs:
360 \li A sliding average of 5 values based on the raw 1/10th second data.
361 \li Three different outputs from the seconds staistics: current value without average,
362 sliding average over 10 seconds and sliding average over 60 seconds.
363 \li Output the minutes and hourly value without averaging.
365 To achieve this, we can augment above code in the following way:
369 .output( 5u).connect(senf::StatisicsLogger("pps 100ms 5"))
371 .output( ).noconnect()
372 .output(10u).connect(senf::StatisicsLogger("pps 1s 10"))
373 .output(60u).connect(senf::StatisicsLogger("pps 1s 60"))
375 .output( ).connect(senf::StatisicsLogger("pps 1min 1"))
377 .output( ).connect(senf::StatisicsLogger("pps 1h 1"));
379 packetStats.output(5u).connect(
380 senf::StatisticsLogger<senf::log::Debug, senf::log::VERBOSE>("pps"));
382 senf::log::FileTarget statslog ("stats.log");
384 statslog.showArea(false);
385 statslog.showLevel(false);
387 statslog.timeFormat("");
389 statslog.route<senf::StatisticsStream>();
392 We use a StatisticsLogger to send the %log messages to the senf::StatisticsStream %log
393 stream. The stream, area an level to send the statistics %log messages to may be configured
394 using template arguments to StatisticsLogger.
396 It is also possible to skip sending the output to any target or send one output to several
399 Here we have opted to use a label which explicitly describes the name of the variable, the
400 basic interval and the size of the sliding average window. However, the label is completely
403 All output is generated using the Senf Logger on the senf::StatisticsStream %log stream. In
404 the example above, we have configured a special logfile \c stats.log which contains the
405 statistics values each prefixed with a timestamp in nanoseconds (but no further information
406 like %log level or tag):
408 0000000000.000000000 pps 100ms 5 43.3 43.3 43.3
409 0000000000.010311928 pps 100ms 5 42.5 42.5 42.5
411 0000000001.002413391 pps 100ms 5 62.0 62.0 62.0
412 0000000001.003920018 pps 1s 1 42.1 53.4 62.0
415 (the nanosecond values will of course be somewhat different ...)
417 \see senf::StatisticsBase::OutputProxy for the output proxy (connect) interface
418 \ingroup senf_statistics
421 : public StatisticsBase, boost::noncopyable
424 #ifndef SENF_DISABLE_CONSOLE
425 console::ScopedDirectory<Statistics> dir;
430 void operator()(unsigned n, float min, float avg, float max, float dev);
432 /**< This member must be called whenever new data is
435 If \a min and \a max values are not available, this
436 member should be called with \a min, \a avg and \a max
437 set to the same value. If no error estimate is
438 available, call with \a dev = 0.
440 In the most common case, this member is to be called
441 periodically and \a n will be 1 on all calls. The
442 interval, at which this member is called then defines
443 the statistics time scale.
445 Calling with \a n > 1 will submit the value \a n
446 times. This makes it possible to aggregate multiple
447 time slices into a single call. This does not change
448 the basic time scale but can change the number of
449 submits per unit time. If the basic time slice is
450 small, this allows to submit values almost arbitrarily
453 \param[in] n number of time-slices
454 \param[in] min minimal data value since last call
455 \param[in] avg average data value since last call
456 \param[in] max maximal data values since last call
457 \param[in] dev standard deviation of avg value */
458 void operator()(float min, float avg, float max, float dev=0.0f);
459 ///< Same as operator() with \a n==1
460 /**< Provided so a Statistics instance can be directly used
461 as a signal target. */
462 void operator()(float value, float dev=0.0f);
463 ///< Same as operator() with \a min == \a avg == \a max
464 /**< Provided so a Statistics instance can be directly used
465 as a signal target. */
467 StatisticsBase::OutputProxy<Statistics> output(unsigned n = 1u);
469 void consoleList(std::ostream & os);
470 void consoleCollect(std::vector<unsigned> & ranks);
471 boost::shared_ptr<senf::console::DirectoryNode> consoleOutput(
472 std::vector<unsigned> & ranks, unsigned window);
475 Statistics & v_base();
476 std::string v_path() const;
479 /** \brief Accumulated statistics collector
481 This class collects accumulated statistics. It is automatically created by
482 senf::Statistics::collect()
484 \see senf::Statistics
486 class Collector : public StatisticsBase
489 virtual unsigned rank() const;
491 StatisticsBase::OutputProxy<Collector> output(unsigned n = 1u);
494 Collector(StatisticsBase * owner, unsigned rank);
495 void enter(unsigned n, float min, float avg, float max, float dev);
496 Statistics & v_base();
497 std::string v_path() const;
505 StatisticsBase * owner_;
507 friend class StatisticsBase;
512 ///////////////////////////////hh.e////////////////////////////////////////
513 #include "Statistics.cci"
514 //#include "Statistics.ct"
515 #include "Statistics.cti"
522 // comment-column: 40
523 // c-file-style: "senf"
524 // indent-tabs-mode: nil
525 // ispell-local-dictionary: "american"
526 // compile-command: "scons -u test"