4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
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
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.
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.
19 // The Original Code is Fraunhofer FOKUS code.
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.
26 // Stefan Bund <g0dil@berlios.de>
29 \brief Statistics public header */
31 #ifndef HH_SENF_Utils_Statistics_
32 #define HH_SENF_Utils_Statistics_ 1
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"
48 //#include "Statistics.mpp"
49 //-/////////////////////////////////////////////////////////////////////////////////////////////////
53 /** \defgroup senf_statistics Statistics
55 The statistics functionality has two parts:
57 \li the senf::Statistics class
58 \li the senf::StatisticsAccumulator class
59 \li statistics sources
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
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.
72 /** \brief Internal: Generic Statistics collection */
75 typedef std::map<unsigned, Collector> Children;
78 typedef Children::value_type & first_argument_type;
79 typedef Collector & result_type;
80 result_type operator()(first_argument_type i) const;
83 typedef boost::transform_iterator<Transform,Children::iterator> ValueIterator;
88 //-////////////////////////////////////////////////////////////////////////
91 typedef boost::iterator_range<ValueIterator> CollectorRange;
93 /** \brief Output connection interface
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
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
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.
107 Externally managed targets are passed by non-const reference to connect(), internally
108 managed targets are passed using <tt>std::auto_ptr</tt>.
110 A target is any callable object which takes three float values as argument: The current
111 minimum, average and maximum value.
114 // Simple function as statistics target
115 void collect(float min, float avg, float max)
121 void operator()(float min, float avg, float max, float dev)
126 \ingroup senf_statistics
128 template <class Owner>
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
145 OutputProxy(Owner * owner, OutputEntry * entry);
146 template <class OtherOwner>
147 OutputProxy(Owner * owner, OutputProxy<OtherOwner> const & other);
151 OutputEntry * entry_;
153 template <class OtherOwner> friend class OutputProxy;
156 //-////////////////////////////////////////////////////////////////////////
157 ///\name Accessing the current value
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
165 virtual unsigned rank() const; ///< Return collectors rank value
166 /**< \returns number of basic values collected into each new
167 value by this collector. */
170 //-////////////////////////////////////////////////////////////////////////
171 ///\name Child collectors
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
194 Collector & collect(unsigned rank); ///< Register a new collector
195 /**< Adds a collector collecting \a rank values into each
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. */
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
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
215 //-////////////////////////////////////////////////////////////////////////
216 ///\name Result generation
218 OutputProxy<StatisticsBase> output(unsigned n = 1u);
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:
226 stats.output(4u).connect(
227 senf::StatisticsLogger());
229 \param[in] n size of sliding average window */
232 //-////////////////////////////////////////////////////////////////////////
235 struct InvalidRankException : public senf::Exception
236 { InvalidRankException() : senf::Exception("Invalid rank value") {} };
238 struct DuplicateRankException : public senf::Exception
239 { DuplicateRankException() : senf::Exception("Duplicate rank value") {} };
241 void consoleList(unsigned level, std::ostream & os) const;
245 virtual ~StatisticsBase();
246 void enter(unsigned n, float min, float avg, float max, float dev);
249 virtual Statistics & v_base() = 0;
250 virtual std::string v_path() const = 0;
252 void generateOutput();
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_) {}
269 typedef std::deque<QueueEntry> Queue;
275 explicit TargetBase(std::string const & label_) : label (label_) {}
276 virtual ~TargetBase() {};
280 template <class PTarget>
281 struct Target : public TargetBase
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) {}
291 explicit OutputEntry(unsigned n_);
292 OutputEntry(const OutputEntry& other);
293 OutputEntry& operator=(const OutputEntry& other);
296 void consoleList(std::ostream & os);
304 boost::signal<void(float,float,float,float)> signal;
305 boost::ptr_vector<TargetBase> targets_;
307 senf::console::ScopedDirectory<> dir;
309 typedef std::map<unsigned, OutputEntry> OutputMap;
311 unsigned maxQueueLen_;
314 /** \brief Collect statistics and generate %log messages
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.
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.
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
327 Let us assume, that Statistics::operator() is called every 100ms. We can than build a chain
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
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.
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.
342 Now to turn all this into code:
345 senf::ppi::module::RateAnalyzer rateAnalyzer;
346 senf::Statistics packetStats;
348 rateAnalyzer.signals.packetsPerSecond.connect(boost::ref(packetStats));
350 packetStats // called 10 times / second
351 .collect(10u) // seconds
352 .collect(60u) // minutes
353 .collect(60u); // hours
355 packetStats[10u].collect(100u); // 100 seconds
357 rateAnalyzer.startStatistics(senf::ClockService::milliseconds(100u));
360 This code will collect the statistics as described in the Example above. However, no output
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.
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.
372 To achieve this, we can augment above code in the following way:
376 .output( 5u).connect(senf::StatisicsLogger("pps 100ms 5"))
378 .output( ).noconnect()
379 .output(10u).connect(senf::StatisicsLogger("pps 1s 10"))
380 .output(60u).connect(senf::StatisicsLogger("pps 1s 60"))
382 .output( ).connect(senf::StatisicsLogger("pps 1min 1"))
384 .output( ).connect(senf::StatisicsLogger("pps 1h 1"));
386 packetStats.output(5u).connect(
387 senf::StatisticsLogger<senf::log::Debug, senf::log::VERBOSE>("pps"));
389 senf::log::FileTarget statslog ("stats.log");
391 statslog.showArea(false);
392 statslog.showLevel(false);
394 statslog.timeFormat("");
396 statslog.route<senf::StatisticsStream>();
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.
403 It is also possible to skip sending the output to any target or send one output to several
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
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):
415 0000000000.000000000 pps 100ms 5 43.3 43.3 43.3
416 0000000000.010311928 pps 100ms 5 42.5 42.5 42.5
418 0000000001.002413391 pps 100ms 5 62.0 62.0 62.0
419 0000000001.003920018 pps 1s 1 42.1 53.4 62.0
422 (the nanosecond values will of course be somewhat different ...)
424 \see senf::StatisticsBase::OutputProxy for the output proxy (connect) interface
425 \ingroup senf_statistics
428 : public StatisticsBase, boost::noncopyable
431 #ifndef SENF_DISABLE_CONSOLE
432 console::ScopedDirectory<Statistics> dir;
437 void operator()(unsigned n, float min, float avg, float max, float dev);
439 /**< This member must be called whenever new data is
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.
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.
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
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*/
486 StatisticsBase::OutputProxy<Statistics> output(unsigned n = 1u);
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);
494 Statistics & v_base();
495 std::string v_path() const;
498 /** \brief Accumulated statistics collector
500 This class collects accumulated statistics. It is automatically created by
501 senf::Statistics::collect()
503 \see senf::Statistics
505 class Collector : public StatisticsBase
508 virtual unsigned rank() const;
510 StatisticsBase::OutputProxy<Collector> output(unsigned n = 1u);
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;
524 StatisticsBase * owner_;
526 friend class StatisticsBase;
531 //-/////////////////////////////////////////////////////////////////////////////////////////////////
532 #include "Statistics.cci"
533 //#include "Statistics.ct"
534 #include "Statistics.cti"
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"