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