blob: c6532958e62e5faff6df4eb7e9982b9d618200c1 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// Date: 2014/09/22 11:57:43
#include <gflags/gflags.h>
#include "butil/unique_ptr.h"
#include "bvar/latency_recorder.h"
namespace bvar {
// Reloading following gflags does not change names of the corresponding bvars.
// Avoid reloading in practice.
DEFINE_int32(bvar_latency_p1, 80, "First latency percentile");
DEFINE_int32(bvar_latency_p2, 90, "Second latency percentile");
DEFINE_int32(bvar_latency_p3, 99, "Third latency percentile");
static bool valid_percentile(const char*, int32_t v) {
return v > 0 && v < 100;
}
const bool ALLOW_UNUSED dummy_bvar_latency_p1 = ::GFLAGS_NS::RegisterFlagValidator(
&FLAGS_bvar_latency_p1, valid_percentile);
const bool ALLOW_UNUSED dummy_bvar_latency_p2 = ::GFLAGS_NS::RegisterFlagValidator(
&FLAGS_bvar_latency_p2, valid_percentile);
const bool ALLOW_UNUSED dummy_bvar_latency_p3 = ::GFLAGS_NS::RegisterFlagValidator(
&FLAGS_bvar_latency_p3, valid_percentile);
namespace detail {
typedef PercentileSamples<1022> CombinedPercentileSamples;
CDF::CDF(PercentileWindow* w) : _w(w) {}
CDF::~CDF() {
hide();
}
void CDF::describe(std::ostream& os, bool) const {
os << "\"click to view\"";
}
int CDF::describe_series(
std::ostream& os, const SeriesOptions& options) const {
if (_w == NULL) {
return 1;
}
if (options.test_only) {
return 0;
}
std::unique_ptr<CombinedPercentileSamples> cb(new CombinedPercentileSamples);
std::vector<GlobalPercentileSamples> buckets;
_w->get_samples(&buckets);
for (size_t i = 0; i < buckets.size(); ++i) {
cb->combine_of(buckets.begin(), buckets.end());
}
std::pair<int, int> values[20];
size_t n = 0;
for (int i = 1; i < 10; ++i) {
values[n++] = std::make_pair(i*10, cb->get_number(i * 0.1));
}
for (int i = 91; i < 100; ++i) {
values[n++] = std::make_pair(i, cb->get_number(i * 0.01));
}
values[n++] = std::make_pair(100, cb->get_number(0.999));
values[n++] = std::make_pair(101, cb->get_number(0.9999));
CHECK_EQ(n, arraysize(values));
os << "{\"label\":\"cdf\",\"data\":[";
for (size_t i = 0; i < n; ++i) {
if (i) {
os << ',';
}
os << '[' << values[i].first << ',' << values[i].second << ']';
}
os << "]}";
return 0;
}
// Return random int value with expectation = `dval'
static int64_t double_to_random_int(double dval) {
int64_t ival = static_cast<int64_t>(dval);
if (dval > ival + butil::fast_rand_double()) {
ival += 1;
}
return ival;
}
static int64_t get_window_recorder_qps(void* arg) {
detail::Sample<Stat> s;
static_cast<RecorderWindow*>(arg)->get_span(&s);
// Use floating point to avoid overflow.
if (s.time_us <= 0) {
return 0;
}
return double_to_random_int(s.data.num * 1000000.0 / s.time_us);
}
static int64_t get_recorder_count(void* arg) {
return static_cast<IntRecorder*>(arg)->get_value().num;
}
// Caller is responsible for deleting the return value.
static CombinedPercentileSamples* combine(PercentileWindow* w) {
CombinedPercentileSamples* cb = new CombinedPercentileSamples;
std::vector<GlobalPercentileSamples> buckets;
w->get_samples(&buckets);
cb->combine_of(buckets.begin(), buckets.end());
return cb;
}
template <int64_t numerator, int64_t denominator>
static int64_t get_percetile(void* arg) {
return ((LatencyRecorder*)arg)->latency_percentile(
(double)numerator / double(denominator));
}
static int64_t get_p1(void* arg) {
LatencyRecorder* lr = static_cast<LatencyRecorder*>(arg);
return lr->latency_percentile(FLAGS_bvar_latency_p1 / 100.0);
}
static int64_t get_p2(void* arg) {
LatencyRecorder* lr = static_cast<LatencyRecorder*>(arg);
return lr->latency_percentile(FLAGS_bvar_latency_p2 / 100.0);
}
static int64_t get_p3(void* arg) {
LatencyRecorder* lr = static_cast<LatencyRecorder*>(arg);
return lr->latency_percentile(FLAGS_bvar_latency_p3 / 100.0);
}
static Vector<int64_t, 4> get_latencies(void *arg) {
std::unique_ptr<CombinedPercentileSamples> cb(
combine((PercentileWindow*)arg));
// NOTE: We don't show 99.99% since it's often significantly larger than
// other values and make other curves on the plotted graph small and
// hard to read.
Vector<int64_t, 4> result;
result[0] = cb->get_number(FLAGS_bvar_latency_p1 / 100.0);
result[1] = cb->get_number(FLAGS_bvar_latency_p2 / 100.0);
result[2] = cb->get_number(FLAGS_bvar_latency_p3 / 100.0);
result[3] = cb->get_number(0.999);
return result;
}
LatencyRecorderBase::LatencyRecorderBase(time_t window_size)
: _max_latency(0)
, _latency_window(&_latency, window_size)
, _max_latency_window(&_max_latency, window_size)
, _count(get_recorder_count, &_latency)
, _qps(get_window_recorder_qps, &_latency_window)
, _latency_percentile_window(&_latency_percentile, window_size)
, _latency_p1(get_p1, this)
, _latency_p2(get_p2, this)
, _latency_p3(get_p3, this)
, _latency_999(get_percetile<999, 1000>, this)
, _latency_9999(get_percetile<9999, 10000>, this)
, _latency_cdf(&_latency_percentile_window)
, _latency_percentiles(get_latencies, &_latency_percentile_window)
{}
} // namespace detail
Vector<int64_t, 4> LatencyRecorder::latency_percentiles() const {
// const_cast here is just to adapt parameter type and safe.
return detail::get_latencies(
const_cast<detail::PercentileWindow*>(&_latency_percentile_window));
}
int64_t LatencyRecorder::qps(time_t window_size) const {
detail::Sample<Stat> s;
_latency_window.get_span(window_size, &s);
// Use floating point to avoid overflow.
if (s.time_us <= 0) {
return 0;
}
return detail::double_to_random_int(s.data.num * 1000000.0 / s.time_us);
}
int LatencyRecorder::expose(const butil::StringPiece& prefix1,
const butil::StringPiece& prefix2) {
if (prefix2.empty()) {
LOG(ERROR) << "Parameter[prefix2] is empty";
return -1;
}
butil::StringPiece prefix = prefix2;
// User may add "_latency" as the suffix, remove it.
if (prefix.ends_with("latency") || prefix.ends_with("Latency")) {
prefix.remove_suffix(7);
if (prefix.empty()) {
LOG(ERROR) << "Invalid prefix2=" << prefix2;
return -1;
}
}
std::string tmp;
if (!prefix1.empty()) {
tmp.reserve(prefix1.size() + prefix.size() + 1);
tmp.append(prefix1.data(), prefix1.size());
tmp.push_back('_'); // prefix1 ending with _ is good.
tmp.append(prefix.data(), prefix.size());
prefix = tmp;
}
// set debug names for printing helpful error log.
_latency.set_debug_name(prefix);
_latency_percentile.set_debug_name(prefix);
if (_latency_window.expose_as(prefix, "latency") != 0) {
return -1;
}
if (_max_latency_window.expose_as(prefix, "max_latency") != 0) {
return -1;
}
if (_count.expose_as(prefix, "count") != 0) {
return -1;
}
if (_qps.expose_as(prefix, "qps") != 0) {
return -1;
}
char namebuf[32];
snprintf(namebuf, sizeof(namebuf), "latency_%d", (int)FLAGS_bvar_latency_p1);
if (_latency_p1.expose_as(prefix, namebuf, DISPLAY_ON_PLAIN_TEXT) != 0) {
return -1;
}
snprintf(namebuf, sizeof(namebuf), "latency_%d", (int)FLAGS_bvar_latency_p2);
if (_latency_p2.expose_as(prefix, namebuf, DISPLAY_ON_PLAIN_TEXT) != 0) {
return -1;
}
snprintf(namebuf, sizeof(namebuf), "latency_%u", (int)FLAGS_bvar_latency_p3);
if (_latency_p3.expose_as(prefix, namebuf, DISPLAY_ON_PLAIN_TEXT) != 0) {
return -1;
}
if (_latency_999.expose_as(prefix, "latency_999", DISPLAY_ON_PLAIN_TEXT) != 0) {
return -1;
}
if (_latency_9999.expose_as(prefix, "latency_9999") != 0) {
return -1;
}
if (_latency_cdf.expose_as(prefix, "latency_cdf", DISPLAY_ON_HTML) != 0) {
return -1;
}
if (_latency_percentiles.expose_as(prefix, "latency_percentiles", DISPLAY_ON_HTML) != 0) {
return -1;
}
snprintf(namebuf, sizeof(namebuf), "%d%%,%d%%,%d%%,99.9%%",
(int)FLAGS_bvar_latency_p1, (int)FLAGS_bvar_latency_p2,
(int)FLAGS_bvar_latency_p3);
CHECK_EQ(0, _latency_percentiles.set_vector_names(namebuf));
return 0;
}
int64_t LatencyRecorder::latency_percentile(double ratio) const {
std::unique_ptr<detail::CombinedPercentileSamples> cb(
combine((detail::PercentileWindow*)&_latency_percentile_window));
return cb->get_number(ratio);
}
void LatencyRecorder::hide() {
_latency_window.hide();
_max_latency_window.hide();
_count.hide();
_qps.hide();
_latency_p1.hide();
_latency_p2.hide();
_latency_p3.hide();
_latency_999.hide();
_latency_9999.hide();
_latency_cdf.hide();
_latency_percentiles.hide();
}
LatencyRecorder& LatencyRecorder::operator<<(int64_t latency) {
_latency << latency;
_max_latency << latency;
_latency_percentile << latency;
return *this;
}
std::ostream& operator<<(std::ostream& os, const LatencyRecorder& rec) {
return os << "{latency=" << rec.latency()
<< " max" << rec.window_size() << '=' << rec.max_latency()
<< " qps=" << rec.qps()
<< " count=" << rec.count() << '}';
}
} // namespace bvar