blob: 82228738b35109f29c740e4b9546b522a07db45f [file] [log] [blame]
#include "request_stats.hpp"
#include "logging.hpp"
namespace managedStore
{
/** the linear constants below are a good start, we can revisit them as we find
* value in this histogram */
/** num of buckets */
constexpr static int64_t kHistNumBuckets = 50;
/** size of each bucket in milliseconds */
constexpr static int64_t kHistBucketSizeMillisec = 100;
/** global stats store config */
RequestStatsStoreConfig statsStoreConfigDefault; // NOLINT
/** global stats store */
RequestStatsStore statsStore(statsStoreConfigDefault); // NOLINT
RequestStatsContext::RequestStatsContext(
const std::string& key, const std::chrono::steady_clock::time_point& now) :
queryURLKey(key), timepointStart(now), rdeStats()
{
BMCWEB_LOG_DEBUG << "RequestStatsContext: start: " << this->toJson();
}
RequestStatsContext::~RequestStatsContext()
{
BMCWEB_LOG_DEBUG << "RequestStatsContext: done: " << this->toJson();
}
std::string
RequestStatsContext::queryURLKeyFor(const boost::beast::http::verb& method,
const boost::urls::url_view& qURL)
{
std::stringstream key;
key << method << kSep << qURL;
return key.str();
}
nlohmann::json RequestStatsContext::toJson() const
{
nlohmann::json obj;
obj["queryURLKey"] = this->queryURLKey;
if (managedStore::RequestStatsStore::isRdeRateLimitEnabled())
{
obj["rdeStats"] = nlohmann::json::object();
obj["rdeStats"]["rdeReqs"] = getRdeReqs();
obj["rdeStats"]["rdeDbusErrors"] = getRdeDbusErrors();
obj["rdeStats"]["rdeSkips"] = getRdeSkips();
obj["rdeStats"]["rdeRateLimitToggles"] = getRdeRateLimitToggles();
}
obj["ageMillisec"] =
managedStore::clockSinceMilliseconds(this->timepointStart);
return obj;
}
std::string RequestStatsContext::getQueryURLKey() const
{
return this->queryURLKey;
};
std::chrono::steady_clock::time_point
RequestStatsContext::getTimepointStart() const
{
return this->timepointStart;
};
void RequestStatsContext::setOK(bool ok)
{
this->isOK = ok;
}
bool RequestStatsContext::ok() const
{
return this->isOK;
}
void RequestStatsContext::updateRdeReqs()
{
this->rdeStats.totalReqs += 1;
}
void RequestStatsContext::updateRdeDbusErrors()
{
this->rdeStats.totalDbusErrors += 1;
}
void RequestStatsContext::updateRdeSkips()
{
this->rdeStats.totalSkips += 1;
}
void RequestStatsContext::updateRdeRateLimitToggles()
{
this->rdeStats.totalRateLimitToggles += 1;
}
int RequestStatsContext::getRdeReqs() const
{
return this->rdeStats.totalReqs;
}
int RequestStatsContext::getRdeDbusErrors() const
{
return this->rdeStats.totalDbusErrors;
}
int RequestStatsContext::getRdeSkips() const
{
return this->rdeStats.totalSkips;
}
int RequestStatsContext::getRdeRateLimitToggles() const
{
return this->rdeStats.totalRateLimitToggles;
}
nlohmann::json RequestStatsStoreConfig::toJson() const
{
nlohmann::json obj;
obj["enable"] = this->enable;
obj["enableRdeRateLimit"] = this->enableRdeRateLimit;
obj["rdeDbusErrorThreshold"] = this->rdeDbusErrorThreshold;
obj["rdeMaxSkips"] = this->rdeMaxSkips;
obj["bucket_num"] = kHistNumBuckets;
obj["bucket_millisec"] = kHistBucketSizeMillisec;
return obj;
}
void RequestStatsStore::initialize(const RequestStatsStoreConfig& config)
{
BMCWEB_LOG_INFO << "RequestStatsStore::initialize: config: "
<< config.toJson();
statsStore.config = config;
}
RequestStatsStore& RequestStatsStore::instance()
{
return statsStore;
}
RequestStatsStore::RequestStatsStore(const RequestStatsStoreConfig& conf) :
config(conf)
{}
RequestStatsStore::~RequestStatsStore() = default;
nlohmann::json RequestStatsStore::toJson() const
{
nlohmann::json obj;
obj["config"] = this->config.toJson();
if (isRdeRateLimitEnabled())
{
obj["rdeStats"] = nlohmann::json::object();
obj["rdeStats"]["totalReqs"] = this->rdeStats.totalReqs;
obj["rdeStats"]["totalDbusErrors"] = this->rdeStats.totalDbusErrors;
obj["rdeStats"]["totalSkips"] = this->rdeStats.totalSkips;
obj["rdeStats"]["totalRateLimitToggles"] =
this->rdeStats.totalRateLimitToggles;
obj["rdeStats"]["CurrentRdeDbusErrorCount"] =
this->rdeStats.rdeDbusErrorCount;
obj["rdeStats"]["CurrentRdeRequestSkips"] =
this->rdeStats.rdeRequestSkips;
}
// TODO: pick on of these:
// obj["statsList"] = nlohmann::json::array();
obj["statsMap"] = nlohmann::json::object();
for (const auto& queryStats : this->requestURLStats)
{
auto queryStatsJson = queryStats.second.toJson();
queryStatsJson["target"] = queryStats.first;
// obj["statsList"].push_back(queryStatsJson);
obj["statsMap"][queryStats.first] = queryStatsJson;
}
return obj;
}
void RequestStatsStore::clear()
{
this->requestURLStats.clear();
this->rdeStats.clear();
}
void RequestStatsStore::logRequestStats(
std::chrono::steady_clock::time_point now,
const RequestStatsContext& requestStatsContext)
{
BMCWEB_LOG_DEBUG << "logRequestStats: " << requestStatsContext.toJson();
// auto iter =
// this->requestURLStats.find(requestStatsContext.getQueryURLKey());
// if (iter == this->requestURLStats.end())
// {
// this->requestURLStats[requestStatsContext.getQueryURLKey()] =
// RequestStats();
// iter =
// this->requestURLStats.find(requestStatsContext.getQueryURLKey());
// }
auto insertRet = this->requestURLStats.insert(
{requestStatsContext.getQueryURLKey(), RequestStats()});
auto insertRetIter = insertRet.first;
// check again, just in case
if (insertRetIter == this->requestURLStats.end())
{
BMCWEB_LOG_ERROR << "failed to allocate stats for: "
<< requestStatsContext.getQueryURLKey();
return;
}
RequestStats& requestStats = insertRetIter->second;
// log returned status code:
if (requestStatsContext.ok())
{
requestStats.countOK += 1;
}
else
{
requestStats.countError += 1;
}
// calcualte and export latency:
const int64_t latencyMilliseconds = managedStore::clockSinceMilliseconds(
requestStatsContext.getTimepointStart(), now);
if (latencyMilliseconds >= 0)
{
int64_t bucketIndex = latencyMilliseconds / kHistBucketSizeMillisec;
// last bucket is "catch all":
if (bucketIndex >= kHistNumBuckets)
{
bucketIndex = kHistNumBuckets;
}
// increment the right histogram bucket:
requestStats.latencyHistogramMilliseconds[kHistBucketSizeMillisec *
(1 + bucketIndex)] += 1;
}
else
{
BMCWEB_LOG_ERROR << "logRequestStats:"
<< " context: " << requestStatsContext.toJson()
<< " latencyMilliseconds: " << latencyMilliseconds
<< " negative";
}
BMCWEB_LOG_INFO << "logRequestStats: iter: "
<< insertRetIter->second.toJson()
<< " requestStatsContext: " << requestStatsContext.toJson();
}
void RequestStatsStore::logRdeRequestStats(
const RequestStatsContext& requestStatsContext)
{
BMCWEB_LOG_DEBUG << "logRdeRequestStats: " << requestStatsContext.toJson();
this->rdeStats.totalReqs += requestStatsContext.getRdeReqs();
this->rdeStats.totalDbusErrors += requestStatsContext.getRdeDbusErrors();
this->rdeStats.totalSkips += requestStatsContext.getRdeSkips();
this->rdeStats.totalRateLimitToggles +=
requestStatsContext.getRdeRateLimitToggles();
}
void RequestStatsStore::updateCurrentRdeDbusErrors()
{
this->rdeStats.rdeDbusErrorCount += 1;
}
int RequestStatsStore::getCurrentRdeDbusErrors() const
{
return this->rdeStats.rdeDbusErrorCount;
}
void RequestStatsStore::setCurrentRdeDbusErrors(int errCount)
{
this->rdeStats.rdeDbusErrorCount = errCount;
}
void RequestStatsStore::updateCurrentRdeRequestSkips()
{
if (this->rdeStats.rdeRequestSkips > 0)
{
this->rdeStats.rdeRequestSkips -= 1;
}
}
int RequestStatsStore::getCurrentRdeRequestSkips() const
{
return this->rdeStats.rdeRequestSkips;
}
void RequestStatsStore::setCurrentRdeRequestSkips(int skipCount)
{
this->rdeStats.rdeRequestSkips = skipCount;
}
} // namespace managedStore