Kea  2.3.7
observation.cc
Go to the documentation of this file.
1 // Copyright (C) 2015-2023 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <stats/observation.h>
10 #include <util/chrono_time_utils.h>
11 #include <cc/data.h>
12 #include <chrono>
13 #include <utility>
14 
15 using namespace std;
16 using namespace std::chrono;
17 using namespace isc::data;
18 
19 namespace isc {
20 namespace stats {
21 
22 std::pair<bool, uint32_t>
23 Observation::default_max_sample_count_ = std::make_pair(true, 20);
24 
25 std::pair<bool, StatsDuration>
26 Observation::default_max_sample_age_ =
27  std::make_pair(false, StatsDuration::zero());
28 
29 Observation::Observation(const std::string& name, const int64_t value) :
30  name_(name), type_(STAT_INTEGER),
31  max_sample_count_(default_max_sample_count_),
32  max_sample_age_(default_max_sample_age_) {
33  setValue(value);
34 }
35 
36 Observation::Observation(const std::string& name, const double value) :
37  name_(name), type_(STAT_FLOAT),
38  max_sample_count_(default_max_sample_count_),
39  max_sample_age_(default_max_sample_age_) {
40  setValue(value);
41 }
42 
43 Observation::Observation(const std::string& name, const StatsDuration& value) :
44  name_(name), type_(STAT_DURATION),
45  max_sample_count_(default_max_sample_count_),
46  max_sample_age_(default_max_sample_age_) {
47  setValue(value);
48 }
49 
50 Observation::Observation(const std::string& name, const std::string& value) :
51  name_(name), type_(STAT_STRING),
52  max_sample_count_(default_max_sample_count_),
53  max_sample_age_(default_max_sample_age_) {
54  setValue(value);
55 }
56 
58  switch(type_) {
59  case STAT_INTEGER: {
60  setMaxSampleAgeInternal(integer_samples_, duration, STAT_INTEGER);
61  return;
62  }
63  case STAT_FLOAT: {
64  setMaxSampleAgeInternal(float_samples_, duration, STAT_FLOAT);
65  return;
66  }
67  case STAT_DURATION: {
68  setMaxSampleAgeInternal(duration_samples_, duration, STAT_DURATION);
69  return;
70  }
71  case STAT_STRING: {
72  setMaxSampleAgeInternal(string_samples_, duration, STAT_STRING);
73  return;
74  }
75  default:
76  isc_throw(InvalidStatType, "Unknown statistic type: "
77  << typeToText(type_));
78  };
79 }
80 
81 void Observation::setMaxSampleCount(uint32_t max_samples) {
82  switch(type_) {
83  case STAT_INTEGER: {
84  setMaxSampleCountInternal(integer_samples_, max_samples, STAT_INTEGER);
85  return;
86  }
87  case STAT_FLOAT: {
88  setMaxSampleCountInternal(float_samples_, max_samples, STAT_FLOAT);
89  return;
90  }
91  case STAT_DURATION: {
92  setMaxSampleCountInternal(duration_samples_, max_samples, STAT_DURATION);
93  return;
94  }
95  case STAT_STRING: {
96  setMaxSampleCountInternal(string_samples_, max_samples, STAT_STRING);
97  return;
98  }
99  default:
100  isc_throw(InvalidStatType, "Unknown statistic type: "
101  << typeToText(type_));
102  };
103 }
104 
105 void Observation::addValue(const int64_t value) {
106  IntegerSample current = getInteger();
107  setValue(current.first + value);
108 }
109 
110 void Observation::addValue(const double value) {
111  FloatSample current = getFloat();
112  setValue(current.first + value);
113 }
114 
116  DurationSample current = getDuration();
117  setValue(current.first + value);
118 }
119 
120 void Observation::addValue(const std::string& value) {
121  StringSample current = getString();
122  setValue(current.first + value);
123 }
124 
125 void Observation::setValue(const int64_t value) {
126  setValueInternal(value, integer_samples_, STAT_INTEGER);
127 }
128 
129 void Observation::setValue(const double value) {
130  setValueInternal(value, float_samples_, STAT_FLOAT);
131 }
132 
134  setValueInternal(value, duration_samples_, STAT_DURATION);
135 }
136 
137 void Observation::setValue(const std::string& value) {
138  setValueInternal(value, string_samples_, STAT_STRING);
139 }
140 
141 size_t Observation::getSize() const {
142  size_t size = 0;
143  switch(type_) {
144  case STAT_INTEGER: {
145  size = getSizeInternal(integer_samples_, STAT_INTEGER);
146  return (size);
147  }
148  case STAT_FLOAT: {
149  size = getSizeInternal(float_samples_, STAT_FLOAT);
150  return (size);
151  }
152  case STAT_DURATION: {
153  size = getSizeInternal(duration_samples_, STAT_DURATION);
154  return (size);
155  }
156  case STAT_STRING: {
157  size = getSizeInternal(string_samples_, STAT_STRING);
158  return (size);
159  }
160  default:
161  isc_throw(InvalidStatType, "Unknown statistic type: "
162  << typeToText(type_));
163  };
164  return (size);
165 }
166 
167 std::pair<bool, StatsDuration> Observation::getMaxSampleAge() const {
168  return (max_sample_age_);
169 }
170 
171 std::pair<bool, uint32_t> Observation::getMaxSampleCount() const {
172  return (max_sample_count_);
173 }
174 
175 template<typename StorageType>
176 size_t Observation::getSizeInternal(StorageType& storage, Type exp_type) const {
177  if (type_ != exp_type) {
178  isc_throw(InvalidStatType, "Invalid statistic type requested: "
179  << typeToText(exp_type) << ", but the actual type is "
180  << typeToText(type_));
181  } else {
182  return (storage.size());
183  }
184  return (0); // to avoid compilation error
185 }
186 
187 template<typename SampleType, typename StorageType>
188 void Observation::setValueInternal(SampleType value, StorageType& storage,
189  Type exp_type) {
190  if (type_ != exp_type) {
191  isc_throw(InvalidStatType, "Invalid statistic type requested: "
192  << typeToText(exp_type) << ", but the actual type is "
193  << typeToText(type_));
194  }
195 
196  if (storage.empty()) {
197  storage.push_back(make_pair(value, SampleClock::now()));
198  } else {
199  // Storing of more than one sample
200  storage.push_front(make_pair(value, SampleClock::now()));
201 
202  if (max_sample_count_.first) {
203  // if max_sample_count_ is set to true
204  // and size of storage is equal to max_sample_count_
205  if (storage.size() > max_sample_count_.second) {
206  storage.pop_back(); // removing the last element
207  }
208  } else {
209  StatsDuration range_of_storage =
210  storage.front().second - storage.back().second;
211  // removing samples until the range_of_storage
212  // stops exceeding the duration limit
213  while (range_of_storage > max_sample_age_.second) {
214  storage.pop_back();
215  range_of_storage =
216  storage.front().second - storage.back().second;
217  }
218  }
219  }
220 }
221 
223  return (getValueInternal<IntegerSample>(integer_samples_, STAT_INTEGER));
224 }
225 
227  return (getValueInternal<FloatSample>(float_samples_, STAT_FLOAT));
228 }
229 
231  return (getValueInternal<DurationSample>(duration_samples_, STAT_DURATION));
232 }
233 
235  return (getValueInternal<StringSample>(string_samples_, STAT_STRING));
236 }
237 
238 template<typename SampleType, typename Storage>
239 SampleType Observation::getValueInternal(Storage& storage, Type exp_type) const {
240  if (type_ != exp_type) {
241  isc_throw(InvalidStatType, "Invalid statistic type requested: "
242  << typeToText(exp_type) << ", but the actual type is "
243  << typeToText(type_));
244  }
245 
246  if (storage.empty()) {
247  // That should never happen. The first element is always initialized in
248  // the constructor. reset() sets its value to zero, but the element should
249  // still be there.
250  isc_throw(Unexpected, "Observation storage container empty");
251  }
252  return (*storage.begin());
253 }
254 
255 std::list<IntegerSample> Observation::getIntegers() const {
256  return (getValuesInternal<IntegerSample>(integer_samples_, STAT_INTEGER));
257 }
258 
259 std::list<FloatSample> Observation::getFloats() const {
260  return (getValuesInternal<FloatSample>(float_samples_, STAT_FLOAT));
261 }
262 
263 std::list<DurationSample> Observation::getDurations() const {
264  return (getValuesInternal<DurationSample>(duration_samples_, STAT_DURATION));
265 }
266 
267 std::list<StringSample> Observation::getStrings() const {
268  return (getValuesInternal<StringSample>(string_samples_, STAT_STRING));
269 }
270 
271 template<typename SampleType, typename Storage>
272 std::list<SampleType> Observation::getValuesInternal(Storage& storage,
273  Type exp_type) const {
274  if (type_ != exp_type) {
275  isc_throw(InvalidStatType, "Invalid statistic type requested: "
276  << typeToText(exp_type) << ", but the actual type is "
277  << typeToText(type_));
278  }
279 
280  if (storage.empty()) {
281  // That should never happen. The first element is always initialized in
282  // the constructor. reset() sets its value to zero, but the element should
283  // still be there.
284  isc_throw(Unexpected, "Observation storage container empty");
285  }
286  return (storage);
287 }
288 
289 template<typename StorageType>
290 void Observation::setMaxSampleAgeInternal(StorageType& storage,
291  const StatsDuration& duration,
292  Type exp_type) {
293  if (type_ != exp_type) {
294  isc_throw(InvalidStatType, "Invalid statistic type requested: "
295  << typeToText(exp_type) << ", but the actual type is "
296  << typeToText(type_));
297  }
298  // setting new value of max_sample_age_
299  max_sample_age_.first = true;
300  max_sample_age_.second = duration;
301  // deactivating the max_sample_count_ limit
302  max_sample_count_.first = false;
303 
304  StatsDuration range_of_storage =
305  storage.front().second - storage.back().second;
306 
307  while (range_of_storage > duration) {
308  // deleting elements which are exceeding the duration limit
309  storage.pop_back();
310  range_of_storage = storage.front().second - storage.back().second;
311  }
312 }
313 
314 template<typename StorageType>
315 void Observation::setMaxSampleCountInternal(StorageType& storage,
316  uint32_t max_samples,
317  Type exp_type) {
318  if (type_ != exp_type) {
319  isc_throw(InvalidStatType, "Invalid statistic type requested: "
320  << typeToText(exp_type) << ", but the actual type is "
321  << typeToText(type_));
322  }
323  // Should we refuse the max_samples = 0 value here?
324  // setting new value of max_sample_count_
325  max_sample_count_.first = true;
326  max_sample_count_.second = max_samples;
327  // deactivating the max_sample_age_ limit
328  max_sample_age_.first = false;
329 
330  while (storage.size() > max_samples) {
331  // deleting elements which are exceeding the max_samples limit
332  storage.pop_back();
333  }
334 }
335 
337  // setting new value of default_max_sample_age_
338  default_max_sample_age_.second = duration;
339 }
340 
341 void Observation::setMaxSampleCountDefault(uint32_t max_samples) {
342  if (max_samples == 0) {
343  // deactivating the default_max_sample_count_ limit
344  default_max_sample_count_.first = false;
345  default_max_sample_age_.first = true;
346  } else {
347  // setting new value of default_max_sample_count_
348  default_max_sample_count_.second = max_samples;
349  // deactivating the default_max_sample_age_ limit
350  default_max_sample_age_.first = false;
351  default_max_sample_count_.first = true;
352  }
353 }
354 
356  return (default_max_sample_age_.second);
357 }
358 
360  if (default_max_sample_count_.first) {
361  return (default_max_sample_count_.second);
362  } else {
363  return (0);
364  }
365 }
366 
367 std::string Observation::typeToText(Type type) {
368  std::stringstream tmp;
369  switch (type) {
370  case STAT_INTEGER:
371  tmp << "integer";
372  break;
373  case STAT_FLOAT:
374  tmp << "float";
375  break;
376  case STAT_DURATION:
377  tmp << "duration";
378  break;
379  case STAT_STRING:
380  tmp << "string";
381  break;
382  default:
383  tmp << "unknown";
384  break;
385  }
386  tmp << "(" << type << ")";
387  return (tmp.str());
388 }
389 
392  ElementPtr list = isc::data::Element::createList(); // multiple observations
393  ElementPtr entry;
394  ElementPtr value;
395  ElementPtr timestamp;
396 
397  // Support for retrieving more than one sample
398  // retrieving all samples of indicated observation
399  switch (type_) {
400  case STAT_INTEGER: {
401  std::list<IntegerSample> s = getIntegers(); // List of all integer samples
402 
403  // Iteration over all elements in the list
404  // and adding alternately value and timestamp to the entry
405  for (std::list<IntegerSample>::iterator it = s.begin(); it != s.end(); ++it) {
407  value = isc::data::Element::create(static_cast<int64_t>((*it).first));
408  timestamp = isc::data::Element::create(isc::util::clockToText((*it).second));
409 
410  entry->add(value);
411  entry->add(timestamp);
412 
413  list->add(entry);
414  }
415  break;
416  }
417  case STAT_FLOAT: {
418  std::list<FloatSample> s = getFloats();
419 
420  // Iteration over all elements in the list
421  // and adding alternately value and timestamp to the entry
422  for (std::list<FloatSample>::iterator it = s.begin(); it != s.end(); ++it) {
424  value = isc::data::Element::create((*it).first);
425  timestamp = isc::data::Element::create(isc::util::clockToText((*it).second));
426 
427  entry->add(value);
428  entry->add(timestamp);
429 
430  list->add(entry);
431  }
432  break;
433  }
434  case STAT_DURATION: {
435  std::list<DurationSample> s = getDurations();
436 
437  // Iteration over all elements in the list
438  // and adding alternately value and timestamp to the entry
439  for (std::list<DurationSample>::iterator it = s.begin(); it != s.end(); ++it) {
442  timestamp = isc::data::Element::create(isc::util::clockToText((*it).second));
443 
444  entry->add(value);
445  entry->add(timestamp);
446 
447  list->add(entry);
448  }
449  break;
450  }
451  case STAT_STRING: {
452  std::list<StringSample> s = getStrings();
453 
454  // Iteration over all elements in the list
455  // and adding alternately value and timestamp to the entry
456  for (std::list<StringSample>::iterator it = s.begin(); it != s.end(); ++it) {
458  value = isc::data::Element::create((*it).first);
459  timestamp = isc::data::Element::create(isc::util::clockToText((*it).second));
460 
461  entry->add(value);
462  entry->add(timestamp);
463 
464  list->add(entry);
465  }
466  break;
467  }
468  default:
469  isc_throw(InvalidStatType, "Unknown statistic type: "
470  << typeToText(type_));
471  };
472 
473  return (list);
474 }
475 
477  switch(type_) {
478  case STAT_INTEGER: {
479  integer_samples_.clear();
480  setValue(static_cast<int64_t>(0));
481  return;
482  }
483  case STAT_FLOAT: {
484  float_samples_.clear();
485  setValue(0.0);
486  return;
487  }
488  case STAT_DURATION: {
489  duration_samples_.clear();
490  setValue(StatsDuration::zero());
491  return;
492  }
493  case STAT_STRING: {
494  string_samples_.clear();
495  setValue(string(""));
496  return;
497  }
498  default:
499  isc_throw(InvalidStatType, "Unknown statistic type: "
500  << typeToText(type_));
501  };
502 }
503 
504 } // end of namespace stats
505 } // end of namespace isc
A generic exception that is thrown when an unexpected error condition occurs.
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition: data.cc:241
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition: data.cc:286
Exception thrown if invalid statistic type is used.
Definition: observation.h:24
static std::string typeToText(Type type)
Converts statistic type to string.
Definition: observation.cc:367
static const StatsDuration & getMaxSampleAgeDefault()
Get default maximum age of samples.
Definition: observation.cc:355
void setValue(const int64_t value)
@
Definition: observation.cc:125
std::list< FloatSample > getFloats() const
Returns observed float samples.
Definition: observation.cc:259
void reset()
Resets statistic.
Definition: observation.cc:476
FloatSample getFloat() const
Returns observed float sample.
Definition: observation.cc:226
static uint32_t getMaxSampleCountDefault()
Get default maximum count of samples.
Definition: observation.cc:359
std::pair< bool, StatsDuration > getMaxSampleAge() const
Returns both values of max_sample_age_ of statistic.
Definition: observation.cc:167
void addValue(const int64_t value)
Records incremental integer observation.
Definition: observation.cc:105
StringSample getString() const
Returns observed string sample.
Definition: observation.cc:234
size_t getSize() const
Returns size of observed storage.
Definition: observation.cc:141
Type
Type of available statistics.
Definition: observation.h:95
@ STAT_INTEGER
this statistic is unsigned 64-bit integer value
Definition: observation.h:96
@ STAT_STRING
this statistic represents a string
Definition: observation.h:99
@ STAT_FLOAT
this statistic is a floating point value
Definition: observation.h:97
@ STAT_DURATION
this statistic represents time duration
Definition: observation.h:98
isc::data::ConstElementPtr getJSON() const
Returns as a JSON structure.
Definition: observation.cc:391
void setMaxSampleCount(uint32_t max_samples)
Determines how many samples of a given statistic should be kept.
Definition: observation.cc:81
static void setMaxSampleCountDefault(uint32_t max_samples)
Determines default maximum count of samples.
Definition: observation.cc:341
IntegerSample getInteger() const
Returns observed integer sample.
Definition: observation.cc:222
static void setMaxSampleAgeDefault(const StatsDuration &duration)
Determines default maximum age of samples.
Definition: observation.cc:336
void setMaxSampleAge(const StatsDuration &duration)
Determines maximum age of samples.
Definition: observation.cc:57
std::list< StringSample > getStrings() const
Returns observed string samples.
Definition: observation.cc:267
std::pair< bool, uint32_t > getMaxSampleCount() const
Returns both values of max_sample_count_ of statistic.
Definition: observation.cc:171
std::list< IntegerSample > getIntegers() const
Returns observed integer samples.
Definition: observation.cc:255
std::list< DurationSample > getDurations() const
Returns observed duration samples.
Definition: observation.cc:263
Observation(const std::string &name, const int64_t value)
Constructor for integer observations.
Definition: observation.cc:29
DurationSample getDuration() const
Returns observed duration sample.
Definition: observation.cc:230
const Name & name_
Definition: dns/message.cc:693
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
std::pair< double, SampleClock::time_point > FloatSample
Float (implemented as double precision)
Definition: observation.h:59
std::pair< int64_t, SampleClock::time_point > IntegerSample
Integer (implemented as signed 64-bit integer)
Definition: observation.h:56
std::pair< std::string, SampleClock::time_point > StringSample
String.
Definition: observation.h:65
std::pair< StatsDuration, SampleClock::time_point > DurationSample
Time Duration.
Definition: observation.h:62
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
std::chrono::system_clock::duration StatsDuration
Defines duration type.
Definition: observation.h:39
std::string durationToText(boost::posix_time::time_duration dur, size_t fsecs_precision=MAX_FSECS_PRECISION)
Converts StatsDuration to text.
std::string clockToText(std::chrono::system_clock::time_point t, size_t fsecs_precision)
Converts chrono time point structure to text.
Defines the logger used by the top-level component of kea-lfc.