Utils/Statistics: added operator[] member to get const & Collector in StatisticsBase
[senf.git] / senf / Utils / Statistics.hh
1 // $Id$
2 //
3 // Copyright (C) 2008
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
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.
12 //
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.
17 //
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.
22
23 /** \file
24     \brief Statistics public header */
25
26 #ifndef HH_SENF_Utils_Statistics_
27 #define HH_SENF_Utils_Statistics_ 1
28
29 // Custom includes
30 #include <map>
31 #include <vector>
32 #include <deque>
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>
41
42 //#include "Statistics.mpp"
43 ///////////////////////////////hh.p////////////////////////////////////////
44
45 namespace senf {
46
47     /** \defgroup senf_statistics Statistics
48
49         The statistics functionality has two parts:
50
51         \li the senf::Statistics class
52         \li statistics sources
53
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
56         source.
57
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.
60      */
61
62     class Collector;
63     class Statistics;
64
65     /** \brief Internal: Generic Statistics collection */
66     class StatisticsBase
67     {
68         typedef std::map<unsigned, Collector> Children;
69
70         struct Transform {
71             typedef Children::value_type & first_argument_type;
72             typedef Collector & result_type;
73             result_type operator()(first_argument_type i) const;
74         };
75
76         typedef boost::transform_iterator<Transform,Children::iterator> ValueIterator;
77
78         struct OutputEntry;
79
80     public:
81         ///////////////////////////////////////////////////////////////////////////
82         // Types
83
84         typedef boost::iterator_range<ValueIterator> CollectorRange;
85
86         /** \brief Output connection interface
87
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
90             target.
91
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
96                 when destroyed.
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.
99
100             Externally managed targets are passed by non-const reference to connect(), internally
101             managed targets are passed using <tt>std::auto_ptr</tt>.
102
103             A target is any callable object which takes three float values as argument: The current
104             minimum, average and maximum value.
105
106             \code
107             // Simple function as statistics target
108             void collect(float min, float avg, float max)
109                 { ... }
110
111             // Function object
112             struct Collector
113             {
114                 void operator()(float min, float avg, float max, float dev)
115                     { ... }
116             };
117             \endcode
118
119            \ingroup senf_statistics
120          */
121         template <class Owner>
122         class OutputProxy
123         {
124         public:
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
134
135 #ifdef DOXYGEN
136         private:
137 #endif
138             OutputProxy(Owner * owner, OutputEntry * entry);
139             template <class OtherOwner>
140             OutputProxy(Owner * owner, OutputProxy<OtherOwner> const & other);
141
142         private:
143             Owner * owner_;
144             OutputEntry * entry_;
145
146             template <class OtherOwner> friend class OutputProxy;
147         };
148
149         ///////////////////////////////////////////////////////////////////////////
150         ///\name Accessing the current value
151         ///\{
152
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
157
158         virtual unsigned rank() const;  ///< Return collectors rank value
159                                         /**< \returns number of basic values collected into each new
160                                              value by this collector. */
161
162         ///\}
163         ///////////////////////////////////////////////////////////////////////////
164         ///\name Child collectors
165         ///\{
166
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
185                                              references */
186
187         Collector & collect(unsigned rank); ///< Register a new collector
188                                         /**< Adds a collector collecting \a rank values into each
189                                              combined value.
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. */
194
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
199                                              \c *this. */
200
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
205                                              collector ranks. */
206
207         ///\}
208         ///////////////////////////////////////////////////////////////////////////
209         ///\name Result generation
210
211         OutputProxy<StatisticsBase> output(unsigned n = 1u);
212                                         ///< Register output
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:
218                                              \code
219                                              stats.output(4u).connect(
220                                                  senf::StatisticsLogger());
221                                              \endcode
222                                              \param[in] n size of sliding average window */
223
224         ///\}
225         ///////////////////////////////////////////////////////////////////////////
226         // Exceptions
227
228         struct InvalidRankException : public senf::Exception
229         { InvalidRankException() : senf::Exception("Invalid rank value") {} };
230
231         struct DuplicateRankException : public senf::Exception
232         { DuplicateRankException() : senf::Exception("Duplicate rank value") {} };
233
234         void consoleList(unsigned level, std::ostream & os) const;
235
236     protected:
237         StatisticsBase();
238         virtual ~StatisticsBase();
239         void enter(unsigned n, float min, float avg, float max, float dev);
240
241     private:
242         virtual Statistics & v_base() = 0;
243         virtual std::string v_path() const = 0;
244
245         void generateOutput();
246
247         float min_;
248         float avg_;
249         float max_;
250         float dev_;
251         Children children_;
252
253         struct QueueEntry {
254             float min;
255             float avg;
256             float max;
257             float dev;
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_) {}
261         };
262         typedef std::deque<QueueEntry> Queue;
263         Queue queue_;
264
265         struct OutputEntry {
266             struct TargetBase
267             {
268                 explicit TargetBase(std::string const & label_) : label (label_) {}
269                 virtual ~TargetBase() {};
270                 std::string label;
271             };
272
273             template <class PTarget>
274             struct Target : public TargetBase
275             {
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) {}
281             };
282
283             OutputEntry();
284             explicit OutputEntry(unsigned n_);
285             OutputEntry(const OutputEntry& other);
286             OutputEntry& operator=(const OutputEntry& other);
287
288             void initDir();
289             void consoleList(std::ostream & os);
290
291             unsigned n;
292             float min;
293             float avg;
294             float max;
295             float dev;
296
297             boost::signal<void(float,float,float,float)> signal;
298             boost::ptr_vector<TargetBase> targets_;
299
300             senf::console::ScopedDirectory<> dir;
301         };
302         typedef std::map<unsigned, OutputEntry> OutputMap;
303         OutputMap outputs_;
304         unsigned maxQueueLen_;
305     };
306
307     /** \brief  Collect statistics and generate %log messages
308
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.
311
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.
315
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
318         example:
319
320         Let us assume, that Statistics::operator() is called every 100ms. We can than build a chain
321         of collectors:
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
326         \li ... and so on
327
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.
330
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.
334
335         Now to turn all this into code:
336
337         \code
338         senf::ppi::module::RateAnalyzer rateAnalyzer;
339         senf::Statistics packetStats;
340
341         rateAnalyzer.signals.packetsPerSecond.connect(boost::ref(packetStats));
342
343         packetStats                      // called 10 times / second
344             .collect(10u)                // seconds
345             .collect(60u)                // minutes
346             .collect(60u);               // hours
347
348         packetStats[10u].collect(100u);  // 100 seconds
349
350         rateAnalyzer.startStatistics(senf::ClockService::milliseconds(100u));
351         \endcode
352
353         This code will collect the statistics as described in the Example above. However, no output
354         will be generated.
355
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.
358
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.
364
365         To achieve this, we can augment above code in the following way:
366
367         \code
368         packetStats
369                 .output( 5u).connect(senf::StatisicsLogger("pps 100ms 5"))
370             .collect(10u)
371                 .output(   ).noconnect()
372                 .output(10u).connect(senf::StatisicsLogger("pps 1s 10"))
373                 .output(60u).connect(senf::StatisicsLogger("pps 1s 60"))
374             .collect(60u)
375                 .output(   ).connect(senf::StatisicsLogger("pps 1min 1"))
376             .collect(60u)
377                 .output(   ).connect(senf::StatisicsLogger("pps 1h 1"));
378
379         packetStats.output(5u).connect(
380             senf::StatisticsLogger<senf::log::Debug, senf::log::VERBOSE>("pps"));
381
382         senf::log::FileTarget statslog ("stats.log");
383
384         statslog.showArea(false);
385         statslog.showLevel(false);
386         statslog.tag("");
387         statslog.timeFormat("");
388
389         statslog.route<senf::StatisticsStream>();
390         \endcode
391
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.
395
396         It is also possible to skip sending the output to any target or send one output to several
397         targets.
398
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
401         arbitrary.
402
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):
407         <pre>
408         0000000000.000000000 pps 100ms 5 43.3 43.3 43.3
409         0000000000.010311928 pps 100ms 5 42.5 42.5 42.5
410         ...
411         0000000001.002413391 pps 100ms 5 62.0 62.0 62.0
412         0000000001.003920018 pps 1s 1 42.1 53.4 62.0
413         ...
414         </pre>
415         (the nanosecond values will of course be somewhat different ...)
416
417         \see senf::StatisticsBase::OutputProxy for the output proxy (connect) interface
418         \ingroup senf_statistics
419      */
420     class Statistics
421         : public StatisticsBase, boost::noncopyable
422     {
423     public:
424 #ifndef SENF_DISABLE_CONSOLE
425         console::ScopedDirectory<Statistics> dir;
426 #endif
427
428         Statistics();
429
430         void operator()(unsigned n, float min, float avg, float max, float dev);
431                                         ///< Enter new data
432                                         /**< This member must be called whenever new data is
433                                              available.
434
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.
439
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.
444
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
451                                              non-periodic.
452
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. */
466
467         StatisticsBase::OutputProxy<Statistics> output(unsigned n = 1u);
468
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);
473
474     private:
475         Statistics & v_base();
476         std::string v_path() const;
477     };
478
479     /** \brief Accumulated statistics collector
480
481         This class collects accumulated statistics. It is automatically created by
482         senf::Statistics::collect()
483
484         \see senf::Statistics
485      */
486     class Collector : public StatisticsBase
487     {
488     public:
489         virtual unsigned rank() const;
490
491         StatisticsBase::OutputProxy<Collector> output(unsigned n = 1u);
492
493     private:
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;
498
499         unsigned rank_;
500         unsigned i_;
501         float accMin_;
502         float accSum_;
503         float accSumSq_;
504         float accMax_;
505         StatisticsBase * owner_;
506
507         friend class StatisticsBase;
508     };
509
510 }
511
512 ///////////////////////////////hh.e////////////////////////////////////////
513 #include "Statistics.cci"
514 //#include "Statistics.ct"
515 #include "Statistics.cti"
516 #endif
517
518 \f
519 // Local Variables:
520 // mode: c++
521 // fill-column: 100
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"
527 // End: