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
395061
    : supported_buckets_(supported_buckets),
25
395061
      computed_quantiles_(HistogramStatisticsImpl::supportedQuantiles().size(), 0.0), unit_(unit) {
26
395061
  refresh(histogram_ptr);
27
395061
}
28

            
29
1210693
const std::vector<double>& HistogramStatisticsImpl::supportedQuantiles() const {
30
1210693
  CONSTRUCT_ON_FIRST_USE(std::vector<double>,
31
1210693
                         {0, 0.25, 0.5, 0.75, 0.90, 0.95, 0.99, 0.995, 0.999, 1});
32
1210693
}
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
407527
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
407527
  constexpr double percent_scale = Histogram::PercentScale;
73

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

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

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

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

            
103
HistogramSettingsImpl::HistogramSettingsImpl(const envoy::config::metrics::v3::StatsConfig& config,
104
                                             Server::Configuration::CommonFactoryContext& context)
105
10679
    : configs_([&config, &context]() {
106
10679
        std::vector<Config> configs;
107
10682
        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
10679
        return configs;
118
10679
      }()) {}
119

            
120
199825
const ConstSupportedBuckets& HistogramSettingsImpl::buckets(absl::string_view stat_name) const {
121
199828
  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
199787
  return defaultBuckets();
127
199825
}
128

            
129
199820
absl::optional<uint32_t> HistogramSettingsImpl::bins(absl::string_view stat_name) const {
130
199822
  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
199816
  return {};
136
199820
}
137

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

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