8217a22cbf274d195ebaf8171624ee1f12dd370d
[senf.git] / senf / Utils / Statistics.hh
1 // $Id$
2 //
3 // Copyright (C) 2008
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 //
6 // The contents of this file are subject to the Fraunhofer FOKUS Public License
7 // Version 1.0 (the "License"); you may not use this file except in compliance
8 // with the License. You may obtain a copy of the License at 
9 // http://senf.berlios.de/license.html
10 //
11 // The Fraunhofer FOKUS Public License Version 1.0 is based on, 
12 // but modifies the Mozilla Public License Version 1.1.
13 // See the full license text for the amendments.
14 //
15 // Software distributed under the License is distributed on an "AS IS" basis, 
16 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
17 // for the specific language governing rights and limitations under the License.
18 //
19 // The Original Code is Fraunhofer FOKUS code.
20 //
21 // The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V. 
22 // (registered association), Hansastraße 27 c, 80686 Munich, Germany.
23 // All Rights Reserved.
24 //
25 // Contributor(s):
26 //   Stefan Bund <g0dil@berlios.de>
27
28 /** \file
29     \brief Statistics public header */
30
31 #ifndef HH_SENF_Utils_Statistics_
32 #define HH_SENF_Utils_Statistics_ 1
33
34 // Custom includes
35 #include <map>
36 #include <vector>
37 #include <deque>
38 #include <boost/iterator/transform_iterator.hpp>
39 #include <boost/range/iterator_range.hpp>
40 #include <boost/utility.hpp>
41 #include <boost/ptr_container/ptr_vector.hpp>
42 #include <boost/signals.hpp>
43 #include <senf/Utils/Logger/Logger.hh>
44 #include <senf/Utils/Console/ScopedDirectory.hh>
45 #include "StatisticAccumulator.hh"
46 #include "Exception.hh"
47
48 //#include "Statistics.mpp"
49 //-/////////////////////////////////////////////////////////////////////////////////////////////////
50
51 namespace senf {
52
53     /** \defgroup senf_statistics Statistics
54
55         The statistics functionality has two parts:
56
57         \li the senf::Statistics class
58         \li the senf::StatisticsAccumulator class
59         \li statistics sources
60
61         Each senf::Statistics instance collects information about a single parameter. Which
62         parameter is set up by connecting the Statistics instance with an arbitrary statistics
63         source.
64
65         %Statistics sources are <a href="http://www.boost.org/doc/libs/release/doc/html/signals.html">
66         Boost Signals</a> which are emitted periodically to provide new data.
67      */
68
69     class Collector;
70     class Statistics;
71
72     /** \brief Internal: Generic Statistics collection */
73     class StatisticsBase
74     {
75         typedef std::map<unsigned, Collector> Children;
76
77         struct Transform {
78             typedef Children::value_type & first_argument_type;
79             typedef Collector & result_type;
80             result_type operator()(first_argument_type i) const;
81         };
82
83         typedef boost::transform_iterator<Transform,Children::iterator> ValueIterator;
84
85         struct OutputEntry;
86
87     public:
88         //-////////////////////////////////////////////////////////////////////////
89         // Types
90
91         typedef boost::iterator_range<ValueIterator> CollectorRange;
92
93         /** \brief Output connection interface
94
95             This class is returned from senf::StatisticsBase::output() and the derived
96             <tt>output()</tt> implementations to allow connecting an output with an arbitrary
97             target.
98
99             There are two types of targets:
100             \li <em>Externally managed targets</em>. These targets live outside of the statistics
101                 module, just a reference to those targets is saved. The target should derive from
102                 <tt>boost::signals::trackable</tt> to ensure they are automatically disconnected
103                 when destroyed.
104             \li <em>Internally managed targets</em>. Those targets are owned by the statistics
105                 module and will be destroyed when the statistics class is destroyed.
106
107             Externally managed targets are passed by non-const reference to connect(), internally
108             managed targets are passed using <tt>std::auto_ptr</tt>.
109
110             A target is any callable object which takes three float values as argument: The current
111             minimum, average and maximum value.
112
113             \code
114             // Simple function as statistics target
115             void collect(float min, float avg, float max)
116                 { ... }
117
118             // Function object
119             struct Collector
120             {
121                 void operator()(float min, float avg, float max, float dev)
122                     { ... }
123             };
124             \endcode
125
126            \ingroup senf_statistics
127          */
128         template <class Owner>
129         class OutputProxy
130         {
131         public:
132             template <class Target> Owner & connect(Target & target,
133                                                     std::string label="") const;
134                                         ///< Connect externally managed target
135             template <class PTarget> Owner & connect(std::auto_ptr<PTarget> target,
136                                                      std::string label="") const;
137                                         ///< Connect internally managed target
138             Owner & noconnect() const;  ///< Don't connect the output
139             senf::console::ScopedDirectory<> & dir() const;
140                                         ///< Get target's console directory
141
142 #ifdef DOXYGEN
143         private:
144 #endif
145             OutputProxy(Owner * owner, OutputEntry * entry);
146             template <class OtherOwner>
147             OutputProxy(Owner * owner, OutputProxy<OtherOwner> const & other);
148
149         private:
150             Owner * owner_;
151             OutputEntry * entry_;
152
153             template <class OtherOwner> friend class OutputProxy;
154         };
155
156         //-////////////////////////////////////////////////////////////////////////
157         ///\name Accessing the current value
158         //\{
159
160         float min() const;              ///< Last min value entered
161         float avg() const;              ///< Last avg value entered
162         float max() const;              ///< Last max value entered
163         float dev() const;              ///< Last dev value entered
164
165         virtual unsigned rank() const;  ///< Return collectors rank value
166                                         /**< \returns number of basic values collected into each new
167                                              value by this collector. */
168
169         //\}
170         //-////////////////////////////////////////////////////////////////////////
171         ///\name Child collectors
172         //\{
173
174         Collector & operator[](unsigned rank);
175                                         ///< Get child collector
176                                         /**< This member will return a reference to the collector
177                                              collecting \a rank values.
178                                              \param[in] rank Number of values the requested
179                                                  collector collects into each combined value.
180                                              \throws InvalidRankException if \a rank is not a valid
181                                                  registered rank value. */
182         Collector const & operator[](unsigned rank) const;
183                                         ///< Get child collector
184                                         /**< This member will return a const reference to the
185                                              collector collecting \a rank values.
186                                              \param[in] rank Number of values the requested
187                                                  collector collects into each combined value.
188                                              \throws InvalidRankException if \a rank is not a valid
189                                                  registered rank value. */
190         CollectorRange collectors();    ///< List all child collectors
191                                         /**< \returns iterator range of child collector
192                                              references */
193
194         Collector & collect(unsigned rank); ///< Register a new collector
195                                         /**< Adds a collector collecting \a rank values into each
196                                              combined value.
197                                              \param[in] rank number of values to collect
198                                              \returns Reference to new collector
199                                              \throws DuplicateRankException if a collector
200                                                  collecting \a rank values already exists. */
201
202         Statistics & base();            ///< Get base statistics object
203                                         /**< Returns the base statistics object. If this is
204                                              a child collector, this will return the outermost
205                                              statistics object, otherwise it will return
206                                              \c *this. */
207
208         std::string path() const;       ///< Get the path to this collector
209                                         /**< Returns the '-'-separated list of collectors up to
210                                              here. If this is the basic statistics object, the value
211                                              is empty, otherwise it is built by joining the
212                                              collector ranks. */
213
214         //\}
215         //-////////////////////////////////////////////////////////////////////////
216         ///\name Result generation
217
218         OutputProxy<StatisticsBase> output(unsigned n = 1u);
219                                         ///< Register output
220                                         /**< This call will request the collector to output
221                                              statistics build by averaging the last \a n
222                                              values. This output is generated for every new value in
223                                              the collector. The output signal can be connected to an
224                                              arbitrary target using the returned proxy. Example:
225                                              \code
226                                              stats.output(4u).connect(
227                                                  senf::StatisticsLogger());
228                                              \endcode
229                                              \param[in] n size of sliding average window */
230
231         //\}
232         //-////////////////////////////////////////////////////////////////////////
233         // Exceptions
234
235         struct InvalidRankException : public senf::Exception
236         { InvalidRankException() : senf::Exception("Invalid rank value") {} };
237
238         struct DuplicateRankException : public senf::Exception
239         { DuplicateRankException() : senf::Exception("Duplicate rank value") {} };
240
241         void consoleList(unsigned level, std::ostream & os) const;
242
243     protected:
244         StatisticsBase();
245         virtual ~StatisticsBase();
246         void enter(unsigned n, float min, float avg, float max, float dev);
247
248     private:
249         virtual Statistics & v_base() = 0;
250         virtual std::string v_path() const = 0;
251
252         void generateOutput();
253
254         float min_;
255         float avg_;
256         float max_;
257         float dev_;
258         Children children_;
259
260         struct QueueEntry {
261             float min;
262             float avg;
263             float max;
264             float dev;
265             QueueEntry() : min(), avg(), max(), dev() {}
266             QueueEntry(float min_, float avg_, float max_, float dev_)
267                 : min(min_), avg(avg_), max(max_), dev(dev_) {}
268         };
269         typedef std::deque<QueueEntry> Queue;
270         Queue queue_;
271
272         struct OutputEntry {
273             struct TargetBase
274             {
275                 explicit TargetBase(std::string const & label_) : label (label_) {}
276                 virtual ~TargetBase() {};
277                 std::string label;
278             };
279
280             template <class PTarget>
281             struct Target : public TargetBase
282             {
283                 boost::scoped_ptr<PTarget> target_;
284                 Target(std::auto_ptr<PTarget> target, std::string const & label)
285                     : TargetBase (label), target_ (target.release()) {}
286                 explicit Target(std::string const & label)
287                     : TargetBase (label), target_ (0) {}
288             };
289
290             OutputEntry();
291             explicit OutputEntry(unsigned n_);
292             OutputEntry(const OutputEntry& other);
293             OutputEntry& operator=(const OutputEntry& other);
294
295             void initDir();
296             void consoleList(std::ostream & os);
297
298             unsigned n;
299             float min;
300             float avg;
301             float max;
302             float dev;
303
304             boost::signal<void(float,float,float,float)> signal;
305             boost::ptr_vector<TargetBase> targets_;
306
307             senf::console::ScopedDirectory<> dir;
308         };
309         typedef std::map<unsigned, OutputEntry> OutputMap;
310         OutputMap outputs_;
311         unsigned maxQueueLen_;
312     };
313
314     /** \brief  Collect statistics and generate %log messages
315
316         The Statistics class collects information about a single value. The assumption is, that
317         Statistics::operator() is called \e periodically to supply new data.
318
319         Each data entry is comprised of a minimum, average and maximum value. These values should
320         describe the value of whatever parameter is analyzed by a Statistics instance over the last
321         period. The length of the period is defined by the interval, at which data is entered.
322
323         The Statistics class combines successive data values into ever larger groups. These groups
324         form a tree where each node combines a fixed number of data values of it's parent node. An
325         example:
326
327         Let us assume, that Statistics::operator() is called every 100ms. We can than build a chain
328         of collectors:
329         \li The basic statistics module provides information with a resolution of 1/10th of a second
330         \li A collector collecting 10 values provides information with 1 second resolution
331         \li The next collector collects 60 values and provides a resolution of 1 minute
332         \li Again the next collector collects 60 values and provides a resolution of 1 hour
333         \li ... and so on
334
335         This way, we create a hierarchy of values. Each collector manages a minimum, average and
336         maximum value always created over it's the last complete interval.
337
338         It is possible to have more than one collector based on the same basic interval. In above
339         scenario, we might want to add another collector which for example collects information
340         with a 100 second scale. This is possible and changes the list of collectors into a tree.
341
342         Now to turn all this into code:
343
344         \code
345         senf::ppi::module::RateAnalyzer rateAnalyzer;
346         senf::Statistics packetStats;
347
348         rateAnalyzer.signals.packetsPerSecond.connect(boost::ref(packetStats));
349
350         packetStats                      // called 10 times / second
351             .collect(10u)                // seconds
352             .collect(60u)                // minutes
353             .collect(60u);               // hours
354
355         packetStats[10u].collect(100u);  // 100 seconds
356
357         rateAnalyzer.startStatistics(senf::ClockService::milliseconds(100u));
358         \endcode
359
360         This code will collect the statistics as described in the Example above. However, no output
361         will be generated.
362
363         For every collector, any number of outputs may be defined. Each output consists of the
364         number of values to calculate a sliding average over and an identifying label.
365
366         Lets say, we want to produce the following outputs:
367         \li A sliding average of 5 values based on the raw 1/10th second data.
368         \li Three different outputs from the seconds staistics: current value without average,
369             sliding average over 10 seconds and sliding average over 60 seconds.
370         \li Output the minutes and hourly value without averaging.
371
372         To achieve this, we can augment above code in the following way:
373
374         \code
375         packetStats
376                 .output( 5u).connect(senf::StatisicsLogger("pps 100ms 5"))
377             .collect(10u)
378                 .output(   ).noconnect()
379                 .output(10u).connect(senf::StatisicsLogger("pps 1s 10"))
380                 .output(60u).connect(senf::StatisicsLogger("pps 1s 60"))
381             .collect(60u)
382                 .output(   ).connect(senf::StatisicsLogger("pps 1min 1"))
383             .collect(60u)
384                 .output(   ).connect(senf::StatisicsLogger("pps 1h 1"));
385
386         packetStats.output(5u).connect(
387             senf::StatisticsLogger<senf::log::Debug, senf::log::VERBOSE>("pps"));
388
389         senf::log::FileTarget statslog ("stats.log");
390
391         statslog.showArea(false);
392         statslog.showLevel(false);
393         statslog.tag("");
394         statslog.timeFormat("");
395
396         statslog.route<senf::StatisticsStream>();
397         \endcode
398
399         We use a StatisticsLogger to send the %log messages to the senf::StatisticsStream %log
400         stream. The stream, area an level to send the statistics %log messages to may be configured
401         using template arguments to StatisticsLogger.
402
403         It is also possible to skip sending the output to any target or send one output to several
404         targets.
405
406         Here we have opted to use a label which explicitly describes the name of the variable, the
407         basic interval and the size of the sliding average window. However, the label is completely
408         arbitrary.
409
410         All output is generated using the Senf Logger on the senf::StatisticsStream %log stream. In
411         the example above, we have configured a special logfile \c stats.log which contains the
412         statistics values each prefixed with a timestamp in nanoseconds (but no further information
413         like %log level or tag):
414         <pre>
415         0000000000.000000000 pps 100ms 5 43.3 43.3 43.3
416         0000000000.010311928 pps 100ms 5 42.5 42.5 42.5
417         ...
418         0000000001.002413391 pps 100ms 5 62.0 62.0 62.0
419         0000000001.003920018 pps 1s 1 42.1 53.4 62.0
420         ...
421         </pre>
422         (the nanosecond values will of course be somewhat different ...)
423
424         \see senf::StatisticsBase::OutputProxy for the output proxy (connect) interface
425         \ingroup senf_statistics
426      */
427     class Statistics
428         : public StatisticsBase, boost::noncopyable
429     {
430     public:
431 #ifndef SENF_DISABLE_CONSOLE
432         console::ScopedDirectory<Statistics> dir;
433 #endif
434
435         Statistics();
436
437         void operator()(unsigned n, float min, float avg, float max, float dev);
438                                         ///< Enter new data
439                                         /**< This member must be called whenever new data is
440                                              available.
441
442                                              If \a min and \a max values are not available, this
443                                              member should be called with \a min, \a avg and \a max
444                                              set to the same value. If no error estimate is
445                                              available, call with \a dev = 0.
446
447                                              In the most common case, this member is to be called
448                                              periodically and \a n will be 1 on all calls. The
449                                              interval, at which this member is called then defines
450                                              the statistics time scale.
451
452                                              Calling with \a n > 1 will submit the value \a n
453                                              times. This makes it possible to aggregate multiple
454                                              time slices into a single call. This does not change
455                                              the basic time scale but can change the number of
456                                              submits per unit time. If the basic time slice is
457                                              small, this allows to submit values almost arbitrarily
458                                              non-periodic.
459
460                                              \param[in] n number of time-slices
461                                              \param[in] min minimal data value since last call
462                                              \param[in] avg average data value since last call
463                                              \param[in] max maximal data values since last call
464                                              \param[in] dev standard deviation of avg value */
465         void operator()(float min, float avg, float max, float dev=0.0f);
466                                         ///< Same as operator() with \a n==1
467                                         /**< Provided so a Statistics instance can be directly used
468                                              as a signal target. */
469         void operator()(float value, float dev=0.0f);
470                                         ///< Same as operator() with \a min == \a avg == \a max
471                                         /**< Provided so a Statistics instance can be directly used
472                                              as a signal target. */
473         void operator()(StatisticsData const & data);
474                                         ///< Same as operator(), but imports statistics data from a StatisticsData object
475                                         /**< Provided so a Statistics instance can be directly used
476                                              as a signal target. */
477         template <class Value>
478         void operator()(unsigned n, StatisticAccumulator<Value> & sa);
479                                         ///< Same as operator() gathers values from StatisticAccumulator
480                                         /**< Provided so a Statistics instance can be directly used
481                                              as a signal target. Caution: Clears values in
482                                              StatisticAccumulator afterwards
483                                              \param[in] n number of time-slices
484                                              \param[in] sa StatisticAccumulator*/
485
486         StatisticsBase::OutputProxy<Statistics> output(unsigned n = 1u);
487
488         void consoleList(std::ostream & os);
489         void consoleCollect(std::vector<unsigned> & ranks);
490         boost::shared_ptr<senf::console::DirectoryNode> consoleOutput(
491             std::vector<unsigned> & ranks, unsigned window);
492
493     private:
494         Statistics & v_base();
495         std::string v_path() const;
496     };
497
498     /** \brief Accumulated statistics collector
499
500         This class collects accumulated statistics. It is automatically created by
501         senf::Statistics::collect()
502
503         \see senf::Statistics
504      */
505     class Collector : public StatisticsBase
506     {
507     public:
508         virtual unsigned rank() const;
509
510         StatisticsBase::OutputProxy<Collector> output(unsigned n = 1u);
511
512     private:
513         Collector(StatisticsBase * owner, unsigned rank);
514         void enter(unsigned n, float min, float avg, float max, float dev);
515         Statistics & v_base();
516         std::string v_path() const;
517
518         unsigned rank_;
519         unsigned i_;
520         float accMin_;
521         float accSum_;
522         float accSumSq_;
523         float accMax_;
524         StatisticsBase * owner_;
525
526         friend class StatisticsBase;
527     };
528
529 }
530
531 //-/////////////////////////////////////////////////////////////////////////////////////////////////
532 #include "Statistics.cci"
533 //#include "Statistics.ct"
534 #include "Statistics.cti"
535 #endif
536
537 \f
538 // Local Variables:
539 // mode: c++
540 // fill-column: 100
541 // comment-column: 40
542 // c-file-style: "senf"
543 // indent-tabs-mode: nil
544 // ispell-local-dictionary: "american"
545 // compile-command: "scons -u test"
546 // End: