status_page.cc
1 // Copyright 2018, Beeri 15. All rights reserved.
2 // Author: Roman Gershman (romange@gmail.com)
3 //
4 #include "util/http/status_page.h"
5 
6 #include "absl/strings/str_replace.h"
7 #include "base/walltime.h"
8 #include "util/proc_stats.h"
9 #include "util/stats/varz_stats.h"
10 
11 namespace util {
12 namespace http {
13 using namespace std;
14 using namespace boost;
15 using beast::http::field;
16 namespace h2 = beast::http;
17 typedef h2::response<h2::string_body> StringResponse;
18 
19 namespace {
20 
21 string GetTimerString(uint64 seconds) {
22  char buf[128];
23  uint32 hours = seconds / 3600;
24  seconds = seconds % 3600;
25  uint32 mins = seconds / 60;
26  uint32 secs = seconds % 60;
27  snprintf(buf, sizeof buf, "%" PRIu32 ":%" PRIu32 ":%" PRIu32, hours, mins, secs);
28  return buf;
29 }
30 
31 string StatusLine(const string& name, const string& val) {
32  string res("<div>");
33  res.append(name).append(":<span class='key_text'>").append(val).append("</span></div>\n");
34  return res;
35 }
36 } // namespace
37 
38 StringResponse BuildStatusPage(const QueryArgs& args, const char* resource_prefix) {
39  StringResponse response(h2::status::ok, 11);
40 
41  bool output_json = false;
42 
43  string varz;
44  auto start = base::GetMonotonicMicrosFast();
45 
46  VarzListNode::IterateValues([&varz](const string& nm, const string& val) {
47  absl::StrAppend(&varz, "\"", nm, "\": ", val, ",\n");
48  });
49  absl::StrAppend(&varz, "\"current-time\": ", time(nullptr), ",\n");
50  varz.resize(varz.size() - 2);
51 
52  for (const auto& k_v : args) {
53  if (k_v.first == "o" && k_v.second == "json")
54  output_json = true;
55  }
56 
57  auto delta_ms = (base::GetMonotonicMicrosFast() - start) / 1000;
58  if (output_json) {
59  response.set(field::content_type, kJsonMime);
60  response.body() = "{" + varz + "}\n";
61  return response;
62  }
63 
64  string a = "<!DOCTYPE html>\n<html><head>\n";
65  a += R"(<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>
66  <link href='https://fonts.googleapis.com/css?family=Roboto:400,300' rel='stylesheet'
67  type='text/css'>
68  <link rel='stylesheet' href='{s3_path}/status_page.css'>
69  <script type="text/javascript" src="{s3_path}/status_page.js"></script>
70 </head>
71 <body>
72 <div><img src='{s3_path}/logo.png' width="160"/></div>)";
73 
74  a = absl::StrReplaceAll(a, {{"{s3_path}", resource_prefix}});
75 
76  a += "\n<div class='left_panel'></div>\n";
77  a += "<div class='styled_border'>\n";
78  a += StatusLine("Status", "OK");
79 
80  static std::atomic<time_t> start_time_cached{0};
81  time_t start_time = start_time_cached.load(std::memory_order_relaxed);
82  if (start_time == 0) {
83  util::ProcessStats stats = util::ProcessStats::Read();
84  start_time = stats.start_time_seconds;
85  start_time_cached.store(stats.start_time_seconds, std::memory_order_seq_cst);
86  }
87  a += StatusLine("Started on", base::PrintLocalTime(start_time));
88  a += StatusLine("Uptime", GetTimerString(time(NULL) - start_time));
89  a += StatusLine("Render Latency", absl::StrCat(delta_ms, " ms"));
90 
91  a += R"(</div>
92 </body>
93 <script>
94 var json_text1 = {)";
95  a += varz + R"(};
96 document.querySelector('.left_panel').innerHTML = JsonToHTML(json_text1);
97 </script>
98 </html>)";
99 
100  response.set(field::content_type, kHtmlMime);
101  response.body() = std::move(a);
102  return response;
103 }
104 
105 } // namespace http
106 } // namespace util