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