LCOV - code coverage report
Current view: top level - source/server/admin - stats_render.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 0 211 0.0 %
Date: 2024-01-05 06:35:25 Functions: 0 20 0.0 %

          Line data    Source code
       1             : #include "source/server/admin/stats_render.h"
       2             : 
       3             : #include "source/common/stats/histogram_impl.h"
       4             : 
       5             : #include "absl/strings/str_format.h"
       6             : 
       7             : namespace Envoy {
       8             : namespace Server {
       9             : 
      10             : StatsTextRender::StatsTextRender(const StatsParams& params)
      11           0 :     : histogram_buckets_mode_(params.histogram_buckets_mode_) {}
      12             : 
      13             : void StatsTextRender::generate(Buffer::Instance& response, const std::string& name,
      14           0 :                                uint64_t value) {
      15           0 :   response.addFragments({name, ": ", absl::StrCat(value), "\n"});
      16           0 : }
      17             : 
      18             : void StatsTextRender::generate(Buffer::Instance& response, const std::string& name,
      19           0 :                                const std::string& value) {
      20           0 :   response.addFragments({name, ": \"", value, "\"\n"});
      21           0 : }
      22             : 
      23             : void StatsTextRender::generate(Buffer::Instance& response, const std::string& name,
      24           0 :                                const Stats::ParentHistogram& histogram) {
      25           0 :   if (!histogram.used()) {
      26           0 :     response.addFragments({name, ": No recorded values\n"});
      27           0 :     return;
      28           0 :   }
      29             : 
      30           0 :   switch (histogram_buckets_mode_) {
      31           0 :   case Utility::HistogramBucketsMode::NoBuckets:
      32           0 :     response.addFragments({name, ": ", histogram.quantileSummary(), "\n"});
      33           0 :     break;
      34           0 :   case Utility::HistogramBucketsMode::Cumulative:
      35           0 :     response.addFragments({name, ": ", histogram.bucketSummary(), "\n"});
      36           0 :     break;
      37           0 :   case Utility::HistogramBucketsMode::Disjoint:
      38           0 :     addDisjointBuckets(name, histogram, response);
      39           0 :     break;
      40           0 :   case Utility::HistogramBucketsMode::Detailed:
      41           0 :     response.addFragments({name, ":\n  totals="});
      42           0 :     addDetail(histogram.detailedTotalBuckets(), response);
      43           0 :     response.add("\n  intervals=");
      44           0 :     addDetail(histogram.detailedIntervalBuckets(), response);
      45           0 :     response.addFragments({"\n  summary=", histogram.quantileSummary(), "\n"});
      46           0 :     break;
      47           0 :   }
      48           0 : }
      49             : 
      50           0 : void StatsTextRender::finalize(Buffer::Instance&) {}
      51             : 
      52             : void StatsTextRender::addDetail(const std::vector<Stats::ParentHistogram::Bucket>& buckets,
      53           0 :                                 Buffer::Instance& response) {
      54           0 :   absl::string_view delim = "";
      55           0 :   for (const Stats::ParentHistogram::Bucket& bucket : buckets) {
      56           0 :     response.addFragments({delim, absl::StrFormat("%.15g,%.15g:%lu", bucket.lower_bound_,
      57           0 :                                                   bucket.width_, bucket.count_)});
      58           0 :     delim = ", ";
      59           0 :   }
      60           0 : }
      61             : 
      62             : // Computes disjoint buckets as text and adds them to the response buffer.
      63             : void StatsTextRender::addDisjointBuckets(const std::string& name,
      64             :                                          const Stats::ParentHistogram& histogram,
      65           0 :                                          Buffer::Instance& response) {
      66           0 :   if (!histogram.used()) {
      67           0 :     response.addFragments({name, ": No recorded values\n"});
      68           0 :     return;
      69           0 :   }
      70           0 :   response.addFragments({name, ": "});
      71           0 :   std::vector<absl::string_view> bucket_summary;
      72             : 
      73           0 :   const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics();
      74           0 :   Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets();
      75           0 :   const std::vector<uint64_t> disjoint_interval_buckets =
      76           0 :       interval_statistics.computeDisjointBuckets();
      77           0 :   const std::vector<uint64_t> disjoint_cumulative_buckets =
      78           0 :       histogram.cumulativeStatistics().computeDisjointBuckets();
      79             :   // Make sure all vectors are the same size.
      80           0 :   ASSERT(disjoint_interval_buckets.size() == disjoint_cumulative_buckets.size());
      81           0 :   ASSERT(disjoint_cumulative_buckets.size() == supported_buckets.size());
      82           0 :   const size_t min_size = std::min({disjoint_interval_buckets.size(),
      83           0 :                                     disjoint_cumulative_buckets.size(), supported_buckets.size()});
      84           0 :   std::vector<std::string> bucket_strings;
      85           0 :   bucket_strings.reserve(min_size);
      86           0 :   for (size_t i = 0; i < min_size; ++i) {
      87           0 :     if (i != 0) {
      88           0 :       bucket_summary.push_back(" ");
      89           0 :     }
      90           0 :     bucket_strings.push_back(fmt::format("B{:g}({},{})", supported_buckets[i],
      91           0 :                                          disjoint_interval_buckets[i],
      92           0 :                                          disjoint_cumulative_buckets[i]));
      93           0 :     bucket_summary.push_back(bucket_strings.back());
      94           0 :   }
      95           0 :   bucket_summary.push_back("\n");
      96           0 :   response.addFragments(bucket_summary);
      97           0 : }
      98             : 
      99             : StatsJsonRender::StatsJsonRender(Http::ResponseHeaderMap& response_headers,
     100             :                                  Buffer::Instance& response, const StatsParams& params)
     101             :     : histogram_buckets_mode_(params.histogram_buckets_mode_),
     102           0 :       json_(std::make_unique<JsonContext>(response)), response_(response) {
     103           0 :   response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json);
     104           0 : }
     105             : 
     106             : StatsJsonRender::JsonContext::JsonContext(Buffer::Instance& response)
     107           0 :     : streamer_(response), stats_map_(streamer_.makeRootMap()) {
     108             :   // We don't create a JSON data model for the stats output, as that makes
     109             :   // streaming difficult. Instead we emit the preamble in the constructor here,
     110             :   // and create json models for each stats entry.
     111           0 :   stats_map_->addKey("stats");
     112           0 :   stats_array_ = stats_map_->addArray();
     113           0 : }
     114             : 
     115           0 : void StatsJsonRender::drainIfNeeded(Buffer::Instance& response) {
     116           0 :   if (&response_ != &response) {
     117           0 :     response.move(response_);
     118           0 :   }
     119           0 : }
     120             : 
     121             : // Buffers a JSON fragment for a numeric stats, flushing to the response
     122             : // buffer once we exceed JsonStatsFlushCount stats.
     123             : void StatsJsonRender::generate(Buffer::Instance& response, const std::string& name,
     124           0 :                                uint64_t value) {
     125           0 :   ASSERT(!histograms_initialized_);
     126           0 :   json_->stats_array_->addMap()->addEntries({{"name", name}, {"value", value}});
     127           0 :   drainIfNeeded(response);
     128           0 : }
     129             : 
     130             : // Buffers a JSON fragment for a text-readout stat, flushing to the response
     131             : // buffer once we exceed JsonStatsFlushCount stats.
     132             : void StatsJsonRender::generate(Buffer::Instance& response, const std::string& name,
     133           0 :                                const std::string& value) {
     134           0 :   ASSERT(!histograms_initialized_);
     135           0 :   json_->stats_array_->addMap()->addEntries({{"name", name}, {"value", value}});
     136           0 :   drainIfNeeded(response);
     137           0 : }
     138             : 
     139             : // In JSON we buffer all histograms and don't write them immediately, so we
     140             : // can, in one JSON structure, emit shared attributes of all histograms and
     141             : // each individual histogram.
     142             : //
     143             : // This is counter to the goals of streaming and chunked interfaces, but
     144             : // usually there are far fewer histograms than counters or gauges.
     145             : //
     146             : // We can further optimize this by streaming out the histograms object, one
     147             : // histogram at a time, in case buffering all the histograms in Envoy
     148             : // buffers up too much memory.
     149             : void StatsJsonRender::generate(Buffer::Instance& response, const std::string& name,
     150           0 :                                const Stats::ParentHistogram& histogram) {
     151           0 :   if (!histograms_initialized_) {
     152           0 :     renderHistogramStart();
     153           0 :   }
     154             : 
     155           0 :   switch (histogram_buckets_mode_) {
     156           0 :   case Utility::HistogramBucketsMode::NoBuckets: {
     157           0 :     Json::Streamer::MapPtr map = json_->histogram_array_->addMap();
     158           0 :     map->addEntries({{"name", name}});
     159           0 :     map->addKey("values");
     160           0 :     populatePercentiles(histogram, *map);
     161           0 :     break;
     162           0 :   }
     163           0 :   case Utility::HistogramBucketsMode::Cumulative: {
     164           0 :     const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics();
     165           0 :     const std::vector<uint64_t>& interval_buckets = interval_statistics.computedBuckets();
     166           0 :     const std::vector<uint64_t>& cumulative_buckets =
     167           0 :         histogram.cumulativeStatistics().computedBuckets();
     168           0 :     collectBuckets(name, histogram, interval_buckets, cumulative_buckets);
     169           0 :     break;
     170           0 :   }
     171           0 :   case Utility::HistogramBucketsMode::Disjoint: {
     172           0 :     const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics();
     173           0 :     const std::vector<uint64_t> interval_buckets = interval_statistics.computeDisjointBuckets();
     174           0 :     const std::vector<uint64_t> cumulative_buckets =
     175           0 :         histogram.cumulativeStatistics().computeDisjointBuckets();
     176           0 :     collectBuckets(name, histogram, interval_buckets, cumulative_buckets);
     177           0 :     break;
     178           0 :   }
     179           0 :   case Utility::HistogramBucketsMode::Detailed: {
     180           0 :     generateHistogramDetail(name, histogram, *json_->histogram_array_->addMap());
     181           0 :     break;
     182           0 :   }
     183           0 :   }
     184           0 :   drainIfNeeded(response);
     185           0 : }
     186             : 
     187           0 : void StatsJsonRender::populateSupportedPercentiles(Json::Streamer::Array& array) {
     188           0 :   Stats::HistogramStatisticsImpl empty_statistics;
     189           0 :   std::vector<double> supported = empty_statistics.supportedQuantiles();
     190           0 :   std::vector<Json::Streamer::Value> views(supported.size());
     191           0 :   for (uint32_t i = 0, n = supported.size(); i < n; ++i) {
     192           0 :     views[i] = supported[i] * 100;
     193           0 :   }
     194           0 :   array.addEntries(views);
     195           0 : }
     196             : 
     197             : void StatsJsonRender::populatePercentiles(const Stats::ParentHistogram& histogram,
     198           0 :                                           Json::Streamer::Map& map) {
     199           0 :   Json::Streamer::ArrayPtr array = map.addArray();
     200           0 :   std::vector<double> totals = histogram.cumulativeStatistics().computedQuantiles(),
     201           0 :                       intervals = histogram.intervalStatistics().computedQuantiles();
     202           0 :   uint32_t min_size = std::min(totals.size(), intervals.size());
     203           0 :   ASSERT(totals.size() == min_size);
     204           0 :   ASSERT(intervals.size() == min_size);
     205           0 :   for (uint32_t i = 0; i < min_size; ++i) {
     206           0 :     array->addMap()->addEntries({{"cumulative", totals[i]}, {"interval", intervals[i]}});
     207           0 :   }
     208           0 : };
     209             : 
     210           0 : void StatsJsonRender::renderHistogramStart() {
     211           0 :   histograms_initialized_ = true;
     212           0 :   json_->histogram_map1_ = json_->stats_array_->addMap();
     213           0 :   json_->histogram_map1_->addKey("histograms");
     214           0 :   switch (histogram_buckets_mode_) {
     215           0 :   case Utility::HistogramBucketsMode::Detailed:
     216           0 :     json_->histogram_map2_ = json_->histogram_map1_->addMap();
     217           0 :     json_->histogram_map2_->addKey("supported_percentiles");
     218           0 :     { populateSupportedPercentiles(*json_->histogram_map2_->addArray()); }
     219           0 :     json_->histogram_map2_->addKey("details");
     220           0 :     json_->histogram_array_ = json_->histogram_map2_->addArray();
     221           0 :     break;
     222           0 :   case Utility::HistogramBucketsMode::NoBuckets:
     223           0 :     json_->histogram_map2_ = json_->histogram_map1_->addMap();
     224           0 :     json_->histogram_map2_->addKey("supported_quantiles");
     225           0 :     { populateSupportedPercentiles(*json_->histogram_map2_->addArray()); }
     226           0 :     json_->histogram_map2_->addKey("computed_quantiles");
     227           0 :     json_->histogram_array_ = json_->histogram_map2_->addArray();
     228           0 :     break;
     229           0 :   case Utility::HistogramBucketsMode::Cumulative:
     230           0 :   case Utility::HistogramBucketsMode::Disjoint:
     231           0 :     json_->histogram_array_ = json_->histogram_map1_->addArray();
     232           0 :     break;
     233           0 :   }
     234           0 : }
     235             : 
     236             : void StatsJsonRender::generateHistogramDetail(const std::string& name,
     237             :                                               const Stats::ParentHistogram& histogram,
     238           0 :                                               Json::Streamer::Map& map) {
     239             :   // Now we produce the stream-able histogram records, without using the json intermediate
     240             :   // representation or serializer.
     241           0 :   map.addEntries({{"name", name}});
     242           0 :   map.addKey("totals");
     243           0 :   populateBucketsVerbose(histogram.detailedTotalBuckets(), map);
     244           0 :   map.addKey("intervals");
     245           0 :   populateBucketsVerbose(histogram.detailedIntervalBuckets(), map);
     246           0 :   map.addKey("percentiles");
     247           0 :   populatePercentiles(histogram, map);
     248           0 : }
     249             : 
     250             : void StatsJsonRender::populateBucketsVerbose(
     251           0 :     const std::vector<Stats::ParentHistogram::Bucket>& buckets, Json::Streamer::Map& map) {
     252           0 :   Json::Streamer::ArrayPtr buckets_array = map.addArray();
     253           0 :   for (const Stats::ParentHistogram::Bucket& bucket : buckets) {
     254           0 :     buckets_array->addMap()->addEntries(
     255           0 :         {{"lower_bound", bucket.lower_bound_}, {"width", bucket.width_}, {"count", bucket.count_}});
     256           0 :   }
     257           0 : }
     258             : 
     259             : // Since histograms are buffered (see above), the finalize() method generates
     260             : // all of them.
     261           0 : void StatsJsonRender::finalize(Buffer::Instance& response) {
     262           0 :   json_.reset();
     263           0 :   drainIfNeeded(response);
     264           0 : }
     265             : 
     266             : // Collects the buckets from the specified histogram, using either the
     267             : // cumulative or disjoint views, as controlled by buckets_fn.
     268             : void StatsJsonRender::collectBuckets(const std::string& name,
     269             :                                      const Stats::ParentHistogram& histogram,
     270             :                                      const std::vector<uint64_t>& interval_buckets,
     271           0 :                                      const std::vector<uint64_t>& cumulative_buckets) {
     272           0 :   const Stats::HistogramStatistics& interval_statistics = histogram.intervalStatistics();
     273           0 :   Stats::ConstSupportedBuckets& supported_buckets = interval_statistics.supportedBuckets();
     274             : 
     275             :   // Make sure all vectors are the same size.
     276           0 :   ASSERT(interval_buckets.size() == cumulative_buckets.size());
     277           0 :   ASSERT(cumulative_buckets.size() == supported_buckets.size());
     278           0 :   size_t min_size =
     279           0 :       std::min({interval_buckets.size(), cumulative_buckets.size(), supported_buckets.size()});
     280             : 
     281           0 :   Json::Streamer::MapPtr map = json_->histogram_array_->addMap();
     282           0 :   map->addEntries({{"name", name}});
     283           0 :   map->addKey("buckets");
     284           0 :   Json::Streamer::ArrayPtr buckets = map->addArray();
     285           0 :   for (uint32_t i = 0; i < min_size; ++i) {
     286           0 :     Json::Streamer::MapPtr bucket_map = buckets->addMap();
     287           0 :     bucket_map->addEntries({{"upper_bound", supported_buckets[i]},
     288           0 :                             {"interval", interval_buckets[i]},
     289           0 :                             {"cumulative", cumulative_buckets[i]}});
     290           0 :   }
     291           0 : }
     292             : 
     293             : } // namespace Server
     294             : } // namespace Envoy

Generated by: LCOV version 1.15