Commit c7b8c275 authored by Franciszek Gorski's avatar Franciszek Gorski
Browse files

Support for storing more than one sample

parent 59d0dbe9
......@@ -1192,8 +1192,7 @@ public:
// Get the nested list which should have two elements, of which first
// is the statistics value we're looking for.
ConstElementPtr second_list = first_list->get(0);
if (second_list && (second_list->getType() == Element::list) &&
(second_list->size() == 2)) {
if (second_list && (second_list->getType() == Element::list)) {
ConstElementPtr addresses_element = second_list->get(0);
if (addresses_element && (addresses_element->getType() == Element::integer)) {
return (addresses_element->intValue());
......
......@@ -1171,8 +1171,7 @@ public:
// Get the nested list which should have two elements, of which first
// is the statistics value we're looking for.
ConstElementPtr second_list = first_list->get(0);
if (second_list && (second_list->getType() == Element::list) &&
(second_list->size() == 2)) {
if (second_list && (second_list->getType() == Element::list)) {
ConstElementPtr addresses_element = second_list->get(0);
if (addresses_element && (addresses_element->getType() == Element::integer)) {
return (addresses_element->intValue());
......
......@@ -20,26 +20,74 @@ using namespace boost::posix_time;
namespace isc {
namespace stats {
Observation::Observation(const std::string& name, const int64_t value)
:name_(name), type_(STAT_INTEGER) {
Observation::Observation(const std::string& name, const int64_t value) :
name_(name), type_(STAT_INTEGER) {
setValue(value);
}
Observation::Observation(const std::string& name, const double value)
:name_(name), type_(STAT_FLOAT) {
Observation::Observation(const std::string& name, const double value) :
name_(name), type_(STAT_FLOAT) {
setValue(value);
}
Observation::Observation(const std::string& name, const StatsDuration& value)
:name_(name), type_(STAT_DURATION) {
Observation::Observation(const std::string& name, const StatsDuration& value) :
name_(name), type_(STAT_DURATION) {
setValue(value);
}
Observation::Observation(const std::string& name, const std::string& value)
:name_(name), type_(STAT_STRING) {
Observation::Observation(const std::string& name, const std::string& value) :
name_(name), type_(STAT_STRING) {
setValue(value);
}
void Observation::setMaxSampleAge(const StatsDuration& duration) {
switch(type_) {
case STAT_INTEGER: {
setMaxSampleAgeInternal(integer_samples_, duration, STAT_INTEGER);
return;
}
case STAT_FLOAT: {
setMaxSampleAgeInternal(float_samples_, duration, STAT_FLOAT);
return;
}
case STAT_DURATION: {
setMaxSampleAgeInternal(duration_samples_, duration, STAT_DURATION);
return;
}
case STAT_STRING: {
setMaxSampleAgeInternal(string_samples_, duration, STAT_STRING);
return;
}
default:
isc_throw(InvalidStatType, "Unknown statistic type: "
<< typeToText(type_));
};
}
void Observation::setMaxSampleCount(uint32_t max_samples) {
switch(type_) {
case STAT_INTEGER: {
setMaxSampleCountInternal(integer_samples_, max_samples, STAT_INTEGER);
return;
}
case STAT_FLOAT: {
setMaxSampleCountInternal(float_samples_, max_samples, STAT_FLOAT);
return;
}
case STAT_DURATION: {
setMaxSampleCountInternal(duration_samples_, max_samples, STAT_DURATION);
return;
}
case STAT_STRING: {
setMaxSampleCountInternal(string_samples_, max_samples, STAT_STRING);
return;
}
default:
isc_throw(InvalidStatType, "Unknown statistic type: "
<< typeToText(type_));
};
}
void Observation::addValue(const int64_t value) {
IntegerSample current = getInteger();
setValue(current.first + value);
......@@ -76,21 +124,76 @@ void Observation::setValue(const std::string& value) {
setValueInternal(value, string_samples_, STAT_STRING);
}
size_t Observation::getSize() const {
size_t size = 0;
switch(type_) {
case STAT_INTEGER: {
size = getSizeInternal(integer_samples_, STAT_INTEGER);
return (size);
}
case STAT_FLOAT: {
size = getSizeInternal(float_samples_, STAT_FLOAT);
return (size);
}
case STAT_DURATION: {
size = getSizeInternal(duration_samples_, STAT_DURATION);
return (size);
}
case STAT_STRING: {
size = getSizeInternal(string_samples_, STAT_STRING);
return (size);
}
default:
isc_throw(InvalidStatType, "Unknown statistic type: "
<< typeToText(type_));
};
return (size);
}
template<typename StorageType>
size_t Observation::getSizeInternal(StorageType& storage, Type exp_type) const {
if (type_ != exp_type) {
isc_throw(InvalidStatType, "Invalid statistic type requested: "
<< typeToText(exp_type) << ", but the actual type is "
<< typeToText(type_));
} else {
return (storage.size());
}
return (0); // to avoid compilation error
}
template<typename SampleType, typename StorageType>
void Observation::setValueInternal(SampleType value, StorageType& storage,
Type exp_type) {
if (type_ != exp_type) {
isc_throw(InvalidStatType, "Invalid statistic type requested: "
<< typeToText(exp_type) << ", but the actual type is "
<< typeToText(type_) );
<< typeToText(type_));
}
if (storage.empty()) {
storage.push_back(make_pair(value, microsec_clock::local_time()));
} else {
/// @todo: Update once more than one sample is supported
*storage.begin() = make_pair(value, microsec_clock::local_time());
// Storing of more than one sample
storage.push_front(make_pair(value, microsec_clock::local_time()));
if (max_sample_count_.first) {
// if max_sample_count is set to true
// and size of storage is equal to max_sample_count
if (storage.size() > max_sample_count_.second) {
storage.pop_back(); // removing the last element
}
} else {
StatsDuration range_of_storage =
storage.front().second - storage.back().second;
// removing samples until the range_of_storage
// stops exceeding the duration limit
while (range_of_storage > max_sample_age_.second) {
storage.pop_back();
range_of_storage =
storage.front().second - storage.back().second;
}
}
}
}
......@@ -115,7 +218,7 @@ SampleType Observation::getValueInternal(Storage& storage, Type exp_type) const
if (type_ != exp_type) {
isc_throw(InvalidStatType, "Invalid statistic type requested: "
<< typeToText(exp_type) << ", but the actual type is "
<< typeToText(type_) );
<< typeToText(type_));
}
if (storage.empty()) {
......@@ -127,6 +230,83 @@ SampleType Observation::getValueInternal(Storage& storage, Type exp_type) const
return (*storage.begin());
}
std::list<IntegerSample> Observation::getIntegers() const {
return (getValuesInternal<IntegerSample>(integer_samples_, STAT_INTEGER));
}
std::list<FloatSample> Observation::getFloats() const {
return (getValuesInternal<FloatSample>(float_samples_, STAT_FLOAT));
}
std::list<DurationSample> Observation::getDurations() const {
return (getValuesInternal<DurationSample>(duration_samples_, STAT_DURATION));
}
std::list<StringSample> Observation::getStrings() const {
return (getValuesInternal<StringSample>(string_samples_, STAT_STRING));
}
template<typename SampleType, typename Storage>
std::list<SampleType> Observation::getValuesInternal(Storage& storage, Type exp_type) const {
if (type_ != exp_type) {
isc_throw(InvalidStatType, "Invalid statistic type requested: "
<< typeToText(exp_type) << ", but the actual type is "
<< typeToText(type_));
}
if (storage.empty()) {
// That should never happen. The first element is always initialized in
// the constructor. reset() sets its value to zero, but the element should
// still be there.
isc_throw(Unexpected, "Observation storage container empty");
}
return (storage);
}
template<typename StorageType>
void Observation::setMaxSampleAgeInternal(StorageType& storage,
const StatsDuration& duration, Type exp_type) {
if (type_ != exp_type) {
isc_throw(InvalidStatType, "Invalid statistic type requested: "
<< typeToText(exp_type) << ", but the actual type is "
<< typeToText(type_));
}
// setting new value of max_sample_age
max_sample_age_.first = true;
max_sample_age_.second = duration;
// deactivating the max_sample_count limit
max_sample_count_.first = false;
StatsDuration range_of_storage =
storage.front().second - storage.back().second;
while (range_of_storage > duration) {
// deleting elements which are exceeding the duration limit
storage.pop_back();
range_of_storage = storage.front().second - storage.back().second;
}
}
template<typename StorageType>
void Observation::setMaxSampleCountInternal(StorageType& storage,
uint32_t max_samples, Type exp_type) {
if (type_ != exp_type) {
isc_throw(InvalidStatType, "Invalid statistic type requested: "
<< typeToText(exp_type) << ", but the actual type is "
<< typeToText(type_));
}
// setting new value of max_sample_count
max_sample_count_.first = true;
max_sample_count_.second = max_samples;
// deactivating the max_sample_age limit
max_sample_age_.first = false;
while (storage.size() > max_samples) {
// deleting elements which are exceeding the max_samples limit
storage.pop_back();
}
}
std::string Observation::typeToText(Type type) {
std::stringstream tmp;
switch (type) {
......@@ -153,36 +333,67 @@ std::string Observation::typeToText(Type type) {
isc::data::ConstElementPtr
Observation::getJSON() const {
ElementPtr entry = isc::data::Element::createList(); // a single observation
ElementPtr entry = isc::data::Element::createList(); // multiple observations
ElementPtr value;
ElementPtr timestamp;
/// @todo: Add support for retrieving more than one sample for a given
/// observation
// Support for retrieving more than one sample
// retrieving all samples of indicated observation
switch (type_) {
case STAT_INTEGER: {
IntegerSample s = getInteger();
value = isc::data::Element::create(static_cast<int64_t>(s.first));
timestamp = isc::data::Element::create(isc::util::ptimeToText(s.second));
std::list<IntegerSample> s = getIntegers(); // List of all integer samples
// Iteration over all elements in the list
// and adding alternately value and timestamp to the entry
for (std::list<IntegerSample>::iterator it = s.begin(); it != s.end(); ++it) {
value = isc::data::Element::create(static_cast<int64_t>((*it).first));
timestamp = isc::data::Element::create(isc::util::ptimeToText((*it).second));
entry->add(value);
entry->add(timestamp);
}
break;
}
case STAT_FLOAT: {
FloatSample s = getFloat();
value = isc::data::Element::create(s.first);
timestamp = isc::data::Element::create(isc::util::ptimeToText(s.second));
std::list<FloatSample> s = getFloats();
// Iteration over all elements in the list
// and adding alternately value and timestamp to the entry
for (std::list<FloatSample>::iterator it = s.begin(); it != s.end(); ++it) {
value = isc::data::Element::create((*it).first);
timestamp = isc::data::Element::create(isc::util::ptimeToText((*it).second));
entry->add(value);
entry->add(timestamp);
}
break;
}
case STAT_DURATION: {
DurationSample s = getDuration();
value = isc::data::Element::create(isc::util::durationToText(s.first));
timestamp = isc::data::Element::create(isc::util::ptimeToText(s.second));
std::list<DurationSample> s = getDurations();
// Iteration over all elements in the list
// and adding alternately value and timestamp to the entry
for (std::list<DurationSample>::iterator it = s.begin(); it != s.end(); ++it) {
value = isc::data::Element::create(isc::util::durationToText((*it).first));
timestamp = isc::data::Element::create(isc::util::ptimeToText((*it).second));
entry->add(value);
entry->add(timestamp);
}
break;
}
case STAT_STRING: {
StringSample s = getString();
value = isc::data::Element::create(s.first);
timestamp = isc::data::Element::create(isc::util::ptimeToText(s.second));
std::list<StringSample> s = getStrings();
// Iteration over all elements in the list
// and adding alternately value and timestamp to the entry
for (std::list<StringSample>::iterator it = s.begin(); it != s.end(); ++it) {
value = isc::data::Element::create((*it).first);
timestamp = isc::data::Element::create(isc::util::ptimeToText((*it).second));
entry->add(value);
entry->add(timestamp);
}
break;
}
default:
......@@ -190,10 +401,7 @@ Observation::getJSON() const {
<< typeToText(type_));
};
entry->add(value);
entry->add(timestamp);
ElementPtr list = isc::data::Element::createList(); // a single observation
ElementPtr list = isc::data::Element::createList(); // multiple observations
list->add(entry);
return (list);
......@@ -202,18 +410,22 @@ Observation::getJSON() const {
void Observation::reset() {
switch(type_) {
case STAT_INTEGER: {
integer_samples_.clear();
setValue(static_cast<int64_t>(0));
return;
}
case STAT_FLOAT: {
float_samples_.clear();
setValue(0.0);
return;
}
case STAT_DURATION: {
setValue(time_duration(0,0,0,0));
duration_samples_.clear();
setValue(time_duration(0, 0, 0, 0));
return;
}
case STAT_STRING: {
string_samples_.clear();
setValue(string(""));
return;
}
......
......@@ -80,7 +80,7 @@ class Observation {
/// int64_t and double. If convincing use cases appear to change them
/// to something else, we may change the underlying type.
enum Type {
STAT_INTEGER, ///< this statistic is unsinged 64-bit integer value
STAT_INTEGER, ///< this statistic is unsigned 64-bit integer value
STAT_FLOAT, ///< this statistic is a floating point value
STAT_DURATION,///< this statistic represents time duration
STAT_STRING ///< this statistic represents a string
......@@ -110,6 +110,36 @@ class Observation {
/// @param value string observed.
Observation(const std::string& name, const std::string& value);
/// @brief Determines maximum age of samples.
///
/// Specifies that statistic name should be stored not as a single value,
/// but rather as a set of values. The duration determines the timespan.
/// Samples older than duration will be discarded. This is time-constrained
/// approach. For sample count constrained approach, see @ref
/// setMaxSampleCount() below.
///
///
/// @param duration determines maximum age of samples
/// Example:
/// To set a statistic to keep observations for the last 5 minutes,
/// call: setMaxSampleAge(time_duration(0, 5, 0, 0));
/// To revert statistic to a single value, call:
/// setMaxSampleAge(time_duration(0, 0, 0, 0))
void setMaxSampleAge(const StatsDuration& duration);
/// @brief Determines how many samples of a given statistic should be kept.
///
/// Specifies that statistic name should be stored not as a single value,
/// but rather as a set of values. In this form, at most max_samples will
/// be kept. When adding max_samples + 1 sample, the oldest sample will be
/// discarded.
///
/// @param max_samples how many samples of a given statistic should be kept
/// Example:
/// To set a statistic to keep the last 100 observations, call:
/// setMaxSampleCount(100);
void setMaxSampleCount(uint32_t max_samples);
/// @brief Records absolute integer observation
///
/// @param value integer value observed
......@@ -158,9 +188,15 @@ class Observation {
/// @throw InvalidStatType if statistic is not a string
void addValue(const std::string& value);
/// @brief Returns size of observed storage
///
/// @return size of storage
size_t getSize() const;
/// @brief Resets statistic.
///
/// Sets statistic to a neutral (0, 0.0 or "") value.
/// Sets statistic to a neutral (0, 0.0 or "") value and
/// clears the underlying storage.
void reset();
/// @brief Returns statistic type
......@@ -189,6 +225,26 @@ class Observation {
/// @throw InvalidStatType if statistic is not a string
StringSample getString() const;
/// @brief Returns observed integer samples
/// @return list of observed samples (value + timestamp)
/// @throw InvalidStatType if statistic is not integer
std::list<IntegerSample> getIntegers() const;
/// @brief Returns observed float samples
/// @return list of observed samples (value + timestamp)
/// @throw InvalidStatType if statistic is not fp
std::list<FloatSample> getFloats() const;
/// @brief Returns observed duration samples
/// @return list of observed samples (value + timestamp)
/// @throw InvalidStatType if statistic is not time duration
std::list<DurationSample> getDurations() const;
/// @brief Returns observed string samples
/// @return list of observed samples (value + timestamp)
/// @throw InvalidStatType if statistic is not a string
std::list<StringSample> getStrings() const;
/// @brief Returns as a JSON structure
/// @return JSON structures representing all observations
isc::data::ConstElementPtr getJSON() const;
......@@ -203,6 +259,19 @@ class Observation {
}
private:
/// @brief Returns size of observed storage
///
/// This method returns size of observed storage.
/// It is used by public methods to return size of
/// available storages.
/// @tparam Storage type of storage (e.g. list<IntegerSample>)
/// @param storage storage which size will be returned
/// @param exp_type expected observation type (used for sanity checking)
/// @return Size of storage
template<typename StorageType>
size_t getSizeInternal(StorageType& storage, Type exp_type) const;
/// @brief Records absolute sample (internal version)
///
/// This method records an absolute value of an observation.
......@@ -230,12 +299,67 @@ private:
template<typename SampleType, typename Storage>
SampleType getValueInternal(Storage& storage, Type exp_type) const;
/// @brief Returns samples (internal version)
///
/// @tparam SampleType type of samples (e.g. IntegerSample)
/// @tparam Storage type of storage (e.g. list<IntegerSample>)
/// @param observation storage
/// @param exp_type expected observation type (used for sanity checking)
/// @throw InvalidStatType if observation type mismatches
/// @return List of observed samples
template<typename SampleType, typename Storage>
std::list<SampleType> getValuesInternal(Storage& storage,
Type exp_type) const;
/// @brief Determines maximum age of samples.
///
/// @tparam Storage type of storage (e.g. list<IntegerSample>)
/// @param storage storage on which limit will be set
/// @param duration determines maximum age of samples
/// @param exp_type expected observation type (used for sanity checking)
template<typename StorageType>
void setMaxSampleAgeInternal(StorageType& storage,
const StatsDuration& duration, Type exp_type);
/// @brief Determines how many samples of a given statistic should be kept.
///
/// @tparam Storage type of storage (e.g. list<IntegerSample>)
/// @param storage storage on which limit will be set
/// @param max_samples determines maximum number of samples
/// @param exp_type expected observation type (used for sanity checking)
template<typename StorageType>
void setMaxSampleCountInternal(StorageType& storage,
uint32_t max_samples, Type exp_type);
/// @brief Observation (statistic) name
std::string name_;
/// @brief Observation (statistic) type)
Type type_;
/// @brief Maximum number of samples
/// The limit is represent as a pair
/// of bool value and uint32_t
/// Only one kind of limit can be active
/// The bool value informs which limit
/// is available
/// True means active limit, false means inactive limit
/// By default the MaxSampleCount is set to 20
/// and MaxSampleAge is disabled
std::pair<bool, uint32_t> max_sample_count_ = std::make_pair(true, 20);
/// @brief Maximum timespan of samples
/// The limit is represent as a pair
/// of bool value and StatsDuration(boost::posix_time::time_duration)
/// Only one kind of limit can be active
/// The bool value informs which limit
/// is available
/// True means active limit, false means inactive limit
/// By default the MaxSampleCount is set to 20
/// and MaxSampleAge is disabled
std::pair<bool, StatsDuration> max_sample_age_ = std::make_pair(false,
boost::posix_time::time_duration(0, 0, 0, 0));
/// @defgroup samples_storage Storage for supported observations
///
/// @brief The following containers serve as a storage for all supported
......
......@@ -10,6 +10,8 @@
#include <stats/stats_mgr.h>
#include <cc/data.h>
#include <cc/command_interpreter.h>
#include <util/boost_time_utils.h>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace std;
using namespace isc::data;
......@@ -23,8 +25,8 @@ StatsMgr& StatsMgr::instance() {
return (stats_mgr);
}
StatsMgr::StatsMgr()
:global_(new StatContext()) {
StatsMgr::StatsMgr() :
global_(new StatContext()) {
}
......@@ -77,13 +79,25 @@ bool StatsMgr::deleteObservation(const std::string& name) {
return (global_->del(name));
}
void StatsMgr::setMaxSampleAge(const std::string& ,
const StatsDuration&) {
isc_throw(NotImplemented, "setMaxSampleAge not implemented");
bool StatsMgr::setMaxSampleAge(const std::string& name,
const StatsDuration& duration) {
ObservationPtr obs = getObservation(name);
if (obs) {
obs->setMaxSampleAge(duration);
return (true);
} else {
return (false);
}
}
void StatsMgr::setMaxSampleCount(const std::string& , uint32_t){
isc_throw(NotImplemented, "setMaxSampleCount not implemented");
bool StatsMgr::setMaxSampleCount(const std::string& name, uint32_t max_samples) {
ObservationPtr obs = getObservation(name);