Line data Source code
1 : #include "source/server/admin/stats_handler.h"
2 :
3 : #include <functional>
4 : #include <vector>
5 :
6 : #include "envoy/admin/v3/mutex_stats.pb.h"
7 : #include "envoy/server/admin.h"
8 :
9 : #include "source/common/buffer/buffer_impl.h"
10 : #include "source/common/common/empty_string.h"
11 : #include "source/common/http/headers.h"
12 : #include "source/common/http/utility.h"
13 : #include "source/server/admin/prometheus_stats.h"
14 : #include "source/server/admin/stats_request.h"
15 :
16 : #include "absl/strings/numbers.h"
17 :
18 : namespace Envoy {
19 : namespace Server {
20 :
21 : const uint64_t RecentLookupsCapacity = 100;
22 :
23 134 : StatsHandler::StatsHandler(Server::Instance& server) : HandlerContextBase(server) {}
24 :
25 : Http::Code StatsHandler::handlerResetCounters(Http::ResponseHeaderMap&, Buffer::Instance& response,
26 0 : AdminStream&) {
27 0 : for (const Stats::CounterSharedPtr& counter : server_.stats().counters()) {
28 0 : counter->reset();
29 0 : }
30 0 : server_.stats().symbolTable().clearRecentLookups();
31 0 : response.add("OK\n");
32 0 : return Http::Code::OK;
33 0 : }
34 :
35 : Http::Code StatsHandler::handlerStatsRecentLookups(Http::ResponseHeaderMap&,
36 0 : Buffer::Instance& response, AdminStream&) {
37 0 : Stats::SymbolTable& symbol_table = server_.stats().symbolTable();
38 0 : std::string table;
39 0 : const uint64_t total =
40 0 : symbol_table.getRecentLookups([&table](absl::string_view name, uint64_t count) {
41 0 : table += fmt::format("{:8d} {}\n", count, name);
42 0 : });
43 0 : if (table.empty() && symbol_table.recentLookupCapacity() == 0) {
44 0 : table = "Lookup tracking is not enabled. Use /stats/recentlookups/enable to enable.\n";
45 0 : } else {
46 0 : response.add(" Count Lookup\n");
47 0 : }
48 0 : response.add(absl::StrCat(table, "\ntotal: ", total, "\n"));
49 0 : return Http::Code::OK;
50 0 : }
51 :
52 : Http::Code StatsHandler::handlerStatsRecentLookupsClear(Http::ResponseHeaderMap&,
53 0 : Buffer::Instance& response, AdminStream&) {
54 0 : server_.stats().symbolTable().clearRecentLookups();
55 0 : response.add("OK\n");
56 0 : return Http::Code::OK;
57 0 : }
58 :
59 : Http::Code StatsHandler::handlerStatsRecentLookupsDisable(Http::ResponseHeaderMap&,
60 : Buffer::Instance& response,
61 0 : AdminStream&) {
62 0 : server_.stats().symbolTable().setRecentLookupCapacity(0);
63 0 : response.add("OK\n");
64 0 : return Http::Code::OK;
65 0 : }
66 :
67 : Http::Code StatsHandler::handlerStatsRecentLookupsEnable(Http::ResponseHeaderMap&,
68 0 : Buffer::Instance& response, AdminStream&) {
69 0 : server_.stats().symbolTable().setRecentLookupCapacity(RecentLookupsCapacity);
70 0 : response.add("OK\n");
71 0 : return Http::Code::OK;
72 0 : }
73 :
74 0 : Admin::RequestPtr StatsHandler::makeRequest(AdminStream& admin_stream) {
75 0 : StatsParams params;
76 0 : Buffer::OwnedImpl response;
77 0 : Http::Code code = params.parse(admin_stream.getRequestHeaders().getPathValue(), response);
78 0 : if (code != Http::Code::OK) {
79 0 : return Admin::makeStaticTextRequest(response, code);
80 0 : }
81 :
82 0 : if (params.format_ == StatsFormat::Prometheus) {
83 : // TODO(#16139): modify streaming algorithm to cover Prometheus.
84 : //
85 : // This may be easiest to accomplish by populating the set
86 : // with tagExtractedName(), and allowing for vectors of
87 : // stats as multiples will have the same tag-extracted names.
88 : // Ideally we'd find a way to do this without slowing down
89 : // the non-Prometheus implementations.
90 0 : Buffer::OwnedImpl response;
91 0 : prometheusFlushAndRender(params, response);
92 0 : return Admin::makeStaticTextRequest(response, code);
93 0 : }
94 :
95 0 : if (server_.statsConfig().flushOnAdmin()) {
96 0 : server_.flushStats();
97 0 : }
98 :
99 0 : bool active_mode;
100 0 : #ifdef ENVOY_ADMIN_HTML
101 0 : active_mode = params.format_ == StatsFormat::ActiveHtml;
102 : #else
103 : active_mode = false;
104 : #endif
105 0 : return makeRequest(
106 0 : server_.stats(), params, server_.clusterManager(),
107 0 : [this, active_mode]() -> Admin::UrlHandler { return statsHandler(active_mode); });
108 0 : }
109 :
110 : Admin::RequestPtr StatsHandler::makeRequest(Stats::Store& stats, const StatsParams& params,
111 : const Upstream::ClusterManager& cluster_manager,
112 0 : StatsRequest::UrlHandlerFn url_handler_fn) {
113 0 : return std::make_unique<StatsRequest>(stats, params, cluster_manager, url_handler_fn);
114 0 : }
115 :
116 : Http::Code StatsHandler::handlerPrometheusStats(Http::ResponseHeaderMap&,
117 : Buffer::Instance& response,
118 0 : AdminStream& admin_stream) {
119 0 : return prometheusStats(admin_stream.getRequestHeaders().getPathValue(), response);
120 0 : }
121 :
122 : Http::Code StatsHandler::prometheusStats(absl::string_view path_and_query,
123 0 : Buffer::Instance& response) {
124 0 : StatsParams params;
125 0 : Http::Code code = params.parse(path_and_query, response);
126 0 : if (code != Http::Code::OK) {
127 0 : return code;
128 0 : }
129 :
130 0 : if (server_.statsConfig().flushOnAdmin()) {
131 0 : server_.flushStats();
132 0 : }
133 :
134 0 : prometheusFlushAndRender(params, response);
135 0 : return Http::Code::OK;
136 0 : }
137 :
138 0 : void StatsHandler::prometheusFlushAndRender(const StatsParams& params, Buffer::Instance& response) {
139 0 : if (server_.statsConfig().flushOnAdmin()) {
140 0 : server_.flushStats();
141 0 : }
142 0 : prometheusRender(server_.stats(), server_.api().customStatNamespaces(), server_.clusterManager(),
143 0 : params, response);
144 0 : }
145 :
146 : void StatsHandler::prometheusRender(Stats::Store& stats,
147 : const Stats::CustomStatNamespaces& custom_namespaces,
148 : const Upstream::ClusterManager& cluster_manager,
149 0 : const StatsParams& params, Buffer::Instance& response) {
150 0 : const std::vector<Stats::TextReadoutSharedPtr>& text_readouts_vec =
151 0 : params.prometheus_text_readouts_ ? stats.textReadouts()
152 0 : : std::vector<Stats::TextReadoutSharedPtr>();
153 0 : PrometheusStatsFormatter::statsAsPrometheus(stats.counters(), stats.gauges(), stats.histograms(),
154 0 : text_readouts_vec, cluster_manager, response, params,
155 0 : custom_namespaces);
156 0 : }
157 :
158 : Http::Code StatsHandler::handlerContention(Http::ResponseHeaderMap& response_headers,
159 0 : Buffer::Instance& response, AdminStream&) {
160 :
161 0 : if (server_.options().mutexTracingEnabled() && server_.mutexTracer() != nullptr) {
162 0 : response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json);
163 :
164 0 : envoy::admin::v3::MutexStats mutex_stats;
165 0 : mutex_stats.set_num_contentions(server_.mutexTracer()->numContentions());
166 0 : mutex_stats.set_current_wait_cycles(server_.mutexTracer()->currentWaitCycles());
167 0 : mutex_stats.set_lifetime_wait_cycles(server_.mutexTracer()->lifetimeWaitCycles());
168 0 : response.add(MessageUtil::getJsonStringFromMessageOrError(mutex_stats, true, true));
169 0 : } else {
170 0 : response.add("Mutex contention tracing is not enabled. To enable, run Envoy with flag "
171 0 : "--enable-mutex-tracing.");
172 0 : }
173 0 : return Http::Code::OK;
174 0 : }
175 :
176 134 : Admin::UrlHandler StatsHandler::statsHandler(bool active_mode) {
177 134 : Admin::ParamDescriptor usedonly{
178 134 : Admin::ParamDescriptor::Type::Boolean, "usedonly",
179 134 : "Only include stats that have been written by system since restart"};
180 134 : Admin::ParamDescriptor histogram_buckets{Admin::ParamDescriptor::Type::Enum,
181 134 : "histogram_buckets",
182 134 : "Histogram bucket display mode",
183 134 : {"cumulative", "disjoint", "detailed", "none"}};
184 134 : Admin::ParamDescriptor format{Admin::ParamDescriptor::Type::Enum,
185 134 : "format",
186 134 : "Format to use",
187 134 : {"html", "active-html", "text", "json"}};
188 134 : Admin::ParamDescriptor filter{Admin::ParamDescriptor::Type::String, "filter",
189 134 : "Regular expression (Google re2) for filtering stats"};
190 134 : Admin::ParamDescriptor type{Admin::ParamDescriptor::Type::Enum,
191 134 : "type",
192 134 : "Stat types to include.",
193 134 : {StatLabels::All, StatLabels::Counters, StatLabels::Histograms,
194 134 : StatLabels::Gauges, StatLabels::TextReadouts}};
195 :
196 134 : Admin::ParamDescriptorVec params{usedonly, filter};
197 134 : if (!active_mode) {
198 134 : params.push_back(format);
199 134 : }
200 134 : params.push_back(type);
201 134 : if (!active_mode) {
202 134 : params.push_back(histogram_buckets);
203 134 : }
204 :
205 134 : return {
206 134 : "/stats",
207 134 : "print server stats",
208 134 : [this](AdminStream& admin_stream) -> Admin::RequestPtr { return makeRequest(admin_stream); },
209 134 : false,
210 134 : false,
211 134 : params};
212 134 : }
213 :
214 : } // namespace Server
215 : } // namespace Envoy
|