Line data Source code
1 : #include "source/server/admin/stats_html_render.h" 2 : 3 : #include <algorithm> 4 : 5 : #include "source/common/buffer/buffer_impl.h" 6 : #include "source/common/common/assert.h" 7 : #include "source/common/filesystem/filesystem_impl.h" 8 : #include "source/common/html/utility.h" 9 : #include "source/server/admin/admin_html_util.h" 10 : 11 : #include "absl/strings/str_replace.h" 12 : 13 : // Note: if you change this file, it's advisable to manually run 14 : // test/integration/admin_html/web_test.sh to semi-automatically validate 15 : // the web interface, in addition to updating and running unit tests. 16 : // 17 : // The admin web test does not yet run automatically. 18 : 19 : namespace Envoy { 20 : namespace Server { 21 : 22 : StatsHtmlRender::StatsHtmlRender(Http::ResponseHeaderMap& response_headers, 23 : Buffer::Instance& response, const StatsParams& params) 24 : : StatsTextRender(params), active_(params.format_ == StatsFormat::ActiveHtml), 25 : json_histograms_(!active_ && 26 0 : params.histogram_buckets_mode_ == Utility::HistogramBucketsMode::Detailed) { 27 0 : AdminHtmlUtil::renderHead(response_headers, response); 28 0 : } 29 : 30 0 : void StatsHtmlRender::finalize(Buffer::Instance& response) { 31 0 : if (first_histogram_) { 32 0 : response.add("</pre>\n"); 33 0 : } 34 : 35 0 : AdminHtmlUtil::finalize(response); 36 0 : } 37 : 38 : void StatsHtmlRender::setupStatsPage(const Admin::UrlHandler& url_handler, 39 0 : const StatsParams& params, Buffer::Instance& response) { 40 0 : AdminHtmlUtil::renderTableBegin(response); 41 0 : AdminHtmlUtil::renderEndpointTableRow(response, url_handler, params.query_, 1, !active_, active_); 42 0 : if (active_) { 43 0 : std::string buf; 44 0 : response.add(AdminHtmlUtil::getResource("active_params.html", buf)); 45 0 : } 46 0 : AdminHtmlUtil::renderTableEnd(response); 47 0 : std::string buf; 48 0 : if (active_) { 49 0 : std::string buf2; 50 0 : response.addFragments({"<script>\n", AdminHtmlUtil::getResource("histograms.js", buf), 51 0 : AdminHtmlUtil::getResource("active_stats.js", buf2), "</script>\n"}); 52 0 : } else { 53 0 : response.addFragments( 54 0 : {"<script>\n", AdminHtmlUtil::getResource("histograms.js", buf), "</script>\n<pre>\n"}); 55 0 : } 56 0 : } 57 : 58 : void StatsHtmlRender::generate(Buffer::Instance& response, const std::string& name, 59 0 : const std::string& value) { 60 0 : ASSERT(first_histogram_); 61 0 : response.addFragments({name, ": \"", Html::Utility::sanitize(value), "\"\n"}); 62 0 : } 63 : 64 0 : void StatsHtmlRender::noStats(Buffer::Instance& response, absl::string_view types) { 65 0 : ASSERT(first_histogram_); 66 0 : if (!active_) { 67 0 : response.addFragments({"</pre>\n<br/><i>No ", types, " found</i><br/>\n<pre>\n"}); 68 0 : } 69 0 : } 70 : 71 : // When using Detailed mode, we override the generate method for HTML to trigger 72 : // some JS that will render the histogram graphically. We will render that from 73 : // JavaScript and convey the histogram data to the JS via JSON, so we can 74 : // delegate to an instantiated JSON `sub-renderer` that will write into 75 : // json_data_. that `sub_renderer` will only be populated in Detailed mode. 76 : // 77 : // All other modes default to rendering the histogram textually. 78 : void StatsHtmlRender::generate(Buffer::Instance& response, const std::string& name, 79 0 : const Stats::ParentHistogram& histogram) { 80 0 : if (json_histograms_) { 81 0 : Json::Streamer streamer(response); 82 : 83 : // If this is the first histogram we are rendering, then we need to first 84 : // generate the supported-percentiles array sand save it in a constant. 85 : // 86 : // We use a separate <script> tag for each histogram so that the browser can 87 : // begin parsing each potentially large histogram as it is generated,rather 88 : // than building up a huge json structure with all the histograms and 89 : // blocking rendering until that is parsed. 90 0 : if (first_histogram_) { 91 0 : first_histogram_ = false; 92 0 : response.add("</pre>\n<div id='histograms'></div>\n<script>\nconst supportedPercentiles = "); 93 0 : { StatsJsonRender::populateSupportedPercentiles(*streamer.makeRootArray()); } 94 0 : response.add(";\nconst histogramDiv = document.getElementById('histograms');\n"); 95 : // The first histogram will share the first script tag with the histogram 96 : // div and supportedPercentiles array constants. 97 0 : } else { 98 0 : response.add("<script>\n"); 99 0 : } 100 0 : response.add("renderHistogram(histogramDiv, supportedPercentiles,\n"); 101 0 : { StatsJsonRender::generateHistogramDetail(name, histogram, *streamer.makeRootMap()); } 102 0 : response.add(");\n</script>\n"); 103 0 : } else { 104 0 : StatsTextRender::generate(response, name, histogram); 105 0 : } 106 0 : } 107 : 108 : } // namespace Server 109 : } // namespace Envoy