| #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 |