Skip to content

Commit 9a1e49f

Browse files
ramaraochavalimattklein123
authored andcommitted
stats: add built-in log linear histogram support (#3130)
Signed-off-by: Rama <[email protected]>
1 parent fefb0d3 commit 9a1e49f

File tree

31 files changed

+1039
-77
lines changed

31 files changed

+1039
-77
lines changed

bazel/external/libcircllhist.BUILD

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
cc_library(
2+
name = "libcircllhist",
3+
srcs = ["src/circllhist.c"],
4+
hdrs = [
5+
"src/circllhist.h",
6+
],
7+
includes = ["src"],
8+
visibility = ["//visibility:public"],
9+
)

bazel/repositories.bzl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ def envoy_dependencies(path = "@envoy_deps//", skip_targets = []):
226226
_boringssl()
227227
_com_google_absl()
228228
_com_github_bombela_backward()
229+
_com_github_circonus_labs_libcircllhist()
229230
_com_github_cyan4973_xxhash()
230231
_com_github_eile_tclap()
231232
_com_github_fmtlib_fmt()
@@ -265,6 +266,16 @@ def _com_github_bombela_backward():
265266
actual = "@com_github_bombela_backward//:backward",
266267
)
267268

269+
def _com_github_circonus_labs_libcircllhist():
270+
_repository_impl(
271+
name = "com_github_circonus_labs_libcircllhist",
272+
build_file = "@envoy//bazel/external:libcircllhist.BUILD",
273+
)
274+
native.bind(
275+
name = "libcircllhist",
276+
actual = "@com_github_circonus_labs_libcircllhist//:libcircllhist",
277+
)
278+
268279
def _com_github_cyan4973_xxhash():
269280
_repository_impl(
270281
name = "com_github_cyan4973_xxhash",

bazel/repository_locations.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ REPOSITORY_LOCATIONS = dict(
1212
commit = "44ae9609e860e3428cd057f7052e505b4819eb84", # 2018-02-06
1313
remote = "https://github.com/bombela/backward-cpp",
1414
),
15+
com_github_circonus_labs_libcircllhist = dict(
16+
commit = "97ef5e088fd01fa8ec5a86334a6308ac0d51ea6f", # 2018-04-07
17+
remote = "https://github.com/circonus-labs/libcircllhist",
18+
),
1519
com_github_cyan4973_xxhash = dict(
1620
commit = "7caf8bd76440c75dfe1070d3acfbd7891aea8fca", # v0.6.4
1721
remote = "https://github.com/Cyan4973/xxHash",

docs/root/intro/version_history.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Version history
4444
:ref:`cluster specific <envoy_api_field_Cluster.upstream_bind_config>` options.
4545
* sockets: added `IP_TRANSPARENT` socket option support for :ref:`listeners
4646
<envoy_api_field_Listener.transparent>`.
47+
* stats: added support for histograms.
4748
* tracing: the sampling decision is now delegated to the tracers, allowing the tracer to decide when and if
4849
to use it. For example, if the :ref:`x-b3-sampled <config_http_conn_man_headers_x-b3-sampled>` header
4950
is supplied with the client request, its value will override any sampling decision made by the Envoy proxy.

docs/root/operations/admin.rst

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,12 @@ The fields are:
181181

182182
.. http:get:: /stats
183183
184-
Outputs all statistics on demand. This includes only counters and gauges. Histograms are not
185-
output as Envoy currently has no built in histogram support and relies on statsd for
186-
aggregation. This command is very useful for local debugging. See :ref:`here <operations_stats>`
187-
for more information.
184+
Outputs all statistics on demand. This command is very useful for local debugging.
185+
Histograms will output the computed quantiles i.e P0,P25,P50,P75,P90,P99,P99.9 and P100.
186+
The output for each quantile will be in the form of (inteval,cumulative) where interval value
187+
represents the summary since last flush interval and cumulative value represents the
188+
summary since the start of envoy instance.
189+
See :ref:`here <operations_stats>` for more information.
188190

189191
.. http:get:: /stats?format=json
190192

include/envoy/stats/stats.h

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <chrono>
44
#include <cstdint>
5+
#include <functional>
56
#include <list>
67
#include <memory>
78
#include <string>
@@ -114,6 +115,11 @@ class Metric {
114115
* Returns the name of the Metric with the portions designated as tags removed.
115116
*/
116117
virtual const std::string& tagExtractedName() const PURE;
118+
119+
/**
120+
* Indicates whether this metric has been updated since the server was started.
121+
*/
122+
virtual bool used() const PURE;
117123
};
118124

119125
/**
@@ -128,7 +134,6 @@ class Counter : public virtual Metric {
128134
virtual void inc() PURE;
129135
virtual uint64_t latch() PURE;
130136
virtual void reset() PURE;
131-
virtual bool used() const PURE;
132137
virtual uint64_t value() const PURE;
133138
};
134139

@@ -146,12 +151,34 @@ class Gauge : public virtual Metric {
146151
virtual void inc() PURE;
147152
virtual void set(uint64_t value) PURE;
148153
virtual void sub(uint64_t amount) PURE;
149-
virtual bool used() const PURE;
150154
virtual uint64_t value() const PURE;
151155
};
152156

153157
typedef std::shared_ptr<Gauge> GaugeSharedPtr;
154158

159+
/**
160+
* Holds the computed statistics for a histogram.
161+
*/
162+
class HistogramStatistics {
163+
public:
164+
virtual ~HistogramStatistics() {}
165+
166+
/**
167+
* Returns summary representation of the histogram.
168+
*/
169+
virtual std::string summary() const PURE;
170+
171+
/**
172+
* Returns supported quantiles.
173+
*/
174+
virtual const std::vector<double>& supportedQuantiles() const PURE;
175+
176+
/**
177+
* Returns computed quantile values during the period.
178+
*/
179+
virtual const std::vector<double>& computedQuantiles() const PURE;
180+
};
181+
155182
/**
156183
* A histogram that records values one at a time.
157184
* Note: Histograms now incorporate what used to be timers because the only difference between the
@@ -171,6 +198,32 @@ class Histogram : public virtual Metric {
171198

172199
typedef std::shared_ptr<Histogram> HistogramSharedPtr;
173200

201+
/**
202+
* A histogram that is stored in main thread and provides summary view of the histogram.
203+
*/
204+
class ParentHistogram : public virtual Histogram {
205+
public:
206+
virtual ~ParentHistogram() {}
207+
208+
/**
209+
* This method is called during the main stats flush process for each of the histograms and used
210+
* to merge the histogram values.
211+
*/
212+
virtual void merge() PURE;
213+
214+
/**
215+
* Returns the interval histogram summary statistics for the flush interval.
216+
*/
217+
virtual const HistogramStatistics& intervalStatistics() const PURE;
218+
219+
/**
220+
* Returns the cumulative histogram summary statistics.
221+
*/
222+
virtual const HistogramStatistics& cumulativeStatistics() const PURE;
223+
};
224+
225+
typedef std::shared_ptr<ParentHistogram> ParentHistogramSharedPtr;
226+
174227
/**
175228
* A sink for stats. Each sink is responsible for writing stats to a backing store.
176229
*/
@@ -194,6 +247,11 @@ class Sink {
194247
*/
195248
virtual void flushGauge(const Gauge& gauge, uint64_t value) PURE;
196249

250+
/**
251+
* Flush a histogram.
252+
*/
253+
virtual void flushHistogram(const ParentHistogram& histogram) PURE;
254+
197255
/**
198256
* This will be called after beginFlush(), some number of flushCounter(), and some number of
199257
* flushGauge(). Sinks can use this to optimize writing if desired.
@@ -263,10 +321,20 @@ class Store : public Scope {
263321
* @return a list of all known gauges.
264322
*/
265323
virtual std::list<GaugeSharedPtr> gauges() const PURE;
324+
325+
/**
326+
* @return a list of all known histograms.
327+
*/
328+
virtual std::list<ParentHistogramSharedPtr> histograms() const PURE;
266329
};
267330

268331
typedef std::unique_ptr<Store> StorePtr;
269332

333+
/**
334+
* Callback invoked when a store's mergeHistogram() runs.
335+
*/
336+
typedef std::function<void()> PostMergeCb;
337+
270338
/**
271339
* The root of the stat store.
272340
*/
@@ -294,6 +362,15 @@ class StoreRoot : public Store {
294362
* down.
295363
*/
296364
virtual void shutdownThreading() PURE;
365+
366+
/**
367+
* Called during the flush process to merge all the thread local histograms. The passed in
368+
* callback will be called on the main thread, but it will happen after the method returns
369+
* which means that the actual flush process will happen on the main thread after this method
370+
* returns. It is expected that only one merge runs at any time and concurrent calls to this
371+
* method would be asserted.
372+
*/
373+
virtual void mergeHistograms(PostMergeCb merge_complete_cb) PURE;
297374
};
298375

299376
typedef std::unique_ptr<StoreRoot> StoreRootPtr;

include/envoy/thread_local/thread_local.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ class Slot {
4646
*/
4747
virtual void runOnAllThreads(Event::PostCb cb) PURE;
4848

49+
/**
50+
* Run a callback on all registered threads with a barrier. A shutdown initiated during the
51+
* running of the PostCBs may prevent all_threads_complete_cb from being called.
52+
* @param cb supplies the callback to run on each thread.
53+
* @param all_threads_complete_cb supplies the callback to run on main thread after cb has
54+
* been run on all registered threads.
55+
*/
56+
virtual void runOnAllThreads(Event::PostCb cb, Event::PostCb all_threads_complete_cb) PURE;
57+
4958
/**
5059
* Set thread local data on all threads previously registered via registerThread().
5160
* @param initializeCb supplies the functor that will be called *on each thread*. The functor

source/common/common/logger.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ namespace Logger {
4343
FUNCTION(testing) \
4444
FUNCTION(tracing) \
4545
FUNCTION(upstream) \
46-
FUNCTION(grpc)
46+
FUNCTION(grpc) \
47+
FUNCTION(stats)
48+
4749

4850
enum class Id {
4951
ALL_LOGGER_IDS(GENERATE_ENUM)

source/common/stats/BUILD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,17 @@ envoy_cc_library(
1212
name = "stats_lib",
1313
srcs = ["stats_impl.cc"],
1414
hdrs = ["stats_impl.h"],
15+
external_deps = [
16+
"abseil_optional",
17+
"libcircllhist",
18+
],
1519
deps = [
1620
"//include/envoy/common:time_interface",
1721
"//include/envoy/server:options_interface",
1822
"//include/envoy/stats:stats_interface",
1923
"//source/common/common:assert_lib",
2024
"//source/common/common:hash_lib",
25+
"//source/common/common:non_copyable",
2126
"//source/common/common:perf_annotation_lib",
2227
"//source/common/common:utility_lib",
2328
"//source/common/config:well_known_names",

source/common/stats/stats_impl.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,5 +273,38 @@ void RawStatData::initialize(absl::string_view key) {
273273
name_[xfer_size] = '\0';
274274
}
275275

276+
HistogramStatisticsImpl::HistogramStatisticsImpl(const histogram_t* histogram_ptr)
277+
: computed_quantiles_(supportedQuantiles().size(), 0.0) {
278+
hist_approx_quantile(histogram_ptr, supportedQuantiles().data(), supportedQuantiles().size(),
279+
computed_quantiles_.data());
280+
}
281+
282+
const std::vector<double>& HistogramStatisticsImpl::supportedQuantiles() const {
283+
static const std::vector<double> supported_quantiles = {0, 0.25, 0.5, 0.75, 0.90,
284+
0.95, 0.99, 0.999, 1};
285+
return supported_quantiles;
286+
}
287+
288+
std::string HistogramStatisticsImpl::summary() const {
289+
std::vector<std::string> summary;
290+
const std::vector<double>& supported_quantiles_ref = supportedQuantiles();
291+
summary.reserve(supported_quantiles_ref.size());
292+
for (size_t i = 0; i < supported_quantiles_ref.size(); ++i) {
293+
summary.push_back(
294+
fmt::format("P{}: {}", 100 * supported_quantiles_ref[i], computed_quantiles_[i]));
295+
}
296+
return absl::StrJoin(summary, ", ");
297+
}
298+
299+
/**
300+
* Clears the old computed values and refreshes it with values computed from passed histogram.
301+
*/
302+
void HistogramStatisticsImpl::refresh(const histogram_t* new_histogram_ptr) {
303+
std::fill(computed_quantiles_.begin(), computed_quantiles_.end(), 0.0);
304+
ASSERT(supportedQuantiles().size() == computed_quantiles_.size());
305+
hist_approx_quantile(new_histogram_ptr, supportedQuantiles().data(), supportedQuantiles().size(),
306+
computed_quantiles_.data());
307+
}
308+
276309
} // namespace Stats
277310
} // namespace Envoy

0 commit comments

Comments
 (0)