Whitespce cleanup: Remove whitespace at end-on-line, remove tabs, wrap
[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/1_37_0/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         CollectorRange collectors();    ///< List all child collectors
176                                         /**< \returns iterator range of child collector
177                                              references */
178
179         Collector & collect(unsigned rank); ///< Register a new collector
180                                         /**< Adds a collector collecting \a rank values into each
181                                              combined value.
182                                              \param[in] rank number of values to collect
183                                              \returns Reference to new collector
184                                              \throws DuplicateRankException if a collector
185                                                  collecting \a rank values already exists. */
186
187         Statistics & base();            ///< Get base statistics object
188                                         /**< Returns the base statistics object. If this is
189                                              a child collector, this will return the outermost
190                                              statistics object, otherwise it will return
191                                              \c *this. */
192
193         std::string path() const;       ///< Get the path to this collector
194                                         /**< Returns the '-'-separated list of collectors up to
195                                              here. If this is the basic statistics object, the value
196                                              is empty, otherwise it is built by joining the
197                                              collector ranks. */
198
199         ///\}
200         ///////////////////////////////////////////////////////////////////////////
201         ///\name Result generation
202
203         OutputProxy<StatisticsBase> output(unsigned n = 1u);
204                                         ///< Register output
205                                         /**< This call will request the collector to output
206                                              statistics build by averaging the last \a n
207                                              values. This output is generated for every new value in
208                                              the collector. The output signal can be connected to an
209                                              arbitrary target using the returned proxy. Example:
210                                              \code
211                                              stats.output(4u).connect(
212                                                  senf::StatisticsLogger());
213                                              \endcode
214                                              \param[in] n size of sliding average window */
215
216         ///\}
217         ///////////////////////////////////////////////////////////////////////////
218         // Exceptions
219
220         struct InvalidRankException : public senf::Exception
221         { InvalidRankException() : senf::Exception("Invalid rank value") {} };
222
223         struct DuplicateRankException : public senf::Exception
224         { DuplicateRankException() : senf::Exception("Duplicate rank value") {} };
225
226         void consoleList(unsigned level, std::ostream & os) const;
227
228     protected:
229         StatisticsBase();
230         virtual ~StatisticsBase();
231         void enter(float min, float avg, float max, float dev);
232
233     private:
234         virtual Statistics & v_base() = 0;
235         virtual std::string v_path() const = 0;
236
237         void generateOutput();
238         void signalChildren();
239
240         float min_;
241         float avg_;
242         float max_;
243         float dev_;
244         Children children_;
245
246         struct QueueEntry {
247             float min;
248             float avg;
249             float max;
250             float dev;
251             QueueEntry() : min(), avg(), max(), dev() {}
252             QueueEntry(float min_, float avg_, float max_, float dev_)
253                 : min(min_), avg(avg_), max(max_), dev(dev_) {}
254         };
255         typedef std::deque<QueueEntry> Queue;
256         Queue queue_;
257
258         struct OutputEntry {
259             struct TargetBase
260             {
261                 explicit TargetBase(std::string const & label_) : label (label_) {}
262                 virtual ~TargetBase() {};
263                 std::string label;
264             };
265
266             template <class PTarget>
267             struct Target : public TargetBase
268             {
269                 boost::scoped_ptr<PTarget> target_;
270                 Target(std::auto_ptr<PTarget> target, std::string const & label)
271                     : TargetBase (label), target_ (target.release()) {}
272                 explicit Target(std::string const & label)
273                     : TargetBase (label), target_ (0) {}
274             };
275
276             OutputEntry();
277             explicit OutputEntry(unsigned n_);
278             OutputEntry(const OutputEntry& other);
279             OutputEntry& operator=(const OutputEntry& other);
280
281             void initDir();
282             void consoleList(std::ostream & os);
283
284             unsigned n;
285             float min;
286             float avg;
287             float max;
288             float dev;
289
290             boost::signal<void(float,float,float,float)> signal;
291             boost::ptr_vector<TargetBase> targets_;
292
293             senf::console::ScopedDirectory<> dir;
294         };
295         typedef std::map<unsigned, OutputEntry> OutputMap;
296         OutputMap outputs_;
297         unsigned maxQueueLen_;
298     };
299
300     /** \brief  Collect statistics and generate %log messages
301
302         The Statistics class collects information about a single value. The assumption is, that
303         Statistics::operator() is called \e periodically to supply new data.
304
305         Each data entry is comprised of a minimum, average and maximum value. These values should
306         describe the value of whatever parameter is analyzed by a Statistics instance over the last
307         period. The length of the period is defined by the interval, at which data is entered.
308
309         The Statistics class combines successive data values into ever larger groups. These groups
310         form a tree where each node combines a fixed number of data values of it's parent node. An
311         example:
312
313         Let us assume, that Statistics::operator() is called every 100ms. We can than build a chain
314         of collectors:
315         \li The basic statistics module provides information with a resolution of 1/10th of a second
316         \li A collector collecting 10 values provides information with 1 second resolution
317         \li The next collector collects 60 values and provides a resolution of 1 minute
318         \li Again the next collector collects 60 values and provides a resolution of 1 hour
319         \li ... and so on
320
321         This way, we create a hierarchy of values. Each collector manages a minimum, average and
322         maximum value always created over it's the last complete interval.
323
324         It is possible to have more than one collector based on the same basic interval. In above
325         scenario, we might want to add another collector which for example collects information
326         with a 100 second scale. This is possible and changes the list of collectors into a tree.
327
328         Now to turn all this into code:
329
330         \code
331         senf::ppi::module::RateAnalyzer rateAnalyzer;
332         senf::Statistics packetStats;
333
334         rateAnalyzer.signals.packetsPerSecond.connect(boost::ref(packetStats));
335
336         packetStats                      // called 10 times / second
337             .collect(10u)                // seconds
338             .collect(60u)                // minutes
339             .collect(60u);               // hours
340
341         packetStats[10u].collect(100u);  // 100 seconds
342
343         rateAnalyzer.startStatistics(senf::ClockService::milliseconds(100u));
344         \endcode
345
346         This code will collect the statistics as described in the Example above. However, no output
347         will be generated.
348
349         For every collector, any number of outputs may be defined. Each output consists of the
350         number of values to calculate a sliding average over and an identifying label.
351
352         Lets say, we want to produce the following outputs:
353         \li A sliding average of 5 values based on the raw 1/10th second data.
354         \li Three different outputs from the seconds staistics: current value without average,
355             sliding average over 10 seconds and sliding average over 60 seconds.
356         \li Output the minutes and hourly value without averaging.
357
358         To achieve this, we can augment above code in the following way:
359
360         \code
361         packetStats
362                 .output( 5u).connect(senf::StatisicsLogger("pps 100ms 5"))
363             .collect(10u)
364                 .output(   ).noconnect()
365                 .output(10u).connect(senf::StatisicsLogger("pps 1s 10"))
366                 .output(60u).connect(senf::StatisicsLogger("pps 1s 60"))
367             .collect(60u)
368                 .output(   ).connect(senf::StatisicsLogger("pps 1min 1"))
369             .collect(60u)
370                 .output(   ).connect(senf::StatisicsLogger("pps 1h 1"));
371
372         packetStats.output(5u).connect(
373             senf::StatisticsLogger<senf::log::Debug, senf::log::VERBOSE>("pps"));
374
375         senf::log::FileTarget statslog ("stats.log");
376
377         statslog.showArea(false);
378         statslog.showLevel(false);
379         statslog.tag("");
380         statslog.timeFormat("");
381
382         statslog.route<senf::StatisticsStream>();
383         \endcode
384
385         We use a StatisticsLogger to send the %log messages to the senf::StatisticsStream %log
386         stream. The stream, area an level to send the statistics %log messages to may be configured
387         using template arguments to StatisticsLogger.
388
389         It is also possible to skip sending the output to any target or send one output to several
390         targets.
391
392         Here we have opted to use a label which explicitly describes the name of the variable, the
393         basic interval and the size of the sliding average window. However, the label is completely
394         arbitrary.
395
396         All output is generated using the Senf Logger on the senf::StatisticsStream %log stream. In
397         the example above, we have configured a special logfile \c stats.log which contains the
398         statistics values each prefixed with a timestamp in nanoseconds (but no further information
399         like %log level or tag):
400         <pre>
401         0000000000.000000000 pps 100ms 5 43.3 43.3 43.3
402         0000000000.010311928 pps 100ms 5 42.5 42.5 42.5
403         ...
404         0000000001.002413391 pps 100ms 5 62.0 62.0 62.0
405         0000000001.003920018 pps 1s 1 42.1 53.4 62.0
406         ...
407         </pre>
408         (the nanosecond values will of course be somewhat different ...)
409
410         \see senf::StatisticsBase::OutputProxy for the output proxy (connect) interface
411         \ingroup senf_statistics
412      */
413     class Statistics
414         : public StatisticsBase, boost::noncopyable
415     {
416     public:
417 #ifndef SENF_DISABLE_CONSOLE
418         console::ScopedDirectory<Statistics> dir;
419 #endif
420
421         Statistics();
422
423         void operator()(float min, float avg, float max, float dev=0.0f);
424                                         ///< Enter new data
425                                         /**< This member must be called whenever a new data value is
426                                              available. It is important to call this member \e
427                                              periodically. The frequency at which this member is
428                                              called defines the basic statistics time scale.
429
430                                              If \a min and \a max values are not available, this
431                                              member should be called with \a min, \a avg and \a max
432                                              set to the same value.
433
434                                              \param[in] min minimal data value since last call
435                                              \param[in] avg average data value since last call
436                                              \param[in] max maximal data values since last call
437                                              \param[in] dev standard deviation of avg value */
438
439         void operator()(float value, float dev=0.0f);
440                                         ///< Same as enter() with \a min == \a avg == \a max
441                                         /**< Provided so a Statistics instance can be directly used
442                                              as a signal target. */
443
444         StatisticsBase::OutputProxy<Statistics> output(unsigned n = 1u);
445
446         void consoleList(std::ostream & os);
447         void consoleCollect(std::vector<unsigned> & ranks);
448         boost::shared_ptr<senf::console::DirectoryNode> consoleOutput(
449             std::vector<unsigned> & ranks, unsigned window);
450
451     private:
452         Statistics & v_base();
453         std::string v_path() const;
454     };
455
456     /** \brief Accumulated statistics collector
457
458         This class collects accumulated statistics. It is automatically created by
459         senf::Statistics::collect()
460
461         \see senf::Statistics
462      */
463     class Collector : public StatisticsBase
464     {
465     public:
466         virtual unsigned rank() const;
467
468         StatisticsBase::OutputProxy<Collector> output(unsigned n = 1u);
469
470     private:
471         Collector(StatisticsBase * owner, unsigned rank);
472         void enter(float min, float avg, float max, float dev);
473         Statistics & v_base();
474         std::string v_path() const;
475
476         unsigned rank_;
477         unsigned i_;
478         float accMin_;
479         float accSum_;
480         float accSumSq_;
481         float accMax_;
482         StatisticsBase * owner_;
483
484         friend class StatisticsBase;
485     };
486
487 }
488
489 ///////////////////////////////hh.e////////////////////////////////////////
490 #include "Statistics.cci"
491 //#include "Statistics.ct"
492 #include "Statistics.cti"
493 #endif
494
495 \f
496 // Local Variables:
497 // mode: c++
498 // fill-column: 100
499 // comment-column: 40
500 // c-file-style: "senf"
501 // indent-tabs-mode: nil
502 // ispell-local-dictionary: "american"
503 // compile-command: "scons -u test"
504 // End: