00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00026 #include "Statistics.hh"
00027
00028
00029
00030 #include <cmath>
00031 #include <cstdlib>
00032 #include <sstream>
00033 #include <senf/Utils/Format.hh>
00034 #include <senf/Utils/Console/STLSupport.hh>
00035 #include "StatisticsTargets.hh"
00036
00037
00038 #define prefix_
00039
00040
00041
00042
00043
00044 prefix_ void senf::StatisticsBase::enter(unsigned n, float min, float avg, float max, float dev)
00045 {
00046 min_ = min;
00047 avg_ = avg;
00048 max_ = max;
00049 dev_ = dev;
00050 for (unsigned i (0); i < n; ++i)
00051 generateOutput();
00052 Children::iterator i (children_.begin());
00053 Children::iterator const i_end (children_.end());
00054 for (; i != i_end; ++i)
00055 i->second.enter(n, min_, avg_, max_, dev_);
00056 }
00057
00058 prefix_ senf::Collector & senf::StatisticsBase::operator[](unsigned rank)
00059 {
00060 Children::iterator i (children_.find(rank));
00061 if (i == children_.end())
00062 throw InvalidRankException();
00063 return i->second;
00064 }
00065
00066 prefix_ senf::Collector const & senf::StatisticsBase::operator[](unsigned rank)
00067 const
00068 {
00069 Children::const_iterator i (children_.find(rank));
00070 if (i == children_.end())
00071 throw InvalidRankException();
00072 return i->second;
00073 }
00074
00075 prefix_ senf::Collector & senf::StatisticsBase::collect(unsigned rank)
00076 {
00077 std::pair<Children::iterator, bool> state (
00078 children_.insert(std::make_pair(rank, Collector(this, rank))) );
00079 if (! state.second)
00080 throw DuplicateRankException();
00081 return state.first->second;
00082 }
00083
00084 prefix_ senf::StatisticsBase::OutputProxy<senf::StatisticsBase>
00085 senf::StatisticsBase::output(unsigned n)
00086 {
00087 OutputMap::iterator i (outputs_.find(n));
00088 if (i == outputs_.end()) {
00089 i = outputs_.insert(std::make_pair(n, OutputEntry(n))).first;
00090 std::stringstream nm;
00091 nm << "output" << path() << ":" << n;
00092 base().dir.node().add(nm.str(), i->second.dir);
00093 detail::StatisticsLoggerRegistry::instance().apply(*this, n, i->second.dir);
00094 }
00095 if (n > maxQueueLen_)
00096 maxQueueLen_ = n;
00097 return OutputProxy<StatisticsBase>(this, &(i->second));
00098 }
00099
00100 prefix_ void senf::StatisticsBase::consoleList(unsigned level, std::ostream & os)
00101 const
00102 {
00103 namespace fmt = senf::format;
00104
00105 os << boost::format("%s%-5d%|15t| %12.5g %19.5g %12.5g\n")
00106 % std::string(2*level,' ') % rank()
00107 % fmt::eng(min()).setw() % fmt::eng(avg(),dev()).setw() % fmt::eng(max()).setw();
00108 {
00109 OutputMap::const_iterator i (outputs_.begin());
00110 OutputMap::const_iterator i_end (outputs_.end());
00111 for (; i != i_end; ++i)
00112 os << boost::format(" %3d %12.5g %19.5g %12.5g\n")
00113 % i->second.n
00114 % fmt::eng(i->second.min).setw()
00115 % fmt::eng(i->second.avg, i->second.dev).setw()
00116 % fmt::eng(i->second.max).setw();
00117 }
00118 {
00119 Children::const_iterator i (children_.begin());
00120 Children::const_iterator const i_end (children_.end());
00121 for (; i != i_end; ++i)
00122 i->second.consoleList(level+1, os);
00123 }
00124 }
00125
00126 prefix_ void senf::StatisticsBase::generateOutput()
00127 {
00128 queue_.push_front(QueueEntry(min_, avg_, max_, dev_));
00129 while (queue_.size() > maxQueueLen_)
00130 queue_.pop_back();
00131
00132 OutputMap::iterator i (outputs_.begin());
00133 OutputMap::iterator const i_end (outputs_.end());
00134 for (; i != i_end; ++i) {
00135 i->second.min = i->second.avg = i->second.max = i->second.dev = 0.0f;
00136 Queue::const_iterator j (queue_.begin());
00137 Queue::const_iterator const j_end (queue_.end());
00138 unsigned n (0);
00139 for (; n < i->second.n && j != j_end; ++n, ++j) {
00140 i->second.min += j->min;
00141 i->second.avg += j->avg;
00142 i->second.max += j->max;
00143 i->second.dev += j->dev;
00144 }
00145 i->second.min /= n;
00146 i->second.avg /= n;
00147 i->second.max /= n;
00148 i->second.dev /= n;
00149 i->second.signal(i->second.min, i->second.avg, i->second.max, i->second.dev);
00150 }
00151 }
00152
00153
00154
00155
00156 prefix_ void senf::StatisticsBase::OutputEntry::consoleList(std::ostream & os)
00157 {
00158 for (boost::ptr_vector<TargetBase>::iterator i (targets_.begin());
00159 i != targets_.end(); ++i)
00160 if (! i->label.empty())
00161 os << i->label << "\n";
00162 }
00163
00164
00165
00166
00167 prefix_ senf::Statistics::Statistics()
00168 #ifndef SENF_DISABLE_CONSOLE
00169 : dir (this)
00170 #endif
00171 {
00172 #ifndef SENF_DISABLE_CONSOLE
00173 namespace fty = console::factory;
00174
00175 dir.add("list", fty::Command(&Statistics::consoleList, this)
00176 .doc("List statistics collection intervals and current values.\n"
00177 "\n"
00178 "Columns:\n"
00179 " RANK Number of values collected. Since the statistics collectors form\n"
00180 " a tree, the value is indented according to it's tree location.\n"
00181 " WIN Size of output average window.\n"
00182 " MIN Last entered minimum value.\n"
00183 " AVG Last entered average value.\n"
00184 " DEV Standard deviation of average value over the collector rank.\n"
00185 " MAX Last entered maximum value.") );
00186 dir.add("collect", fty::Command(&Statistics::consoleCollect, this)
00187 .doc("Add statistics collection groups. The argument gives a sequence of collector\n"
00188 "ranks each building on the preceding collector:\n"
00189 "\n"
00190 " $ collect (10 60 60)\n"
00191 "\n"
00192 "Will start by collecting every 10 values together to a new value. 60 of such\n"
00193 "combined values will be collected together in the next step again followed by\n"
00194 "a collection of 60 values. If the statistics is entered with a frequency of\n"
00195 "10 values per second, this will provide combined statistics over the second,\n"
00196 "minutes and hours ranges.\n"
00197 "\n"
00198 "You may call collect multiple times. Any missing collection ranks will be\n"
00199 "added.")
00200 .arg("ranks","chain of collector ranks") );
00201 dir.add("output", fty::Command(&Statistics::consoleOutput, this)
00202 .doc("Generate statistics output. This statement will add an additional output\n"
00203 "generator. This generator will be attached to the collector specified by\n"
00204 "the {rank} parameter. This parameter is a chain of successive rank values\n"
00205 "which specifies the exact collector to use. If the collector does not\n"
00206 "exist, it will be created (this is like automatically calling 'collect'\n"
00207 "with {rank} as argument).\n"
00208 "\n"
00209 "If the output is to be sent somewhere it must be connected to a statistics\n"
00210 "target.\n"
00211 "\n"
00212 "The output may optionally be built using a sliding average over the last\n"
00213 "{window} values.\n"
00214 "\n"
00215 " $ output ()\n"
00216 "\n"
00217 "will output the basic statistics value each time a new value is entered.\n"
00218 "\n"
00219 " $ output (10 60) 5\n"
00220 "\n"
00221 "Assuming that new data values are entered 10 times per second, this command\n"
00222 "will generate output once every minute. The value will be the average over\n"
00223 "the last 5 minutes.")
00224 .arg("rank","Rank chain selecting the value to generate output for")
00225 .arg("window","Optional size of sliding average window",
00226 console::kw::default_value = 1u) );
00227 #endif
00228 }
00229
00230 prefix_ void senf::Statistics::consoleList(std::ostream & os)
00231 {
00232 os << "RANK WIN MIN AVG MAX\n";
00233 StatisticsBase::consoleList(0, os);
00234 }
00235
00236 prefix_ void senf::Statistics::consoleCollect(std::vector<unsigned> & ranks)
00237 {
00238 StatisticsBase * stats (this);
00239 std::vector<unsigned>::const_iterator i (ranks.begin());
00240 std::vector<unsigned>::const_iterator const i_end (ranks.end());
00241
00242 try {
00243 for (; i != i_end; ++i)
00244 stats = &(*stats)[*i];
00245 }
00246 catch (InvalidRankException &) {}
00247
00248 for (; i != i_end; ++i)
00249 stats = & (stats->collect(*i));
00250
00251 }
00252
00253 prefix_ boost::shared_ptr<senf::console::DirectoryNode>
00254 senf::Statistics::consoleOutput(std::vector<unsigned> & ranks, unsigned window)
00255 {
00256 StatisticsBase * stats (this);
00257 std::vector<unsigned>::const_iterator i (ranks.begin());
00258 std::vector<unsigned>::const_iterator const i_end (ranks.end());
00259
00260 try {
00261 for (; i != i_end; ++i)
00262 stats = &(*stats)[*i];
00263 }
00264 catch (InvalidRankException &) {}
00265
00266 for (; i != i_end; ++i)
00267 stats = & (stats->collect(*i));
00268
00269 return stats->output(window).dir().node().thisptr();
00270 }
00271
00272 prefix_ senf::Statistics & senf::Statistics::v_base()
00273 {
00274 return *this;
00275 }
00276
00277 prefix_ std::string senf::Statistics::v_path()
00278 const
00279 {
00280 return "";
00281 }
00282
00283
00284
00285
00286 prefix_ void senf::Collector::enter(unsigned n, float min, float avg, float max, float dev)
00287 {
00288 if (min < accMin_) accMin_ = min;
00289 if (max > accMax_) accMax_ = max;
00290
00291 if (i_ + n >= rank_) {
00292 accSum_ += (rank_-i_)*avg;
00293 accSumSq_ += (rank_-i_)*(rank_-i_)*(avg*avg + dev*dev);
00294 float accAvg (accSum_ / rank_);
00295 float accDev (std::sqrt(std::max(0.0f,accSumSq_ / rank_ - accAvg*accAvg)));
00296 StatisticsBase::enter(1, accMin_, accAvg, accMax_, accDev);
00297 accMin_ = FLT_MAX;
00298 accSum_ = 0.0f;
00299 accSumSq_ = 0.0f;
00300 accMax_ = -FLT_MAX;
00301 n -= (rank_ - i_);
00302 i_ = 0;
00303
00304 if (n >= rank_) {
00305 std::div_t d (std::div(int(n), int(rank_)));
00306 StatisticsBase::enter(d.quot, min, avg, max, dev);
00307 n = d.rem;
00308 }
00309 }
00310
00311 if (n>0) {
00312 accSum_ += n*avg;
00313 accSumSq_ += n*n*(avg*avg+dev*dev);
00314 i_ += n;
00315 if (min < accMin_) accMin_ = min;
00316 if (max > accMax_) accMax_ = max;
00317 }
00318 }
00319
00320 prefix_ senf::Statistics & senf::Collector::v_base()
00321 {
00322 return owner_->base();
00323 }
00324
00325 prefix_ std::string senf::Collector::v_path()
00326 const
00327 {
00328 return owner_->path() + "-" + senf::str(rank_);
00329 }
00330
00331
00332 #undef prefix_
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344