Line data Source code
1 : #pragma once
2 :
3 : #include <string>
4 :
5 : #include "envoy/stats/histogram.h"
6 : #include "envoy/stats/stats.h"
7 :
8 : #include "source/common/stats/symbol_table.h"
9 : #include "source/common/stats/utility.h"
10 :
11 : #include "absl/strings/match.h"
12 : #include "absl/strings/str_cat.h"
13 :
14 : namespace Envoy {
15 : /**
16 : * These are helper macros for allocating "fixed" stats throughout the code base in a way that
17 : * is also easy to mock and test. The general flow looks like this:
18 : *
19 : * Define a block of stats like this:
20 : * #define MY_COOL_STATS(COUNTER, GAUGE, HISTOGRAM) \
21 : * COUNTER(counter1) \
22 : * GAUGE(gauge1, mode) \
23 : * HISTOGRAM(histogram1, unit)
24 : * ...
25 : *
26 : * By convention, starting with #7083, we sort the lines of this macro block, so
27 : * all the counters are grouped together, then all the gauges, etc. We do not
28 : * use clang-format-on/off etc. "bazel run //tools/code_format:check_format -- fix" will take
29 : * care of lining up the backslashes.
30 : *
31 : * Now actually put these stats somewhere, usually as a member of a struct:
32 : * struct MyCoolStats {
33 : * MY_COOL_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT, GENERATE_HISTOGRAM_STRUCT)
34 : * };
35 : *
36 : * Finally, when you want to actually instantiate the above struct using a Stats::Pool, you do:
37 : * MyCoolStats stats{
38 : * MY_COOL_STATS(POOL_COUNTER(...), POOL_GAUGE(...), POOL_HISTOGRAM(...))};
39 : *
40 : *
41 : * The above constructs are the simplest way to declare counters, gauges,
42 : * histograms, and text-readouts in your data structures. However they incur
43 : * some overhead to symbolize the names every time they are instantiated. For
44 : * data structures that are re-instantiated extensively during operation,
45 : * e.g. in response to an xDS update, We can separately instantiate a
46 : * StatNameStruct, containing symbolized names for each stat. That should be
47 : * instantiated once at startup and held in some context or factory. Do that
48 : * with:
49 : *
50 : * MAKE_STAT_NAMES_STRUCT(MyStatNames, MY_COOL_STATS);
51 : *
52 : * This generates a structure definition with a constructor that requires a
53 : * SymbolTable. So you must, in a context instantiated once, initialize with:
54 : *
55 : * : my_cool_stat_names_(stat_store.symbolTable())
56 : *
57 : * Once you have the StatNamesStruct instance declared, you can create a stats
58 : * struct efficiently during operation (e.g. in an xDS handler) with
59 : *
60 : * MAKE_STATS_STRUCT(MyStats, MyStatNames, MY_COOL_STATS);
61 : *
62 : * This new structure is constructed with 2 or 3 args:
63 : * 1. The stat_names struct created from MAKE_STAT_NAMES_STRUCT
64 : * 2. The scope in which to instantiate the stats
65 : * 3. An optional prefix, which will be prepended to each stat name.
66 : * For example:
67 : *
68 : * : my_cool_stats_(context.my_cool_stat_names_, scope, opt_prefix)
69 : */
70 :
71 : // Fully-qualified for use in external callsites.
72 : #define GENERATE_COUNTER_STRUCT(NAME) Envoy::Stats::Counter& NAME##_;
73 : #define GENERATE_GAUGE_STRUCT(NAME, MODE) Envoy::Stats::Gauge& NAME##_;
74 : #define GENERATE_HISTOGRAM_STRUCT(NAME, UNIT) Envoy::Stats::Histogram& NAME##_;
75 : #define GENERATE_TEXT_READOUT_STRUCT(NAME) Envoy::Stats::TextReadout& NAME##_;
76 :
77 84762 : #define FINISH_STAT_DECL_(X) #X)),
78 21148 : #define FINISH_STAT_DECL_MODE_(X, MODE) #X), Envoy::Stats::Gauge::ImportMode::MODE),
79 892 : #define FINISH_STAT_DECL_UNIT_(X, UNIT) #X), Envoy::Stats::Histogram::Unit::UNIT),
80 :
81 125540 : static inline std::string statPrefixJoin(absl::string_view prefix, absl::string_view token) {
82 125540 : if (prefix.empty()) {
83 15033 : return std::string(token);
84 115319 : } else if (absl::EndsWith(prefix, ".")) {
85 : // TODO(jmarantz): eliminate this case -- remove all the trailing dots from prefixes.
86 89380 : return absl::StrCat(prefix, token);
87 89380 : }
88 21127 : return absl::StrCat(prefix, ".", token);
89 125540 : }
90 :
91 84528 : #define POOL_COUNTER_PREFIX(POOL, PREFIX) (POOL).counterFromString(Envoy::statPrefixJoin(PREFIX, FINISH_STAT_DECL_
92 21148 : #define POOL_GAUGE_PREFIX(POOL, PREFIX) (POOL).gaugeFromString(Envoy::statPrefixJoin(PREFIX, FINISH_STAT_DECL_MODE_
93 892 : #define POOL_HISTOGRAM_PREFIX(POOL, PREFIX) (POOL).histogramFromString(Envoy::statPrefixJoin(PREFIX, FINISH_STAT_DECL_UNIT_
94 234 : #define POOL_TEXT_READOUT_PREFIX(POOL, PREFIX) (POOL).textReadoutFromString(Envoy::statPrefixJoin(PREFIX, FINISH_STAT_DECL_
95 : #define POOL_STAT_NAME_PREFIX(POOL, PREFIX) (POOL).symbolTable().textReadoutFromString(Envoy::statPrefixJoin(PREFIX, FINISH_STAT_DECL_
96 :
97 : #define POOL_COUNTER(POOL) POOL_COUNTER_PREFIX(POOL, "")
98 : #define POOL_GAUGE(POOL) POOL_GAUGE_PREFIX(POOL, "")
99 : #define POOL_HISTOGRAM(POOL) POOL_HISTOGRAM_PREFIX(POOL, "")
100 : #define POOL_TEXT_READOUT(POOL) POOL_TEXT_READOUT_PREFIX(POOL, "")
101 :
102 : #define NULL_STAT_DECL_(X) std::string(#X)),
103 : #define NULL_STAT_DECL_IGNORE_MODE_(X, MODE) std::string(#X)),
104 :
105 : #define NULL_POOL_GAUGE(POOL) (POOL).nullGauge(NULL_STAT_DECL_IGNORE_MODE_
106 :
107 : // Used for declaring StatNames in a structure.
108 : #define GENERATE_STAT_NAME_STRUCT(NAME, ...) Envoy::Stats::StatName NAME##_;
109 : #define GENERATE_STAT_NAME_INIT(NAME, ...) , NAME##_(pool_.add(#NAME))
110 :
111 : // Used for defining constrcutors of stat objects
112 : #define GENERATE_CONSTRUCTOR_PARAM(NAME) Envoy::Stats::Counter &NAME,
113 : #define GENERATE_CONSTRUCTOR_COUNTER_PARAM(NAME) Envoy::Stats::Counter &NAME,
114 : #define GENERATE_CONSTRUCTOR_GAUGE_PARAM(NAME, ...) Envoy::Stats::Gauge &NAME,
115 : #define GENERATE_CONSTRUCTOR_INIT_LIST(NAME, ...) , NAME##_(NAME)
116 :
117 : // Macros for declaring stat-structures using StatNames, for those that must be
118 : // instantiated during operation, and where speed and scale matters. These
119 : // macros are not for direct use; they are only for use from
120 : // MAKE_STAT_NAMES_STRUCT. and MAKE_STAT_STRUCT.
121 : #define MAKE_STATS_STRUCT_COUNTER_HELPER_(NAME) \
122 : , NAME##_(Envoy::Stats::Utility::counterFromStatNames(scope, {prefix, stat_names.NAME##_}))
123 : #define MAKE_STATS_STRUCT_GAUGE_HELPER_(NAME, MODE) \
124 : , NAME##_(Envoy::Stats::Utility::gaugeFromStatNames(scope, {prefix, stat_names.NAME##_}, \
125 : Envoy::Stats::Gauge::ImportMode::MODE))
126 : #define MAKE_STATS_STRUCT_HISTOGRAM_HELPER_(NAME, UNIT) \
127 : , NAME##_(Envoy::Stats::Utility::histogramFromStatNames(scope, {prefix, stat_names.NAME##_}, \
128 : Envoy::Stats::Histogram::Unit::UNIT))
129 : #define MAKE_STATS_STRUCT_TEXT_READOUT_HELPER_(NAME) \
130 : , NAME##_(Envoy::Stats::Utility::textReadoutFromStatNames(scope, {prefix, stat_names.NAME##_}))
131 :
132 : #define MAKE_STATS_STRUCT_STATNAME_HELPER_(name)
133 : #define GENERATE_STATNAME_STRUCT(name)
134 :
135 : /**
136 : * Generates a struct with StatNames for a subsystem, based on the stats macro
137 : * with COUNTER, GAUGE, HISTOGRAM, TEXT_READOUT, and STATNAME calls. The
138 : * ALL_STATS macro must have all 5 parameters.
139 : */
140 : #define MAKE_STAT_NAMES_STRUCT(StatNamesStruct, ALL_STATS) \
141 : struct StatNamesStruct { \
142 : explicit StatNamesStruct(Envoy::Stats::SymbolTable& symbol_table) \
143 : : pool_(symbol_table) \
144 : ALL_STATS(GENERATE_STAT_NAME_INIT, GENERATE_STAT_NAME_INIT, GENERATE_STAT_NAME_INIT, \
145 22157 : GENERATE_STAT_NAME_INIT, GENERATE_STAT_NAME_INIT) {} \
146 : Envoy::Stats::StatNamePool pool_; \
147 : ALL_STATS(GENERATE_STAT_NAME_STRUCT, GENERATE_STAT_NAME_STRUCT, GENERATE_STAT_NAME_STRUCT, \
148 : GENERATE_STAT_NAME_STRUCT, GENERATE_STAT_NAME_STRUCT) \
149 : }
150 :
151 : /**
152 : * Instantiates a structure of stats based on a new scope and optional prefix,
153 : * using a predefined structure of stat names. A reference to the stat_names is
154 : * also stored in the structure, for two reasons: (a) as a syntactic convenience
155 : * for using macros to generate the comma separators for the initializer and (b)
156 : * as a convenience at the call-site to access STATNAME-declared names from the
157 : * stats structure.
158 : */
159 : #define MAKE_STATS_STRUCT(StatsStruct, StatNamesStruct, ALL_STATS) \
160 : struct StatsStruct { \
161 : /* Also referenced in Stats::createDeferredCompatibleStats. */ \
162 : using StatNameType = StatNamesStruct; \
163 0 : static const absl::string_view typeName() { return #StatsStruct; } \
164 : StatsStruct(const StatNamesStruct& stat_names, Envoy::Stats::Scope& scope, \
165 : Envoy::Stats::StatName prefix = Envoy::Stats::StatName()) \
166 : : stat_names_(stat_names) \
167 : ALL_STATS(MAKE_STATS_STRUCT_COUNTER_HELPER_, MAKE_STATS_STRUCT_GAUGE_HELPER_, \
168 : MAKE_STATS_STRUCT_HISTOGRAM_HELPER_, \
169 : MAKE_STATS_STRUCT_TEXT_READOUT_HELPER_, \
170 17111 : MAKE_STATS_STRUCT_STATNAME_HELPER_) {} \
171 : const StatNameType& stat_names_; \
172 : ALL_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT, GENERATE_HISTOGRAM_STRUCT, \
173 : GENERATE_TEXT_READOUT_STRUCT, GENERATE_STATNAME_STRUCT) \
174 : }
175 : } // namespace Envoy
|