| #ifndef THIRD_PARTY_GBMCWEB_INCLUDE_REQUEST_STATS_H_ | |
| #define THIRD_PARTY_GBMCWEB_INCLUDE_REQUEST_STATS_H_ | |
| #include <chrono> // NOLINT | |
| #include <cstdint> | |
| #include <map> | |
| #include <string> | |
| #include "boost/beast/http/verb.hpp" // NOLINT | |
| #include "boost/url/url_view.hpp" // NOLINT | |
| #include <nlohmann/json.hpp> | |
| #include "managed_store_clock.hpp" | |
| // TODO:: probably need a new namespace? even though there is no good reason for | |
| // it | |
| // TODO:: probably will just rename to bmcweb | |
| namespace managedStore { | |
| struct RdeRequestStats { | |
| nlohmann::json toJson() const { | |
| nlohmann::json obj; | |
| obj["totalReqs"] = this->totalReqs; | |
| obj["totalDbusErrors"] = this->totalDbusErrors; | |
| obj["totalSkips"] = this->totalSkips; | |
| obj["totalRateLimitToggles"] = this->totalRateLimitToggles; | |
| obj["CurrentRdeDbusErrorCount"] = this->rdeDbusErrorCount; | |
| obj["CurrentRdeRequestSkips"] = this->rdeRequestSkips; | |
| return obj; | |
| } | |
| void clear() { | |
| this->totalReqs = 0; | |
| this->totalDbusErrors = 0; | |
| this->totalSkips = 0; | |
| this->totalRateLimitToggles = 0; | |
| } | |
| /** Total number of Rde Requests */ | |
| int totalReqs = 0; | |
| /** Total number if dbus error in Rde Requests */ | |
| int totalDbusErrors = 0; | |
| /** Total number Rde Requests skipped because of Rate Limit */ | |
| int totalSkips = 0; | |
| /** Total number times Rde Requests Rate Limiter enabled */ | |
| int totalRateLimitToggles = 0; | |
| // This is used to enable the rate limit the number of requests that can | |
| // be sent to the RDEd. | |
| /** Current Dbus error count*/ | |
| int rdeDbusErrorCount = 0; | |
| // This keeps track number of skipped RDEd requests before turning off | |
| // rate limiting | |
| /** Current RDE Requests skip count*/ | |
| int rdeRequestSkips = 0; | |
| }; | |
| /** stats context captured by each request handler in the bmcweb */ | |
| class RequestStatsContext { | |
| public: | |
| explicit RequestStatsContext(const std::string& queryURLKey, | |
| const std::chrono::steady_clock::time_point& | |
| now = managedStore::clockNow()); | |
| virtual ~RequestStatsContext(); | |
| RequestStatsContext(const RequestStatsContext&) = default; | |
| RequestStatsContext& operator=(const RequestStatsContext&) = default; | |
| RequestStatsContext(RequestStatsContext&&) = default; | |
| RequestStatsContext& operator=(RequestStatsContext&&) = default; | |
| nlohmann::json toJson() const; | |
| std::string getQueryURLKey() const; | |
| std::chrono::steady_clock::time_point getTimepointStart() const; | |
| void setOK(bool ok); | |
| bool ok() const; | |
| constexpr static const char kSep = '|'; | |
| void updateRdeReqs(); | |
| void updateRdeDbusErrors(); | |
| void updateRdeSkips(); | |
| void updateRdeRateLimitToggles(); | |
| int getRdeReqs() const; | |
| int getRdeDbusErrors() const; | |
| int getRdeSkips() const; | |
| int getRdeRateLimitToggles() const; | |
| /** compose a key for the histogram bucket to agg the stats */ | |
| static std::string queryURLKeyFor(const boost::beast::http::verb& method, | |
| const boost::urls::url_view& qURL); | |
| protected: | |
| std::string queryURLKey; | |
| bool isOK = false; | |
| std::chrono::steady_clock::time_point timepointStart; | |
| /** Rde Request Stats */ | |
| RdeRequestStats rdeStats; | |
| }; | |
| /** Stats collected by the RequestStatsStore */ | |
| struct RequestStats { | |
| nlohmann::json toJson() const { | |
| nlohmann::json obj; | |
| obj["countOK"] = this->countOK; | |
| obj["countError"] = this->countError; | |
| obj["latencyHistogram"] = nlohmann::json::array(); | |
| for (const auto& histogramBucket : this->latencyHistogramMilliseconds) { | |
| nlohmann::json bucketJson; | |
| bucketJson["milliseconds"] = histogramBucket.first; | |
| bucketJson["count"] = histogramBucket.second; | |
| obj["latencyHistogram"].push_back(bucketJson); | |
| } | |
| return obj; | |
| } | |
| /** num of ok (200)*/ | |
| int countOK = 0; | |
| /** num of errors (none 200)*/ | |
| int countError = 0; | |
| /** sparse histogra buckets */ | |
| std::map<int64_t, int64_t> latencyHistogramMilliseconds; | |
| }; | |
| /** Static config for RequestStatsStore */ | |
| struct RequestStatsStoreConfig { | |
| nlohmann::json toJson() const; | |
| bool enable = true; | |
| /** config to enable rde rate limiting*/ | |
| bool enableRdeRateLimit = false; | |
| /** Number of consecutive dbus error to trigger RDE Request Rate Limiting*/ | |
| int rdeDbusErrorThreshold = 10; | |
| /** When rate limiting enabled, the number of RDE requests skipped | |
| before disabling disabling rate limit */ | |
| int rdeMaxSkips = 30; | |
| }; | |
| /** global store to track and managed aggregated stats */ | |
| class RequestStatsStore { | |
| public: | |
| static void initialize(const RequestStatsStoreConfig& config); | |
| /** singleton instance */ | |
| static RequestStatsStore& instance(); | |
| /** check if the store is enabled */ | |
| static bool isEnabled() { return instance().getConfig().enable; } | |
| /** check if the rde rate limiting is enabled */ | |
| static bool isRdeRateLimitEnabled() { | |
| return instance().getConfig().enableRdeRateLimit; | |
| } | |
| static int rdeErrorThreshold() { | |
| return instance().getConfig().rdeDbusErrorThreshold; | |
| } | |
| static int rdeMaxSkips() { return instance().getConfig().rdeMaxSkips; } | |
| /** log request stats */ | |
| void logRequestStats(std::chrono::steady_clock::time_point now, | |
| const RequestStatsContext& requestStatsContext); | |
| /** log Rde request stats */ | |
| void logRdeRequestStats(const RequestStatsContext& requestStatsContext); | |
| /** reset all the stats */ | |
| void clear(); | |
| /** export JSON */ | |
| nlohmann::json toJson() const; | |
| // Current RDE counters to enable/disable rate limiting | |
| void updateCurrentRdeDbusErrors(); | |
| int getCurrentRdeDbusErrors() const; | |
| void setCurrentRdeDbusErrors(int errCount); | |
| void updateCurrentRdeRequestSkips(); | |
| int getCurrentRdeRequestSkips() const; | |
| void setCurrentRdeRequestSkips(int skipCount); | |
| // used only for testing: | |
| explicit RequestStatsStore(const RequestStatsStoreConfig& config); | |
| virtual ~RequestStatsStore(); | |
| RequestStatsStore(const RequestStatsStore&) = default; | |
| RequestStatsStore& operator=(const RequestStatsStore&) = default; | |
| RequestStatsStore(RequestStatsStore&&) = default; | |
| RequestStatsStore& operator=(RequestStatsStore&&) = default; | |
| /** read only stats */ | |
| const std::map<std::string, RequestStats>& queryStats() const { | |
| return this->requestURLStats; | |
| } | |
| /** read only config */ | |
| const RequestStatsStoreConfig& getConfig() const { return this->config; } | |
| protected: | |
| /** store config */ | |
| RequestStatsStoreConfig config; | |
| /** redfish URL -> RequestStats */ | |
| std::map<std::string, RequestStats> requestURLStats; | |
| /** Rde Request Stats */ | |
| RdeRequestStats rdeStats; | |
| }; | |
| } // namespace managedStore | |
| #endif // THIRD_PARTY_GBMCWEB_INCLUDE_REQUEST_STATS_H_ |