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;
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;
85 //-////////////////////////////////////////////////////////////////////////
88 typedef boost::iterator_range<collector_iterator> CollectorRange;
89 typedef boost::iterator_range<collector_const_iterator> const_CollectorRange;
91 /** \brief Output connection interface
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
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
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.
105 Externally managed targets are passed by non-const reference to connect(), internally
106 managed targets are passed using <tt>std::auto_ptr</tt>.
108 A target is any callable object which takes three float values as argument: The current
109 minimum, average and maximum value.
112 // Simple function as statistics target
113 void collect(float min, float avg, float max)
119 void operator()(float min, float avg, float max, float dev)
124 \ingroup senf_statistics
126 template <class Owner>
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
143 OutputProxy(Owner * owner, OutputEntry * entry);
144 template <class OtherOwner>
145 OutputProxy(Owner * owner, OutputProxy<OtherOwner> const & other);
149 OutputEntry * entry_;
151 template <class OtherOwner> friend class OutputProxy;
154 //-////////////////////////////////////////////////////////////////////////
155 ///\name Accessing the current value
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
163 virtual unsigned rank() const; ///< Return collectors rank value
164 /**< \returns number of basic values collected into each new
165 value by this collector. */
168 //-////////////////////////////////////////////////////////////////////////
169 ///\name Child collectors
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
191 const_CollectorRange collectors() const;
192 ///< List all child collectors
193 /**< \returns const iterator range of child collector
196 Collector & collect(unsigned rank); ///< Register a new collector
197 /**< Adds a collector collecting \a rank values into each
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. */
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
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
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
222 //-////////////////////////////////////////////////////////////////////////
223 ///\name Result generation
225 OutputProxy<StatisticsBase> output(unsigned n = 1u);
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:
233 stats.output(4u).connect(
234 senf::StatisticsLogger());
236 \param[in] n size of sliding average window */
239 StatisticsData data() const; ///< Get the Statistics data as senf::StatisticsData
240 /**< Return a Statistic Data object containing values
241 from this instance. */
244 //-////////////////////////////////////////////////////////////////////////
247 struct InvalidRankException : public senf::Exception
248 { InvalidRankException() : senf::Exception("Invalid rank value") {} };
250 struct DuplicateRankException : public senf::Exception
251 { DuplicateRankException() : senf::Exception("Duplicate rank value") {} };
253 void consoleList(unsigned level, std::ostream & os) const;
257 virtual ~StatisticsBase();
258 void enter(unsigned n, float min, float avg, float max, float dev);
261 virtual Statistics & v_base() = 0;
262 virtual std::string v_path() const = 0;
264 void generateOutput();
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_) {}
281 typedef std::deque<QueueEntry> Queue;
287 explicit TargetBase(std::string const & label_) : label (label_) {}
288 virtual ~TargetBase() {};
292 template <class PTarget>
293 struct Target : public TargetBase
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) {}
303 explicit OutputEntry(unsigned n_);
304 OutputEntry(const OutputEntry& other);
305 OutputEntry& operator=(const OutputEntry& other);
308 void consoleList(std::ostream & os);
316 boost::signal<void(float,float,float,float)> signal;
317 boost::ptr_vector<TargetBase> targets_;
319 senf::console::ScopedDirectory<> dir;
321 typedef std::map<unsigned, OutputEntry> OutputMap;
323 unsigned maxQueueLen_;
326 /** \brief Collect statistics and generate %log messages
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.
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.
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
339 Let us assume, that Statistics::operator() is called every 100ms. We can than build a chain
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
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.
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.
354 Now to turn all this into code:
357 senf::ppi::module::RateAnalyzer rateAnalyzer;
358 senf::Statistics packetStats;
360 rateAnalyzer.signals.packetsPerSecond.connect(boost::ref(packetStats));
362 packetStats // called 10 times / second
363 .collect(10u) // seconds
364 .collect(60u) // minutes
365 .collect(60u); // hours
367 packetStats[10u].collect(100u); // 100 seconds
369 rateAnalyzer.startStatistics(senf::ClockService::milliseconds(100u));
372 This code will collect the statistics as described in the Example above. However, no output
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.
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.
384 To achieve this, we can augment above code in the following way:
388 .output( 5u).connect(senf::StatisicsLogger("pps 100ms 5"))
390 .output( ).noconnect()
391 .output(10u).connect(senf::StatisicsLogger("pps 1s 10"))
392 .output(60u).connect(senf::StatisicsLogger("pps 1s 60"))
394 .output( ).connect(senf::StatisicsLogger("pps 1min 1"))
396 .output( ).connect(senf::StatisicsLogger("pps 1h 1"));
398 packetStats.output(5u).connect(
399 senf::StatisticsLogger<senf::log::Debug, senf::log::VERBOSE>("pps"));
401 senf::log::FileTarget statslog ("stats.log");
403 statslog.showArea(false);
404 statslog.showLevel(false);
406 statslog.timeFormat("");
408 statslog.route<senf::StatisticsStream>();
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.
415 It is also possible to skip sending the output to any target or send one output to several
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
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):
427 0000000000.000000000 pps 100ms 5 43.3 43.3 43.3
428 0000000000.010311928 pps 100ms 5 42.5 42.5 42.5
430 0000000001.002413391 pps 100ms 5 62.0 62.0 62.0
431 0000000001.003920018 pps 1s 1 42.1 53.4 62.0
434 (the nanosecond values will of course be somewhat different ...)
436 \see senf::StatisticsBase::OutputProxy for the output proxy (connect) interface
437 \ingroup senf_statistics
440 : public StatisticsBase, boost::noncopyable
443 #ifndef SENF_DISABLE_CONSOLE
444 console::ScopedDirectory<Statistics> dir;
449 void operator()(unsigned n, float min, float avg, float max, float dev);
451 /**< This member must be called whenever new data is
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.
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.
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
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*/
498 StatisticsBase::OutputProxy<Statistics> output(unsigned n = 1u);
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);
506 Statistics & v_base();
507 std::string v_path() const;
510 /** \brief Accumulated statistics collector
512 This class collects accumulated statistics. It is automatically created by
513 senf::Statistics::collect()
515 \see senf::Statistics
517 class Collector : public StatisticsBase
520 virtual unsigned rank() const;
522 StatisticsBase::OutputProxy<Collector> output(unsigned n = 1u);
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;
536 StatisticsBase * owner_;
538 friend class StatisticsBase;
543 //-/////////////////////////////////////////////////////////////////////////////////////////////////
544 #include "Statistics.cci"
545 //#include "Statistics.ct"
546 #include "Statistics.cti"
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"