/Users/deen/code/yugabyte-db/src/yb/util/metrics.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 | | |
33 | | #include "yb/util/metrics.h" |
34 | | |
35 | | #include <map> |
36 | | #include <set> |
37 | | |
38 | | #include "yb/gutil/atomicops.h" |
39 | | #include "yb/gutil/casts.h" |
40 | | #include "yb/gutil/map-util.h" |
41 | | |
42 | | #include "yb/util/hdr_histogram.h" |
43 | | #include "yb/util/histogram.pb.h" |
44 | | #include "yb/util/jsonwriter.h" |
45 | | #include "yb/util/locks.h" |
46 | | #include "yb/util/status.h" |
47 | | #include "yb/util/status_log.h" |
48 | | |
49 | | DEFINE_bool(expose_metric_histogram_percentiles, true, |
50 | | "Should we expose the percentiles information for metrics histograms."); |
51 | | |
52 | | DEFINE_int32(max_tables_metrics_breakdowns, INT32_MAX, |
53 | | "The maxmimum number of tables to retrieve metrics for"); |
54 | | |
55 | | // Process/server-wide metrics should go into the 'server' entity. |
56 | | // More complex applications will define other entities. |
57 | | METRIC_DEFINE_entity(server); |
58 | | |
59 | | namespace yb { |
60 | | |
61 | | void RegisterMetricPrototype(const MetricPrototype* prototype); |
62 | | |
63 | | using std::string; |
64 | | using std::vector; |
65 | | using strings::Substitute; |
66 | | |
67 | | // |
68 | | // MetricUnit |
69 | | // |
70 | | |
71 | 35 | const char* MetricUnit::Name(Type unit) { |
72 | 35 | switch (unit) { |
73 | 0 | case kCacheHits: |
74 | 0 | return "hits"; |
75 | 0 | case kCacheQueries: |
76 | 0 | return "queries"; |
77 | 9 | case kBytes: |
78 | 9 | return "bytes"; |
79 | 1 | case kRequests: |
80 | 1 | return "requests"; |
81 | 0 | case kEntries: |
82 | 0 | return "entries"; |
83 | 0 | case kRows: |
84 | 0 | return "rows"; |
85 | 0 | case kCells: |
86 | 0 | return "cells"; |
87 | 0 | case kConnections: |
88 | 0 | return "connections"; |
89 | 0 | case kOperations: |
90 | 0 | return "operations"; |
91 | 0 | case kProbes: |
92 | 0 | return "probes"; |
93 | 0 | case kNanoseconds: |
94 | 0 | return "nanoseconds"; |
95 | 1 | case kMicroseconds: |
96 | 1 | return "microseconds"; |
97 | 7 | case kMilliseconds: |
98 | 7 | return "milliseconds"; |
99 | 0 | case kSeconds: |
100 | 0 | return "seconds"; |
101 | 2 | case kThreads: |
102 | 2 | return "threads"; |
103 | 0 | case kTransactions: |
104 | 0 | return "transactions"; |
105 | 0 | case kUnits: |
106 | 0 | return "units"; |
107 | 0 | case kMaintenanceOperations: |
108 | 0 | return "operations"; |
109 | 7 | case kBlocks: |
110 | 7 | return "blocks"; |
111 | 0 | case kLogBlockContainers: |
112 | 0 | return "log block containers"; |
113 | 3 | case kTasks: |
114 | 3 | return "tasks"; |
115 | 0 | case kMessages: |
116 | 0 | return "messages"; |
117 | 2 | case kContextSwitches: |
118 | 2 | return "context switches"; |
119 | 3 | case kFiles: |
120 | 3 | return "files"; |
121 | 0 | default: |
122 | 0 | return "UNKNOWN UNIT"; |
123 | 35 | } |
124 | 35 | } |
125 | | |
126 | | // |
127 | | // MetricType |
128 | | // |
129 | | |
130 | | const char* const MetricType::kGaugeType = "gauge"; |
131 | | const char* const MetricType::kCounterType = "counter"; |
132 | | const char* const MetricType::kHistogramType = "histogram"; |
133 | 35 | const char* MetricType::Name(MetricType::Type type) { |
134 | 35 | switch (type) { |
135 | 17 | case kGauge: |
136 | 17 | return kGaugeType; |
137 | 15 | case kCounter: |
138 | 15 | return kCounterType; |
139 | 1 | case kHistogram: |
140 | 1 | return kHistogramType; |
141 | 2 | default: |
142 | 2 | return "UNKNOWN TYPE"; |
143 | 35 | } |
144 | 35 | } |
145 | | |
146 | | namespace { |
147 | | |
148 | 35 | const char* MetricLevelName(MetricLevel level) { |
149 | 35 | switch (level) { |
150 | 0 | case MetricLevel::kDebug: |
151 | 0 | return "debug"; |
152 | 35 | case MetricLevel::kInfo: |
153 | 35 | return "info"; |
154 | 0 | case MetricLevel::kWarn: |
155 | 0 | return "warn"; |
156 | 0 | default: |
157 | 0 | return "UNKNOWN LEVEL"; |
158 | 35 | } |
159 | 35 | } |
160 | | |
161 | | } // anonymous namespace |
162 | | |
163 | | // |
164 | | // MetricRegistry |
165 | | // |
166 | | |
167 | 19.5k | MetricRegistry::MetricRegistry() { |
168 | 19.5k | } |
169 | | |
170 | 2.25k | MetricRegistry::~MetricRegistry() { |
171 | 2.25k | } |
172 | | |
173 | 646k | bool MetricRegistry::TabletHasBeenShutdown(const scoped_refptr<MetricEntity> entity) const { |
174 | 646k | if (strcmp(entity->prototype_->name(), "tablet") == 0 && tablets_shutdown_find(entity->id())) { |
175 | 0 | DVLOG(5) << "Do not report metrics for shutdown tablet " << entity->id(); |
176 | 263 | return true; |
177 | 263 | } |
178 | | |
179 | 646k | return false; |
180 | 646k | } |
181 | | |
182 | | Status MetricRegistry::WriteAsJson(JsonWriter* writer, |
183 | | const vector<string>& requested_metrics, |
184 | 15.3k | const MetricJsonOptions& opts) const { |
185 | 15.3k | EntityMap entities; |
186 | 15.3k | { |
187 | 15.3k | std::lock_guard<simple_spinlock> l(lock_); |
188 | 15.3k | entities = entities_; |
189 | 15.3k | } |
190 | | |
191 | 15.3k | writer->StartArray(); |
192 | 646k | for (const EntityMap::value_type& e : entities) { |
193 | 646k | if (TabletHasBeenShutdown(e.second)) { |
194 | 263 | continue; |
195 | 263 | } |
196 | | |
197 | 645k | WARN_NOT_OK(e.second->WriteAsJson(writer, requested_metrics, opts), |
198 | 645k | Substitute("Failed to write entity $0 as JSON", e.second->id())); |
199 | 645k | } |
200 | 15.3k | writer->EndArray(); |
201 | | |
202 | | // Rather than having a thread poll metrics periodically to retire old ones, |
203 | | // we'll just retire them here. The only downside is that, if no one is polling |
204 | | // metrics, we may end up leaving them around indefinitely; however, metrics are |
205 | | // small, and one might consider it a feature: if monitoring stops polling for |
206 | | // metrics, we should keep them around until the next poll. |
207 | 15.3k | entities.clear(); // necessary to deref metrics we just dumped before doing retirement scan. |
208 | 15.3k | const_cast<MetricRegistry*>(this)->RetireOldMetrics(); |
209 | 15.3k | return Status::OK(); |
210 | 15.3k | } |
211 | | |
212 | | CHECKED_STATUS MetricRegistry::WriteForPrometheus(PrometheusWriter* writer, |
213 | 0 | const MetricPrometheusOptions& opts) const { |
214 | 0 | return WriteForPrometheus(writer, {""}, opts); // Include all metrics. |
215 | 0 | } |
216 | | |
217 | | CHECKED_STATUS MetricRegistry::WriteForPrometheus(PrometheusWriter* writer, |
218 | | const vector<string>& requested_metrics, |
219 | 19 | const MetricPrometheusOptions& opts) const { |
220 | 19 | EntityMap entities; |
221 | 19 | { |
222 | 19 | std::lock_guard<simple_spinlock> l(lock_); |
223 | 19 | entities = entities_; |
224 | 19 | } |
225 | | |
226 | 226 | for (const EntityMap::value_type& e : entities) { |
227 | 226 | if (TabletHasBeenShutdown(e.second)) { |
228 | 0 | continue; |
229 | 0 | } |
230 | | |
231 | 226 | WARN_NOT_OK(e.second->WriteForPrometheus(writer, requested_metrics, opts), |
232 | 226 | Substitute("Failed to write entity $0 as Prometheus", e.second->id())); |
233 | 226 | } |
234 | 19 | RETURN_NOT_OK(writer->FlushAggregatedValues(opts.max_tables_metrics_breakdowns, |
235 | 19 | opts.priority_regex)); |
236 | | |
237 | | // Rather than having a thread poll metrics periodically to retire old ones, |
238 | | // we'll just retire them here. The only downside is that, if no one is polling |
239 | | // metrics, we may end up leaving them around indefinitely; however, metrics are |
240 | | // small, and one might consider it a feature: if monitoring stops polling for |
241 | | // metrics, we should keep them around until the next poll. |
242 | 19 | entities.clear(); // necessary to deref metrics we just dumped before doing retirement scan. |
243 | 19 | const_cast<MetricRegistry*>(this)->RetireOldMetrics(); |
244 | 19 | return Status::OK(); |
245 | 19 | } |
246 | | |
247 | 17.0k | void MetricRegistry::RetireOldMetrics() { |
248 | 17.0k | std::lock_guard<simple_spinlock> l(lock_); |
249 | 698k | for (auto it = entities_.begin(); it != entities_.end();) { |
250 | 681k | it->second->RetireOldMetrics(); |
251 | | |
252 | 681k | if (it->second->num_metrics() == 0 && it->second->HasOneRef()) { |
253 | | // No metrics and no external references to this entity, so we can retire it. |
254 | | // Unlike retiring the metrics themselves, we don't wait for any timeout |
255 | | // to retire them -- we assume that that timed retention has been satisfied |
256 | | // by holding onto the metrics inside the entity. |
257 | | |
258 | | // For a tablet that has been shutdown, metrics are being deleted. So do not track |
259 | | // the tablet anymore. |
260 | 1.88k | if (strcmp(it->second->prototype_->name(), "tablet") == 0) { |
261 | 0 | DVLOG(3) << "T " << it->first << ": " |
262 | 0 | << "Remove from set of tablets that have been shutdown so as to be freed"; |
263 | 1.52k | tablets_shutdown_erase(it->first); |
264 | 1.52k | } |
265 | | |
266 | 1.88k | entities_.erase(it++); |
267 | 679k | } else { |
268 | 679k | ++it; |
269 | 679k | } |
270 | 681k | } |
271 | 17.0k | } |
272 | | |
273 | | // |
274 | | // MetricPrototype |
275 | | // |
276 | 22.0M | MetricPrototype::MetricPrototype(CtorArgs args) : args_(std::move(args)) { |
277 | 22.0M | RegisterMetricPrototype(this); |
278 | 22.0M | } |
279 | | |
280 | | void MetricPrototype::WriteFields(JsonWriter* writer, |
281 | 75.8M | const MetricJsonOptions& opts) const { |
282 | 75.8M | writer->String("name"); |
283 | 75.8M | writer->String(name()); |
284 | | |
285 | 75.8M | if (opts.include_schema_info) { |
286 | 35 | writer->String("label"); |
287 | 35 | writer->String(label()); |
288 | | |
289 | 35 | writer->String("type"); |
290 | 35 | writer->String(MetricType::Name(type())); |
291 | | |
292 | 35 | writer->String("unit"); |
293 | 35 | writer->String(MetricUnit::Name(unit())); |
294 | | |
295 | 35 | writer->String("description"); |
296 | 35 | writer->String(description()); |
297 | | |
298 | 35 | writer->String("level"); |
299 | 35 | writer->String(MetricLevelName(level())); |
300 | 35 | } |
301 | 75.8M | } |
302 | | |
303 | | // |
304 | | // FunctionGaugeDetacher |
305 | | // |
306 | | |
307 | | scoped_refptr<MetricEntity> MetricRegistry::FindOrCreateEntity( |
308 | | const MetricEntityPrototype* prototype, |
309 | | const std::string& id, |
310 | 208k | const MetricEntity::AttributeMap& initial_attributes) { |
311 | 208k | std::lock_guard<simple_spinlock> l(lock_); |
312 | 208k | scoped_refptr<MetricEntity> e = FindPtrOrNull(entities_, id); |
313 | 208k | if (!e) { |
314 | 130k | e = new MetricEntity(prototype, id, initial_attributes); |
315 | 130k | InsertOrDie(&entities_, id, e); |
316 | 77.7k | } else { |
317 | 77.7k | e->SetAttributes(initial_attributes); |
318 | 77.7k | } |
319 | 208k | return e; |
320 | 208k | } |
321 | | |
322 | | // |
323 | | // Metric |
324 | | // |
325 | | Metric::Metric(const MetricPrototype* prototype) |
326 | 20.1M | : prototype_(prototype) { |
327 | 20.1M | } |
328 | | |
329 | | Metric::Metric(std::unique_ptr<MetricPrototype> prototype) |
330 | 2.19M | : prototype_holder_(std::move(prototype)), prototype_(prototype_holder_.get()) { |
331 | 2.19M | } |
332 | | |
333 | 1.93M | Metric::~Metric() { |
334 | 1.93M | } |
335 | | |
336 | | // |
337 | | // Gauge |
338 | | // |
339 | | |
340 | | Status Gauge::WriteAsJson(JsonWriter* writer, |
341 | 59.8M | const MetricJsonOptions& opts) const { |
342 | 59.8M | if (prototype_->level() < opts.level) { |
343 | 0 | return Status::OK(); |
344 | 0 | } |
345 | | |
346 | 59.8M | writer->StartObject(); |
347 | | |
348 | 59.8M | prototype_->WriteFields(writer, opts); |
349 | | |
350 | 59.8M | writer->String("value"); |
351 | 59.8M | WriteValue(writer); |
352 | | |
353 | 59.8M | writer->EndObject(); |
354 | 59.8M | return Status::OK(); |
355 | 59.8M | } |
356 | | |
357 | | // |
358 | | // StringGauge |
359 | | // |
360 | | |
361 | | StringGauge::StringGauge(const GaugePrototype<string>* proto, |
362 | | string initial_value) |
363 | 0 | : Gauge(proto), value_(std::move(initial_value)) {} |
364 | | |
365 | 0 | std::string StringGauge::value() const { |
366 | 0 | std::lock_guard<simple_spinlock> l(lock_); |
367 | 0 | return value_; |
368 | 0 | } |
369 | | |
370 | 0 | void StringGauge::set_value(const std::string& value) { |
371 | 0 | std::lock_guard<simple_spinlock> l(lock_); |
372 | 0 | value_ = value; |
373 | 0 | } |
374 | | |
375 | 0 | void StringGauge::WriteValue(JsonWriter* writer) const { |
376 | 0 | writer->String(value()); |
377 | 0 | } |
378 | | |
379 | | CHECKED_STATUS StringGauge::WriteForPrometheus( |
380 | | PrometheusWriter* writer, const MetricEntity::AttributeMap& attr, |
381 | 0 | const MetricPrometheusOptions& opts) const { |
382 | 0 | if (prototype_->level() < opts.level) { |
383 | 0 | return Status::OK(); |
384 | 0 | } |
385 | | |
386 | | // TODO(bogdan): don't think we need this? |
387 | | // return writer->WriteSingleEntry(attr, prototype_->name(), value()); |
388 | 0 | return Status::OK(); |
389 | 0 | } |
390 | | |
391 | | // |
392 | | // Counter |
393 | | // |
394 | | // This implementation is optimized by using a striped counter. See LongAdder for details. |
395 | | |
396 | | scoped_refptr<Counter> CounterPrototype::Instantiate( |
397 | 12.3M | const scoped_refptr<MetricEntity>& entity) const { |
398 | 12.3M | return entity->FindOrCreateCounter(this); |
399 | 12.3M | } |
400 | | |
401 | 7.69M | Counter::Counter(const CounterPrototype* proto) : Metric(proto) { |
402 | 7.69M | } |
403 | | |
404 | 15.4M | int64_t Counter::value() const { |
405 | 15.4M | return value_.Value(); |
406 | 15.4M | } |
407 | | |
408 | 113M | void Counter::Increment() { |
409 | 113M | IncrementBy(1); |
410 | 113M | } |
411 | | |
412 | 289M | void Counter::IncrementBy(int64_t amount) { |
413 | 289M | value_.IncrementBy(amount); |
414 | 289M | } |
415 | | |
416 | | Status Counter::WriteAsJson(JsonWriter* writer, |
417 | 15.4M | const MetricJsonOptions& opts) const { |
418 | 15.4M | if (prototype_->level() < opts.level) { |
419 | 0 | return Status::OK(); |
420 | 0 | } |
421 | | |
422 | 15.4M | writer->StartObject(); |
423 | | |
424 | 15.4M | prototype_->WriteFields(writer, opts); |
425 | | |
426 | 15.4M | writer->String("value"); |
427 | 15.4M | writer->Int64(value()); |
428 | | |
429 | 15.4M | writer->EndObject(); |
430 | 15.4M | return Status::OK(); |
431 | 15.4M | } |
432 | | |
433 | | CHECKED_STATUS Counter::WriteForPrometheus( |
434 | | PrometheusWriter* writer, const MetricEntity::AttributeMap& attr, |
435 | 13.6k | const MetricPrometheusOptions& opts) const { |
436 | 13.6k | if (prototype_->level() < opts.level) { |
437 | 0 | return Status::OK(); |
438 | 0 | } |
439 | | |
440 | 13.6k | return writer->WriteSingleEntry(attr, prototype_->name(), value(), |
441 | 13.6k | prototype()->aggregation_function()); |
442 | 13.6k | } |
443 | | |
444 | | // |
445 | | // MillisLag |
446 | | // |
447 | | |
448 | | scoped_refptr<MillisLag> MillisLagPrototype::Instantiate( |
449 | 0 | const scoped_refptr<MetricEntity>& entity) const { |
450 | 0 | return entity->FindOrCreateMillisLag(this); |
451 | 0 | } |
452 | | |
453 | | MillisLag::MillisLag(const MillisLagPrototype* proto) |
454 | | : Metric(proto), |
455 | | timestamp_ms_(static_cast<int64_t>(std::chrono::duration_cast<std::chrono::milliseconds>( |
456 | 105k | std::chrono::system_clock::now().time_since_epoch()).count())) { |
457 | 105k | } |
458 | | |
459 | 0 | Status MillisLag::WriteAsJson(JsonWriter* writer, const MetricJsonOptions& opts) const { |
460 | 0 | if (prototype_->level() < opts.level) { |
461 | 0 | return Status::OK(); |
462 | 0 | } |
463 | | |
464 | 0 | writer->StartObject(); |
465 | |
|
466 | 0 | prototype_->WriteFields(writer, opts); |
467 | |
|
468 | 0 | writer->String("value"); |
469 | 0 | writer->Int64(lag_ms()); |
470 | |
|
471 | 0 | writer->EndObject(); |
472 | 0 | return Status::OK(); |
473 | 0 | } |
474 | | |
475 | | Status MillisLag::WriteForPrometheus( |
476 | | PrometheusWriter* writer, const MetricEntity::AttributeMap& attr, |
477 | 0 | const MetricPrometheusOptions& opts) const { |
478 | 0 | if (prototype_->level() < opts.level) { |
479 | 0 | return Status::OK(); |
480 | 0 | } |
481 | | |
482 | 0 | return writer->WriteSingleEntry(attr, prototype_->name(), lag_ms(), |
483 | 0 | prototype()->aggregation_function()); |
484 | 0 | } |
485 | | |
486 | | AtomicMillisLag::AtomicMillisLag(const MillisLagPrototype* proto) |
487 | | : MillisLag(proto), |
488 | | atomic_timestamp_ms_(static_cast<int64_t>(std::chrono::duration_cast<std::chrono::milliseconds>( |
489 | 105k | std::chrono::system_clock::now().time_since_epoch()).count())) { |
490 | 105k | } |
491 | | |
492 | 525k | Status AtomicMillisLag::WriteAsJson(JsonWriter* writer, const MetricJsonOptions& opts) const { |
493 | 525k | if (prototype_->level() < opts.level) { |
494 | 0 | return Status::OK(); |
495 | 0 | } |
496 | | |
497 | 525k | writer->StartObject(); |
498 | | |
499 | 525k | prototype_->WriteFields(writer, opts); |
500 | | |
501 | 525k | writer->String("value"); |
502 | 525k | writer->Int64(this->lag_ms()); |
503 | | |
504 | 525k | writer->EndObject(); |
505 | 525k | return Status::OK(); |
506 | 525k | } |
507 | | |
508 | | ///////////////////////////////////////////////// |
509 | | // HistogramPrototype |
510 | | ///////////////////////////////////////////////// |
511 | | |
512 | | HistogramPrototype::HistogramPrototype(const MetricPrototype::CtorArgs& args, |
513 | | uint64_t max_trackable_value, int num_sig_digits, |
514 | | ExportPercentiles export_percentiles) |
515 | | : MetricPrototype(args), |
516 | | max_trackable_value_(max_trackable_value), |
517 | | num_sig_digits_(num_sig_digits), |
518 | 4.37M | export_percentiles_(export_percentiles) { |
519 | | // Better to crash at definition time that at instantiation time. |
520 | 0 | CHECK(HdrHistogram::IsValidHighestTrackableValue(max_trackable_value)) |
521 | 0 | << Substitute("Invalid max trackable value on histogram $0: $1", |
522 | 0 | args.name_, max_trackable_value); |
523 | 0 | CHECK(HdrHistogram::IsValidNumSignificantDigits(num_sig_digits)) |
524 | 0 | << Substitute("Invalid number of significant digits on histogram $0: $1", |
525 | 0 | args.name_, num_sig_digits); |
526 | 4.37M | } |
527 | | |
528 | | scoped_refptr<Histogram> HistogramPrototype::Instantiate( |
529 | 7.50M | const scoped_refptr<MetricEntity>& entity) const { |
530 | 7.50M | return entity->FindOrCreateHistogram(this); |
531 | 7.50M | } |
532 | | |
533 | | ///////////////////////////////////////////////// |
534 | | // Histogram |
535 | | ///////////////////////////////////////////////// |
536 | | |
537 | | Histogram::Histogram(const HistogramPrototype* proto) |
538 | | : Metric(proto), |
539 | | histogram_(new HdrHistogram(proto->max_trackable_value(), proto->num_sig_digits())), |
540 | 2.32M | export_percentiles_(proto->export_percentiles()) { |
541 | 2.32M | } |
542 | | |
543 | | Histogram::Histogram( |
544 | | std::unique_ptr <HistogramPrototype> proto, uint64_t highest_trackable_value, |
545 | | int num_significant_digits, ExportPercentiles export_percentiles) |
546 | | : Metric(std::move(proto)), |
547 | | histogram_(new HdrHistogram(highest_trackable_value, num_significant_digits)), |
548 | 43.7k | export_percentiles_(export_percentiles) { |
549 | 43.7k | } |
550 | | |
551 | 269M | void Histogram::Increment(int64_t value) { |
552 | 269M | histogram_->Increment(value); |
553 | 269M | } |
554 | | |
555 | 1 | void Histogram::IncrementBy(int64_t value, int64_t amount) { |
556 | 1 | histogram_->IncrementBy(value, amount); |
557 | 1 | } |
558 | | |
559 | | Status Histogram::WriteAsJson(JsonWriter* writer, |
560 | 4.92M | const MetricJsonOptions& opts) const { |
561 | 4.92M | if (prototype_->level() < opts.level) { |
562 | 0 | return Status::OK(); |
563 | 0 | } |
564 | | |
565 | 4.92M | HistogramSnapshotPB snapshot; |
566 | 4.92M | RETURN_NOT_OK(GetAndResetHistogramSnapshotPB(&snapshot, opts)); |
567 | 4.92M | writer->Protobuf(snapshot); |
568 | 4.92M | return Status::OK(); |
569 | 4.92M | } |
570 | | |
571 | | CHECKED_STATUS Histogram::WriteForPrometheus( |
572 | | PrometheusWriter* writer, const MetricEntity::AttributeMap& attr, |
573 | 3.91k | const MetricPrometheusOptions& opts) const { |
574 | 3.91k | if (prototype_->level() < opts.level) { |
575 | 0 | return Status::OK(); |
576 | 0 | } |
577 | | |
578 | 3.91k | HdrHistogram snapshot(*histogram_); |
579 | | // HdrHistogram reports percentiles based on all the data points from the |
580 | | // begining of time. We are interested in the percentiles based on just |
581 | | // the "newly-arrived" data. So, we will reset the histogram's percentiles |
582 | | // between each invocation. |
583 | 3.91k | histogram_->ResetPercentiles(); |
584 | | |
585 | | // Representing the sum and count require suffixed names. |
586 | 3.91k | std::string hist_name = prototype_->name(); |
587 | 3.91k | auto copy_of_attr = attr; |
588 | 3.91k | RETURN_NOT_OK(writer->WriteSingleEntry( |
589 | 3.91k | copy_of_attr, hist_name + "_sum", snapshot.TotalSum(), |
590 | 3.91k | prototype()->aggregation_function())); |
591 | 3.91k | RETURN_NOT_OK(writer->WriteSingleEntry( |
592 | 3.91k | copy_of_attr, hist_name + "_count", snapshot.TotalCount(), |
593 | 3.91k | prototype()->aggregation_function())); |
594 | | |
595 | | // Copy the label map to add the quatiles. |
596 | 3.91k | if (export_percentiles_ && FLAGS_expose_metric_histogram_percentiles) { |
597 | 2.75k | copy_of_attr["quantile"] = "p50"; |
598 | 2.75k | RETURN_NOT_OK(writer->WriteSingleEntry(copy_of_attr, hist_name, |
599 | 2.75k | snapshot.ValueAtPercentile(50), |
600 | 2.75k | prototype()->aggregation_function())); |
601 | 2.75k | copy_of_attr["quantile"] = "p95"; |
602 | 2.75k | RETURN_NOT_OK(writer->WriteSingleEntry(copy_of_attr, hist_name, |
603 | 2.75k | snapshot.ValueAtPercentile(95), |
604 | 2.75k | prototype()->aggregation_function())); |
605 | 2.75k | copy_of_attr["quantile"] = "p99"; |
606 | 2.75k | RETURN_NOT_OK(writer->WriteSingleEntry(copy_of_attr, hist_name, |
607 | 2.75k | snapshot.ValueAtPercentile(99), |
608 | 2.75k | prototype()->aggregation_function())); |
609 | 2.75k | copy_of_attr["quantile"] = "mean"; |
610 | 2.75k | RETURN_NOT_OK(writer->WriteSingleEntry(copy_of_attr, hist_name, |
611 | 2.75k | snapshot.MeanValue(), |
612 | 2.75k | prototype()->aggregation_function())); |
613 | 2.75k | copy_of_attr["quantile"] = "max"; |
614 | 2.75k | RETURN_NOT_OK(writer->WriteSingleEntry(copy_of_attr, hist_name, |
615 | 2.75k | snapshot.MaxValue(), |
616 | 2.75k | prototype()->aggregation_function())); |
617 | 2.75k | } |
618 | 3.91k | return Status::OK(); |
619 | 3.91k | } |
620 | | |
621 | | Status Histogram::GetAndResetHistogramSnapshotPB(HistogramSnapshotPB* snapshot_pb, |
622 | 4.92M | const MetricJsonOptions& opts) const { |
623 | 4.92M | HdrHistogram snapshot(*histogram_); |
624 | | // HdrHistogram reports percentiles based on all the data points from the |
625 | | // begining of time. We are interested in the percentiles based on just |
626 | | // the "newly-arrived" data. So, we will reset the histogram's percentiles |
627 | | // between each invocation. |
628 | 4.92M | histogram_->ResetPercentiles(); |
629 | | |
630 | 4.92M | snapshot_pb->set_name(prototype_->name()); |
631 | 4.92M | if (opts.include_schema_info) { |
632 | 0 | snapshot_pb->set_type(MetricType::Name(prototype_->type())); |
633 | 0 | snapshot_pb->set_label(prototype_->label()); |
634 | 0 | snapshot_pb->set_unit(MetricUnit::Name(prototype_->unit())); |
635 | 0 | snapshot_pb->set_description(prototype_->description()); |
636 | 0 | snapshot_pb->set_level(MetricLevelName(prototype_->level())); |
637 | 0 | snapshot_pb->set_max_trackable_value(snapshot.highest_trackable_value()); |
638 | 0 | snapshot_pb->set_num_significant_digits(snapshot.num_significant_digits()); |
639 | 0 | } |
640 | 4.92M | snapshot_pb->set_total_count(snapshot.TotalCount()); |
641 | 4.92M | snapshot_pb->set_total_sum(snapshot.TotalSum()); |
642 | 4.92M | snapshot_pb->set_min(snapshot.MinValue()); |
643 | 4.92M | snapshot_pb->set_mean(snapshot.MeanValue()); |
644 | 4.92M | snapshot_pb->set_percentile_75(snapshot.ValueAtPercentile(75)); |
645 | 4.92M | snapshot_pb->set_percentile_95(snapshot.ValueAtPercentile(95)); |
646 | 4.92M | snapshot_pb->set_percentile_99(snapshot.ValueAtPercentile(99)); |
647 | 4.92M | snapshot_pb->set_percentile_99_9(snapshot.ValueAtPercentile(99.9)); |
648 | 4.92M | snapshot_pb->set_percentile_99_99(snapshot.ValueAtPercentile(99.99)); |
649 | 4.92M | snapshot_pb->set_max(snapshot.MaxValue()); |
650 | | |
651 | 4.92M | if (opts.include_raw_histograms) { |
652 | 1 | RecordedValuesIterator iter(&snapshot); |
653 | 101 | while (iter.HasNext()) { |
654 | 100 | HistogramIterationValue value; |
655 | 100 | RETURN_NOT_OK(iter.Next(&value)); |
656 | 100 | snapshot_pb->add_values(value.value_iterated_to); |
657 | 100 | snapshot_pb->add_counts(value.count_at_value_iterated_to); |
658 | 100 | } |
659 | 1 | } |
660 | 4.92M | return Status::OK(); |
661 | 4.92M | } |
662 | | |
663 | 0 | uint64_t Histogram::CountInBucketForValueForTests(uint64_t value) const { |
664 | 0 | return histogram_->CountInBucketForValue(value); |
665 | 0 | } |
666 | | |
667 | 78.2k | uint64_t Histogram::TotalCount() const { |
668 | 78.2k | return histogram_->TotalCount(); |
669 | 78.2k | } |
670 | | |
671 | 2 | uint64_t Histogram::MinValueForTests() const { |
672 | 2 | return histogram_->MinValue(); |
673 | 2 | } |
674 | | |
675 | 3 | uint64_t Histogram::MaxValueForTests() const { |
676 | 3 | return histogram_->MaxValue(); |
677 | 3 | } |
678 | 1 | double Histogram::MeanValueForTests() const { |
679 | 1 | return histogram_->MeanValue(); |
680 | 1 | } |
681 | | |
682 | | ScopedLatencyMetric::ScopedLatencyMetric( |
683 | | const scoped_refptr<Histogram>& latency_hist, Auto automatic) |
684 | 27.9M | : latency_hist_(latency_hist), auto_(automatic) { |
685 | 27.9M | Restart(); |
686 | 27.9M | } |
687 | | |
688 | | ScopedLatencyMetric::ScopedLatencyMetric(ScopedLatencyMetric&& rhs) |
689 | | : latency_hist_(std::move(rhs.latency_hist_)), time_started_(rhs.time_started_), |
690 | 0 | auto_(rhs.auto_) { |
691 | 0 | } |
692 | | |
693 | 0 | void ScopedLatencyMetric::operator=(ScopedLatencyMetric&& rhs) { |
694 | 0 | if (auto_) { |
695 | 0 | Finish(); |
696 | 0 | } |
697 | |
|
698 | 0 | latency_hist_ = std::move(rhs.latency_hist_); |
699 | 0 | time_started_ = rhs.time_started_; |
700 | 0 | auto_ = rhs.auto_; |
701 | 0 | } |
702 | | |
703 | 27.9M | ScopedLatencyMetric::~ScopedLatencyMetric() { |
704 | 27.9M | if (auto_) { |
705 | 27.9M | Finish(); |
706 | 27.9M | } |
707 | 27.9M | } |
708 | | |
709 | 27.9M | void ScopedLatencyMetric::Restart() { |
710 | 27.9M | if (latency_hist_) { |
711 | 27.8M | time_started_ = MonoTime::Now(); |
712 | 27.8M | } |
713 | 27.9M | } |
714 | | |
715 | 27.9M | void ScopedLatencyMetric::Finish() { |
716 | 27.9M | if (latency_hist_ != nullptr) { |
717 | 27.8M | auto passed = (MonoTime::Now() - time_started_).ToMicroseconds(); |
718 | 27.8M | latency_hist_->Increment(passed); |
719 | 27.8M | } |
720 | 27.9M | } |
721 | | |
722 | | // Replace specific chars with underscore to pass PrometheusNameRegex(). |
723 | 2.74M | void EscapeMetricNameForPrometheus(std::string *id) { |
724 | 2.74M | std::replace(id->begin(), id->end(), ' ', '_'); |
725 | 2.74M | std::replace(id->begin(), id->end(), '.', '_'); |
726 | 2.74M | std::replace(id->begin(), id->end(), '-', '_'); |
727 | 2.74M | } |
728 | | |
729 | | } // namespace yb |