1
#include "source/common/stats/histogram_impl.h"
2

            
3
#include <algorithm>
4
#include <string>
5

            
6
#include "source/common/common/utility.h"
7
#include "source/common/protobuf/utility.h"
8

            
9
#include "absl/strings/str_join.h"
10

            
11
namespace Envoy {
12
namespace Stats {
13

            
14
namespace {
15
const ConstSupportedBuckets default_buckets{};
16
}
17

            
18
HistogramStatisticsImpl::HistogramStatisticsImpl()
19
58
    : supported_buckets_(default_buckets), computed_quantiles_(supportedQuantiles().size(), 0.0) {}
20

            
21
HistogramStatisticsImpl::HistogramStatisticsImpl(const histogram_t* histogram_ptr,
22
                                                 Histogram::Unit unit,
23
                                                 ConstSupportedBuckets& supported_buckets)
24
394609
    : supported_buckets_(supported_buckets),
25
394609
      computed_quantiles_(HistogramStatisticsImpl::supportedQuantiles().size(), 0.0), unit_(unit) {
26
394609
  refresh(histogram_ptr);
27
394609
}
28

            
29
1210286
const std::vector<double>& HistogramStatisticsImpl::supportedQuantiles() const {
30
1210286
  CONSTRUCT_ON_FIRST_USE(std::vector<double>,
31
1210286
                         {0, 0.25, 0.5, 0.75, 0.90, 0.95, 0.99, 0.995, 0.999, 1});
32
1210286
}
33

            
34
104
std::vector<uint64_t> HistogramStatisticsImpl::computeDisjointBuckets() const {
35
104
  std::vector<uint64_t> buckets;
36
104
  buckets.reserve(computed_buckets_.size());
37
104
  uint64_t previous_computed_bucket = 0;
38
1856
  for (uint64_t computed_bucket : computed_buckets_) {
39
1856
    buckets.push_back(computed_bucket - previous_computed_bucket);
40
1856
    previous_computed_bucket = computed_bucket;
41
1856
  }
42
104
  return buckets;
43
104
}
44

            
45
95
std::string HistogramStatisticsImpl::quantileSummary() const {
46
95
  std::vector<std::string> summary;
47
95
  const std::vector<double>& supported_quantiles = supportedQuantiles();
48
95
  summary.reserve(supported_quantiles.size());
49
1045
  for (size_t i = 0; i < supported_quantiles.size(); ++i) {
50
950
    summary.push_back(
51
950
        fmt::format("P{:g}: {:g}", 100 * supported_quantiles[i], computed_quantiles_[i]));
52
950
  }
53
95
  return absl::StrJoin(summary, ", ");
54
95
}
55

            
56
101
std::string HistogramStatisticsImpl::bucketSummary() const {
57
101
  std::vector<std::string> bucket_summary;
58
101
  ConstSupportedBuckets& supported_buckets = supportedBuckets();
59
101
  bucket_summary.reserve(supported_buckets.size());
60
2020
  for (size_t i = 0; i < supported_buckets.size(); ++i) {
61
1919
    bucket_summary.push_back(fmt::format("B{:g}: {}", supported_buckets[i], computed_buckets_[i]));
62
1919
  }
63
101
  return absl::StrJoin(bucket_summary, ", ");
64
101
}
65

            
66
/**
67
 * Clears the old computed values and refreshes it with values computed from passed histogram.
68
 */
69
407287
void HistogramStatisticsImpl::refresh(const histogram_t* new_histogram_ptr) {
70
  // Convert to double once to avoid needing to cast it on every use. Use a double
71
  // to ensure the compiler doesn't try to convert the expression to integer math.
72
407287
  constexpr double percent_scale = Histogram::PercentScale;
73

            
74
407287
  std::fill(computed_quantiles_.begin(), computed_quantiles_.end(), 0.0);
75
407287
  ASSERT(supportedQuantiles().size() == computed_quantiles_.size());
76
407287
  hist_approx_quantile(new_histogram_ptr, supportedQuantiles().data(), supportedQuantiles().size(),
77
407287
                       computed_quantiles_.data());
78
407287
  if (unit_ == Histogram::Unit::Percent) {
79
280
    for (double& val : computed_quantiles_) {
80
280
      val /= percent_scale;
81
280
    }
82
28
  }
83

            
84
407287
  sample_count_ = hist_sample_count(new_histogram_ptr);
85
407287
  sample_sum_ = hist_approx_sum(new_histogram_ptr);
86
407287
  if (unit_ == Histogram::Unit::Percent) {
87
28
    sample_sum_ /= percent_scale;
88
28
  }
89

            
90
407287
  computed_buckets_.clear();
91
407287
  ConstSupportedBuckets& supported_buckets = supportedBuckets();
92
407287
  computed_buckets_.reserve(supported_buckets.size());
93
7737120
  for (auto bucket : supported_buckets) {
94
7737120
    if (unit_ == Histogram::Unit::Percent) {
95
446
      bucket *= percent_scale;
96
446
    }
97
7737120
    computed_buckets_.emplace_back(hist_approx_count_below(new_histogram_ptr, bucket));
98
7737120
  }
99

            
100
407287
  out_of_bound_count_ = hist_approx_count_above(new_histogram_ptr, supported_buckets.back());
101
407287
}
102

            
103
HistogramSettingsImpl::HistogramSettingsImpl(const envoy::config::metrics::v3::StatsConfig& config,
104
                                             Server::Configuration::CommonFactoryContext& context)
105
10663
    : configs_([&config, &context]() {
106
10663
        std::vector<Config> configs;
107
10666
        for (const auto& matcher : config.histogram_bucket_settings()) {
108
38
          std::vector<double> buckets{matcher.buckets().begin(), matcher.buckets().end()};
109
38
          std::sort(buckets.begin(), buckets.end());
110
38
          configs.emplace_back(Matchers::StringMatcherImpl(matcher.match(), context),
111
38
                               buckets.empty()
112
38
                                   ? absl::nullopt
113
38
                                   : absl::make_optional<ConstSupportedBuckets>(std::move(buckets)),
114
38
                               PROTOBUF_GET_OPTIONAL_WRAPPED(matcher, bins));
115
38
        }
116

            
117
10663
        return configs;
118
10663
      }()) {}
119

            
120
199551
const ConstSupportedBuckets& HistogramSettingsImpl::buckets(absl::string_view stat_name) const {
121
199554
  for (const auto& config : configs_) {
122
421
    if (config.matcher_.match(stat_name) && config.buckets_.has_value()) {
123
38
      return config.buckets_.value();
124
38
    }
125
421
  }
126
199513
  return defaultBuckets();
127
199551
}
128

            
129
199546
absl::optional<uint32_t> HistogramSettingsImpl::bins(absl::string_view stat_name) const {
130
199548
  for (const auto& config : configs_) {
131
415
    if (config.matcher_.match(stat_name) && config.bins_.has_value()) {
132
4
      return config.bins_;
133
4
    }
134
415
  }
135
199542
  return {};
136
199546
}
137

            
138
199610
const ConstSupportedBuckets& HistogramSettingsImpl::defaultBuckets() {
139
199610
  CONSTRUCT_ON_FIRST_USE(ConstSupportedBuckets,
140
199610
                         {0.5, 1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 30000,
141
199610
                          60000, 300000, 600000, 1800000, 3600000});
142
199610
}
143

            
144
} // namespace Stats
145
} // namespace Envoy