added data() member in StatisticsBase class to directly get a senf::StatisticsData
[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         StatisticsData data();      ///< Get the Statistics data as senf::StatisticsData
233                                     /**< Return a Statistic Data object containing values
234                                          from this instance.
235                                      */
236
237
238         //-////////////////////////////////////////////////////////////////////////
239         // Exceptions
240
241         struct InvalidRankException : public senf::Exception
242         { InvalidRankException() : senf::Exception("Invalid rank value") {} };
243
244         struct DuplicateRankException : public senf::Exception
245         { DuplicateRankException() : senf::Exception("Duplicate rank value") {} };
246
247         void consoleList(unsigned level, std::ostream & os) const;
248
249     protected:
250         StatisticsBase();
251         virtual ~StatisticsBase();
252         void enter(unsigned n, float min, float avg, float max, float dev);
253
254     private:
255         virtual Statistics & v_base() = 0;
256         virtual std::string v_path() const = 0;
257
258         void generateOutput();
259
260         float min_;
261         float avg_;
262         float max_;
263         float dev_;
264         Children children_;
265
266         struct QueueEntry {
267             float min;
268             float avg;
269             float max;
270             float dev;
271             QueueEntry() : min(), avg(), max(), dev() {}
272             QueueEntry(float min_, float avg_, float max_, float dev_)
273                 : min(min_), avg(avg_), max(max_), dev(dev_) {}
274         };
275         typedef std::deque<QueueEntry> Queue;
276         Queue queue_;
277
278         struct OutputEntry {
279             struct TargetBase
280             {
281                 explicit TargetBase(std::string const & label_) : label (label_) {}
282                 virtual ~TargetBase() {};
283                 std::string label;
284             };
285
286             template <class PTarget>
287             struct Target : public TargetBase
288             {
289                 boost::scoped_ptr<PTarget> target_;
290                 Target(std::auto_ptr<PTarget> target, std::string const & label)
291                     : TargetBase (label), target_ (target.release()) {}
292                 explicit Target(std::string const & label)
293                     : TargetBase (label), target_ (0) {}
294             };
295
296             OutputEntry();
297             explicit OutputEntry(unsigned n_);
298             OutputEntry(const OutputEntry& other);
299             OutputEntry& operator=(const OutputEntry& other);
300
301             void initDir();
302             void consoleList(std::ostream & os);
303
304             unsigned n;
305             float min;
306             float avg;
307             float max;
308             float dev;
309
310             boost::signal<void(float,float,float,float)> signal;
311             boost::ptr_vector<TargetBase> targets_;
312
313             senf::console::ScopedDirectory<> dir;
314         };
315         typedef std::map<unsigned, OutputEntry> OutputMap;
316         OutputMap outputs_;
317         unsigned maxQueueLen_;
318     };
319
320     /** \brief  Collect statistics and generate %log messages
321
322         The Statistics class collects information about a single value. The assumption is, that
323         Statistics::operator() is called \e periodically to supply new data.
324
325         Each data entry is comprised of a minimum, average and maximum value. These values should
326         describe the value of whatever parameter is analyzed by a Statistics instance over the last
327         period. The length of the period is defined by the interval, at which data is entered.
328
329         The Statistics class combines successive data values into ever larger groups. These groups
330         form a tree where each node combines a fixed number of data values of it's parent node. An
331         example:
332
333         Let us assume, that Statistics::operator() is called every 100ms. We can than build a chain
334         of collectors:
335         \li The basic statistics module provides information with a resolution of 1/10th of a second
336         \li A collector collecting 10 values provides information with 1 second resolution
337         \li The next collector collects 60 values and provides a resolution of 1 minute
338         \li Again the next collector collects 60 values and provides a resolution of 1 hour
339         \li ... and so on
340
341         This way, we create a hierarchy of values. Each collector manages a minimum, average and
342         maximum value always created over it's the last complete interval.
343
344         It is possible to have more than one collector based on the same basic interval. In above
345         scenario, we might want to add another collector which for example collects information
346         with a 100 second scale. This is possible and changes the list of collectors into a tree.
347
348         Now to turn all this into code:
349
350         \code
351         senf::ppi::module::RateAnalyzer rateAnalyzer;
352         senf::Statistics packetStats;
353
354         rateAnalyzer.signals.packetsPerSecond.connect(boost::ref(packetStats));
355
356         packetStats                      // called 10 times / second
357             .collect(10u)                // seconds
358             .collect(60u)                // minutes
359             .collect(60u);               // hours
360
361         packetStats[10u].collect(100u);  // 100 seconds
362
363         rateAnalyzer.startStatistics(senf::ClockService::milliseconds(100u));
364         \endcode
365
366         This code will collect the statistics as described in the Example above. However, no output
367         will be generated.
368
369         For every collector, any number of outputs may be defined. Each output consists of the
370         number of values to calculate a sliding average over and an identifying label.
371
372         Lets say, we want to produce the following outputs:
373         \li A sliding average of 5 values based on the raw 1/10th second data.
374         \li Three different outputs from the seconds staistics: current value without average,
375             sliding average over 10 seconds and sliding average over 60 seconds.
376         \li Output the minutes and hourly value without averaging.
377
378         To achieve this, we can augment above code in the following way:
379
380         \code
381         packetStats
382                 .output( 5u).connect(senf::StatisicsLogger("pps 100ms 5"))
383             .collect(10u)
384                 .output(   ).noconnect()
385                 .output(10u).connect(senf::StatisicsLogger("pps 1s 10"))
386                 .output(60u).connect(senf::StatisicsLogger("pps 1s 60"))
387             .collect(60u)
388                 .output(   ).connect(senf::StatisicsLogger("pps 1min 1"))
389             .collect(60u)
390                 .output(   ).connect(senf::StatisicsLogger("pps 1h 1"));
391
392         packetStats.output(5u).connect(
393             senf::StatisticsLogger<senf::log::Debug, senf::log::VERBOSE>("pps"));
394
395         senf::log::FileTarget statslog ("stats.log");
396
397         statslog.showArea(false);
398         statslog.showLevel(false);
399         statslog.tag("");
400         statslog.timeFormat("");
401
402         statslog.route<senf::StatisticsStream>();
403         \endcode
404
405         We use a StatisticsLogger to send the %log messages to the senf::StatisticsStream %log
406         stream. The stream, area an level to send the statistics %log messages to may be configured
407         using template arguments to StatisticsLogger.
408
409         It is also possible to skip sending the output to any target or send one output to several
410         targets.
411
412         Here we have opted to use a label which explicitly describes the name of the variable, the
413         basic interval and the size of the sliding average window. However, the label is completely
414         arbitrary.
415
416         All output is generated using the Senf Logger on the senf::StatisticsStream %log stream. In
417         the example above, we have configured a special logfile \c stats.log which contains the
418         statistics values each prefixed with a timestamp in nanoseconds (but no further information
419         like %log level or tag):
420         <pre>
421         0000000000.000000000 pps 100ms 5 43.3 43.3 43.3
422         0000000000.010311928 pps 100ms 5 42.5 42.5 42.5
423         ...
424         0000000001.002413391 pps 100ms 5 62.0 62.0 62.0
425         0000000001.003920018 pps 1s 1 42.1 53.4 62.0
426         ...
427         </pre>
428         (the nanosecond values will of course be somewhat different ...)
429
430         \see senf::StatisticsBase::OutputProxy for the output proxy (connect) interface
431         \ingroup senf_statistics
432      */
433     class Statistics
434         : public StatisticsBase, boost::noncopyable
435     {
436     public:
437 #ifndef SENF_DISABLE_CONSOLE
438         console::ScopedDirectory<Statistics> dir;
439 #endif
440
441         Statistics();
442
443         void operator()(unsigned n, float min, float avg, float max, float dev);
444                                         ///< Enter new data
445                                         /**< This member must be called whenever new data is
446                                              available.
447
448                                              If \a min and \a max values are not available, this
449                                              member should be called with \a min, \a avg and \a max
450                                              set to the same value. If no error estimate is
451                                              available, call with \a dev = 0.
452
453                                              In the most common case, this member is to be called
454                                              periodically and \a n will be 1 on all calls. The
455                                              interval, at which this member is called then defines
456                                              the statistics time scale.
457
458                                              Calling with \a n > 1 will submit the value \a n
459                                              times. This makes it possible to aggregate multiple
460                                              time slices into a single call. This does not change
461                                              the basic time scale but can change the number of
462                                              submits per unit time. If the basic time slice is
463                                              small, this allows to submit values almost arbitrarily
464                                              non-periodic.
465
466                                              \param[in] n number of time-slices
467                                              \param[in] min minimal data value since last call
468                                              \param[in] avg average data value since last call
469                                              \param[in] max maximal data values since last call
470                                              \param[in] dev standard deviation of avg value */
471         void operator()(float min, float avg, float max, float dev=0.0f);
472                                         ///< Same as operator() with \a n==1
473                                         /**< Provided so a Statistics instance can be directly used
474                                              as a signal target. */
475         void operator()(float value, float dev=0.0f);
476                                         ///< Same as operator() with \a min == \a avg == \a max
477                                         /**< Provided so a Statistics instance can be directly used
478                                              as a signal target. */
479         void operator()(StatisticsData const & data);
480                                         ///< Same as operator(), but imports statistics data from a StatisticsData object
481                                         /**< Provided so a Statistics instance can be directly used
482                                              as a signal target. */
483         template <class Value>
484         void operator()(unsigned n, StatisticAccumulator<Value> & sa);
485                                         ///< Same as operator() gathers values from StatisticAccumulator
486                                         /**< Provided so a Statistics instance can be directly used
487                                              as a signal target. Caution: Clears values in
488                                              StatisticAccumulator afterwards
489                                              \param[in] n number of time-slices
490                                              \param[in] sa StatisticAccumulator*/
491
492         StatisticsBase::OutputProxy<Statistics> output(unsigned n = 1u);
493
494         void consoleList(std::ostream & os);
495         void consoleCollect(std::vector<unsigned> & ranks);
496         boost::shared_ptr<senf::console::DirectoryNode> consoleOutput(
497             std::vector<unsigned> & ranks, unsigned window);
498
499     private:
500         Statistics & v_base();
501         std::string v_path() const;
502     };
503
504     /** \brief Accumulated statistics collector
505
506         This class collects accumulated statistics. It is automatically created by
507         senf::Statistics::collect()
508
509         \see senf::Statistics
510      */
511     class Collector : public StatisticsBase
512     {
513     public:
514         virtual unsigned rank() const;
515
516         StatisticsBase::OutputProxy<Collector> output(unsigned n = 1u);
517
518     private:
519         Collector(StatisticsBase * owner, unsigned rank);
520         void enter(unsigned n, float min, float avg, float max, float dev);
521         Statistics & v_base();
522         std::string v_path() const;
523
524         unsigned rank_;
525         unsigned i_;
526         float accMin_;
527         float accSum_;
528         float accSumSq_;
529         float accMax_;
530         StatisticsBase * owner_;
531
532         friend class StatisticsBase;
533     };
534
535 }
536
537 //-/////////////////////////////////////////////////////////////////////////////////////////////////
538 #include "Statistics.cci"
539 //#include "Statistics.ct"
540 #include "Statistics.cti"
541 #endif
542
543 \f
544 // Local Variables:
545 // mode: c++
546 // fill-column: 100
547 // comment-column: 40
548 // c-file-style: "senf"
549 // indent-tabs-mode: nil
550 // ispell-local-dictionary: "american"
551 // compile-command: "scons -u test"
552 // End: