YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/server/default-path-handlers.cc
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
// Licensed under the Apache License, Version 2.0 (the "License");
33
// you may not use this file except in compliance with the License.
34
// You may obtain a copy of the License at
35
//
36
// http://www.apache.org/licenses/LICENSE-2.0
37
//
38
// Unless required by applicable law or agreed to in writing, software
39
// distributed under the License is distributed on an "AS IS" BASIS,
40
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
41
// See the License for the specific language governing permissions and
42
// limitations under the License.
43
44
#include "yb/server/default-path-handlers.h"
45
46
#include <sys/stat.h>
47
48
#include <fstream>
49
#include <functional>
50
#include <memory>
51
#include <sstream>
52
#include <string>
53
#include <unordered_set>
54
#include <vector>
55
56
#include <boost/algorithm/string.hpp>
57
58
#ifdef TCMALLOC_ENABLED
59
#include <gperftools/malloc_extension.h>
60
#endif
61
62
#include "yb/fs/fs_manager.h"
63
64
#include "yb/gutil/map-util.h"
65
#include "yb/gutil/strings/human_readable.h"
66
#include "yb/gutil/strings/split.h"
67
#include "yb/gutil/strings/substitute.h"
68
#include "yb/server/pprof-path-handlers.h"
69
#include "yb/server/webserver.h"
70
#include "yb/util/flag_tags.h"
71
#include "yb/util/format.h"
72
#include "yb/util/histogram.pb.h"
73
#include "yb/util/logging.h"
74
#include "yb/util/mem_tracker.h"
75
#include "yb/util/memory/memory.h"
76
#include "yb/util/metrics.h"
77
#include "yb/util/jsonwriter.h"
78
#include "yb/util/result.h"
79
#include "yb/util/status_log.h"
80
#include "yb/util/version_info.h"
81
#include "yb/util/version_info.pb.h"
82
83
DEFINE_uint64(web_log_bytes, 1024 * 1024,
84
    "The maximum number of bytes to display on the debug webserver's log page");
85
DECLARE_int32(max_tables_metrics_breakdowns);
86
TAG_FLAG(web_log_bytes, advanced);
87
TAG_FLAG(web_log_bytes, runtime);
88
89
namespace yb {
90
91
using boost::replace_all;
92
using google::CommandlineFlagsIntoString;
93
using std::ifstream;
94
using std::string;
95
using std::endl;
96
using std::shared_ptr;
97
using strings::Substitute;
98
99
using namespace std::placeholders;
100
101
namespace {
102
103
// Html/Text formatting tags
104
struct Tags {
105
  string pre_tag, end_pre_tag, line_break, header, end_header;
106
107
  // If as_text is true, set the html tags to a corresponding raw text representation.
108
2
  explicit Tags(bool as_text) {
109
2
    if (as_text) {
110
2
      pre_tag = "";
111
2
      end_pre_tag = "\n";
112
2
      line_break = "\n";
113
2
      header = "";
114
2
      end_header = "";
115
0
    } else {
116
0
      pre_tag = "<pre>";
117
0
      end_pre_tag = "</pre>";
118
0
      line_break = "<br/>";
119
0
      header = "<h2>";
120
0
      end_header = "</h2>";
121
0
    }
122
2
  }
123
};
124
125
// Writes the last FLAGS_web_log_bytes of the INFO logfile to a webpage
126
// Note to get best performance, set GLOG_logbuflevel=-1 to prevent log buffering
127
0
static void LogsHandler(const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
128
0
  std::stringstream *output = &resp->output;
129
0
  bool as_text = (req.parsed_args.find("raw") != req.parsed_args.end());
130
0
  Tags tags(as_text);
131
0
  string logfile;
132
0
  GetFullLogFilename(google::INFO, &logfile);
133
0
  (*output) << tags.header <<"INFO logs" << tags.end_header << endl;
134
0
  (*output) << "Log path is: " << logfile << endl;
135
136
0
  struct stat file_stat;
137
0
  if (stat(logfile.c_str(), &file_stat) == 0) {
138
0
    size_t size = file_stat.st_size;
139
0
    size_t seekpos = size < FLAGS_web_log_bytes ? 0L : size - FLAGS_web_log_bytes;
140
0
    ifstream log(logfile.c_str(), std::ios::in);
141
    // Note if the file rolls between stat and seek, this could fail
142
    // (and we could wind up reading the whole file). But because the
143
    // file is likely to be small, this is unlikely to be an issue in
144
    // practice.
145
0
    log.seekg(seekpos);
146
0
    (*output) << tags.line_break <<"Showing last " << FLAGS_web_log_bytes
147
0
              << " bytes of log" << endl;
148
0
    (*output) << tags.line_break << tags.pre_tag << log.rdbuf() << tags.end_pre_tag;
149
150
0
  } else {
151
0
    (*output) << tags.line_break << "Couldn't open INFO log file: " << logfile;
152
0
  }
153
0
}
154
155
// Registered to handle "/flags", and prints out all command-line flags and their values
156
1
static void FlagsHandler(const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
157
1
  std::stringstream *output = &resp->output;
158
1
  bool as_text = (req.parsed_args.find("raw") != req.parsed_args.end());
159
1
  Tags tags(as_text);
160
1
  (*output) << tags.header << "Command-line Flags" << tags.end_header;
161
1
  (*output) << tags.pre_tag;
162
1
  std::vector<google::CommandLineFlagInfo> flag_infos;
163
1
  google::GetAllFlags(&flag_infos);
164
208
  for (const auto& flag_info : flag_infos) {
165
208
    (*output) << "--" << flag_info.name << "=";
166
208
    std::unordered_set<string> tags;
167
208
    GetFlagTags(flag_info.name, &tags);
168
208
    if (PREDICT_FALSE(ContainsKey(tags, "sensitive_info"))) {
169
0
      (*output) << "****" << endl;
170
208
    } else {
171
208
      (*output) << flag_info.current_value << endl;
172
208
    }
173
208
  }
174
1
  (*output) << tags.end_pre_tag;
175
1
}
176
177
// Registered to handle "/status", and simply returns empty JSON.
178
1
static void StatusHandler(const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
179
1
  std::stringstream *output = &resp->output;
180
1
  (*output) << "{}";
181
1
}
182
183
// Registered to handle "/memz", and prints out memory allocation statistics.
184
1
static void MemUsageHandler(const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
185
1
  std::stringstream *output = &resp->output;
186
1
  bool as_text = (req.parsed_args.find("raw") != req.parsed_args.end());
187
1
  Tags tags(as_text);
188
189
1
  (*output) << tags.pre_tag;
190
1
#ifndef TCMALLOC_ENABLED
191
1
  (*output) << "Memory tracking is not available unless tcmalloc is enabled.";
192
#else
193
  auto tmp = TcMallocStats();
194
  // Replace new lines with <br> for html.
195
  replace_all(tmp, "\n", tags.line_break);
196
  (*output) << tmp << tags.end_pre_tag;
197
#endif
198
1
}
199
200
// Registered to handle "/mem-trackers", and prints out to handle memory tracker information.
201
2
static void MemTrackersHandler(const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
202
2
  std::stringstream *output = &resp->output;
203
2
  *output << "<h1>Memory usage by subsystem</h1>\n";
204
2
  *output << "<table class='table table-striped'>\n";
205
2
  *output << "  <tr><th>Id</th><th>Current Consumption</th>"
206
2
      "<th>Peak consumption</th><th>Limit</th></tr>\n";
207
208
2
  int max_depth = INT_MAX;
209
2
  string depth = FindWithDefault(req.parsed_args, "max_depth", "");
210
2
  if (depth != "") {
211
0
    max_depth = std::stoi(depth);
212
0
  }
213
214
2
  std::vector<MemTrackerData> trackers;
215
2
  CollectMemTrackerData(MemTracker::GetRootTracker(), 0, &trackers);
216
94
  for (const auto& data : trackers) {
217
    // If the data.depth >= max_depth, skip the info.
218
94
    if (data.depth > max_depth) {
219
0
      continue;
220
0
    }
221
94
    const auto& tracker = data.tracker;
222
94
    const std::string limit_str =
223
76
        tracker->limit() == -1 ? "none" : HumanReadableNumBytes::ToString(tracker->limit());
224
94
    const std::string current_consumption_str =
225
94
        HumanReadableNumBytes::ToString(tracker->consumption());
226
94
    const std::string peak_consumption_str =
227
94
        HumanReadableNumBytes::ToString(tracker->peak_consumption());
228
94
    *output << Format("  <tr data-depth=\"$0\" class=\"level$0\">\n", data.depth);
229
94
    *output << "    <td>" << tracker->id() << "</td>";
230
    // UpdateConsumption returns true if consumption is taken from external source,
231
    // for instance tcmalloc stats. So we should show only it in this case.
232
94
    if (!data.consumption_excluded_from_ancestors || data.tracker->UpdateConsumption()) {
233
82
      *output << Format("<td>$0</td>", current_consumption_str);
234
12
    } else {
235
12
      auto full_consumption_str = HumanReadableNumBytes::ToString(
236
12
          tracker->consumption() + data.consumption_excluded_from_ancestors);
237
12
      *output << Format("<td>$0 ($1)</td>", current_consumption_str, full_consumption_str);
238
12
    }
239
94
    *output << Format("<td>$0</td><td>$1</td>\n", peak_consumption_str, limit_str);
240
94
    *output << "  </tr>\n";
241
94
  }
242
243
2
  *output << "</table>\n";
244
2
}
245
246
15.3k
static MetricLevel MetricLevelFromName(const std::string& level) {
247
15.3k
  if (level == "debug") {
248
15.3k
    return MetricLevel::kDebug;
249
0
  } else if (level == "info") {
250
0
    return MetricLevel::kInfo;
251
0
  } else if (level == "warn") {
252
0
    return MetricLevel::kWarn;
253
0
  } else {
254
0
    return MetricLevel::kDebug;
255
0
  }
256
15.3k
}
257
258
static void ParseRequestOptions(const Webserver::WebRequest& req,
259
                                vector<string> *requested_metrics,
260
                                MetricPrometheusOptions *promethus_opts,
261
                                MetricJsonOptions *json_opts = nullptr,
262
15.3k
                                JsonWriter::Mode *json_mode = nullptr) {
263
264
15.3k
  if (requested_metrics) {
265
15.3k
    const string* requested_metrics_param = FindOrNull(req.parsed_args, "metrics");
266
15.3k
    if (requested_metrics_param != nullptr) {
267
170
      SplitStringUsing(*requested_metrics_param, ",", requested_metrics);
268
15.1k
    } else {
269
      // Default to including all metrics.
270
15.1k
      requested_metrics->push_back("*");
271
15.1k
    }
272
15.3k
  }
273
274
15.3k
  string arg;
275
15.3k
  if (json_opts) {
276
15.3k
    arg = FindWithDefault(req.parsed_args, "include_raw_histograms", "false");
277
15.3k
    json_opts->include_raw_histograms = ParseLeadingBoolValue(arg.c_str(), false);
278
279
15.3k
    arg = FindWithDefault(req.parsed_args, "include_schema", "false");
280
15.3k
    json_opts->include_schema_info = ParseLeadingBoolValue(arg.c_str(), false);
281
282
15.3k
    arg = FindWithDefault(req.parsed_args, "level", "debug");
283
15.3k
    json_opts->level = MetricLevelFromName(arg);
284
15.3k
  }
285
286
15.3k
  if (promethus_opts) {
287
19
    arg = FindWithDefault(req.parsed_args, "level", "debug");
288
19
    promethus_opts->level = MetricLevelFromName(arg);
289
19
    promethus_opts->max_tables_metrics_breakdowns = std::stoi(FindWithDefault(req.parsed_args,
290
19
      "max_tables_metrics_breakdowns", std::to_string(FLAGS_max_tables_metrics_breakdowns)));
291
19
    promethus_opts->priority_regex = FindWithDefault(req.parsed_args, "priority_regex", "");
292
19
  }
293
294
15.3k
  if (json_mode) {
295
15.3k
    arg = FindWithDefault(req.parsed_args, "compact", "false");
296
15.3k
    *json_mode =
297
15.3k
        ParseLeadingBoolValue(arg.c_str(), false) ? JsonWriter::COMPACT : JsonWriter::PRETTY;
298
15.3k
  }
299
15.3k
}
300
301
static void WriteMetricsAsJson(const MetricRegistry* const metrics,
302
15.3k
                               const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
303
15.3k
  vector<string> requested_metrics;
304
15.3k
  MetricJsonOptions opts;
305
15.3k
  JsonWriter::Mode json_mode;
306
15.3k
  ParseRequestOptions(req, &requested_metrics, /* prometheus opts */ nullptr, &opts, &json_mode);
307
308
15.3k
  std::stringstream* output = &resp->output;
309
15.3k
  JsonWriter writer(output, json_mode);
310
311
15.3k
  WARN_NOT_OK(metrics->WriteAsJson(&writer, requested_metrics, opts),
312
15.3k
              "Couldn't write JSON metrics over HTTP");
313
15.3k
}
314
315
static void WriteMetricsForPrometheus(const MetricRegistry* const metrics,
316
19
                               const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
317
19
  vector<string> requested_metrics;
318
19
  MetricPrometheusOptions opts;
319
19
  ParseRequestOptions(req, &requested_metrics, &opts);
320
321
19
  std::stringstream *output = &resp->output;
322
19
  PrometheusWriter writer(output);
323
19
  WARN_NOT_OK(metrics->WriteForPrometheus(&writer, requested_metrics, opts),
324
19
              "Couldn't write text metrics for Prometheus");
325
19
}
326
327
static void HandleGetVersionInfo(
328
0
    const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
329
0
  std::stringstream *output = &resp->output;
330
331
0
  VersionInfoPB version_info;
332
0
  VersionInfo::GetVersionInfoPB(&version_info);
333
334
0
  JsonWriter jw(output, JsonWriter::COMPACT);
335
0
  jw.StartObject();
336
337
0
  jw.String("build_id");
338
0
  jw.String(version_info.build_id());
339
0
  jw.String("build_type");
340
0
  jw.String(version_info.build_type());
341
0
  jw.String("build_number");
342
0
  jw.String(version_info.build_number());
343
0
  jw.String("build_timestamp");
344
0
  jw.String(version_info.build_timestamp());
345
0
  jw.String("build_username");
346
0
  jw.String(version_info.build_username());
347
0
  jw.String("version_number");
348
0
  jw.String(version_info.version_number());
349
0
  jw.String("build_hostname");
350
0
  jw.String(version_info.build_hostname());
351
0
  jw.String("git_revision");
352
0
  jw.String(version_info.git_hash());
353
354
0
  jw.EndObject();
355
0
}
356
357
} // anonymous namespace
358
359
17.1k
void AddDefaultPathHandlers(Webserver* webserver) {
360
17.1k
  webserver->RegisterPathHandler("/logs", "Logs", LogsHandler, true, false);
361
17.1k
  webserver->RegisterPathHandler("/varz", "Flags", FlagsHandler, true, false);
362
17.1k
  webserver->RegisterPathHandler("/status", "Status", StatusHandler, false, false);
363
17.1k
  webserver->RegisterPathHandler("/memz", "Memory (total)", MemUsageHandler, true, false);
364
17.1k
  webserver->RegisterPathHandler("/mem-trackers", "Memory (detail)",
365
17.1k
                                 MemTrackersHandler, true, false);
366
17.1k
  webserver->RegisterPathHandler("/api/v1/version-info", "Build Version Info",
367
17.1k
                                 HandleGetVersionInfo, false, false);
368
369
17.1k
  AddPprofPathHandlers(webserver);
370
17.1k
}
371
372
17.1k
void RegisterMetricsJsonHandler(Webserver* webserver, const MetricRegistry* const metrics) {
373
17.1k
  Webserver::PathHandlerCallback callback = std::bind(WriteMetricsAsJson, metrics, _1, _2);
374
17.1k
  Webserver::PathHandlerCallback prometheus_callback = std::bind(
375
17.1k
      WriteMetricsForPrometheus, metrics, _1, _2);
376
17.1k
  bool not_styled = false;
377
17.1k
  bool not_on_nav_bar = false;
378
17.1k
  webserver->RegisterPathHandler("/metrics", "Metrics", callback, not_styled, not_on_nav_bar);
379
380
  // The old name -- this is preserved for compatibility with older releases of
381
  // monitoring software which expects the old name.
382
17.1k
  webserver->RegisterPathHandler("/jsonmetricz", "Metrics", callback, not_styled, not_on_nav_bar);
383
384
17.1k
  webserver->RegisterPathHandler(
385
17.1k
      "/prometheus-metrics", "Metrics", prometheus_callback, not_styled, not_on_nav_bar);
386
17.1k
}
387
388
// Registered to handle "/drives", and prints out paths usage
389
static void PathUsageHandler(FsManager* fsmanager,
390
                             const Webserver::WebRequest& req,
391
0
                             Webserver::WebResponse* resp) {
392
0
  std::stringstream *output = &resp->output;
393
0
  *output << "<h1>Drives usage by subsystem</h1>\n";
394
0
  *output << "<table class='table table-striped'>\n";
395
0
  *output << "  <tr><th>Path</th><th>Used Space</th>"
396
0
      "<th>Total Space</th></tr>\n";
397
398
0
  Env* env = fsmanager->env();
399
0
  for (const auto& path : fsmanager->GetDataRootDirs()) {
400
0
    const auto stats = env->GetFilesystemStatsBytes(path);
401
0
    if (!stats.ok()) {
402
0
      LOG(WARNING) << stats.status();
403
0
      *output << Format("  <tr><td>$0</td><td>NA</td><td>NA</td></tr>\n", path);
404
0
      continue;
405
0
    }
406
0
    const std::string used_space_str = HumanReadableNumBytes::ToString(stats->used_space);
407
0
    const std::string total_space_str = HumanReadableNumBytes::ToString(stats->total_space);
408
0
    *output << Format("  <tr><td>$0</td><td>$1</td><td>$2</td></tr>\n",
409
0
                      path, used_space_str, total_space_str);
410
0
  }
411
0
  *output << "</table>\n";
412
0
}
413
414
17.1k
void RegisterPathUsageHandler(Webserver* webserver, FsManager* fsmanager) {
415
17.1k
  Webserver::PathHandlerCallback callback = std::bind(PathUsageHandler, fsmanager, _1, _2);
416
17.1k
  webserver->RegisterPathHandler("/drives", "Drives", callback, true, false);
417
17.1k
}
418
419
} // namespace yb