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
3478066
#define FINISH_STAT_DECL_(X) #X)),
78
826023
#define FINISH_STAT_DECL_MODE_(X, MODE) #X), Envoy::Stats::Gauge::ImportMode::MODE),
79
147081
#define FINISH_STAT_DECL_UNIT_(X, UNIT) #X), Envoy::Stats::Histogram::Unit::UNIT),
80

            
81
4453702
static inline std::string statPrefixJoin(absl::string_view prefix, absl::string_view token) {
82
4453702
  if (prefix.empty()) {
83
877488
    return std::string(token);
84
3593140
  } else if (absl::EndsWith(prefix, ".")) {
85
    // TODO(jmarantz): eliminate this case -- remove all the trailing dots from prefixes.
86
3259742
    return absl::StrCat(prefix, token);
87
3259742
  }
88
316472
  return absl::StrCat(prefix, ".", token);
89
4453702
}
90

            
91
3462961
#define POOL_COUNTER_PREFIX(POOL, PREFIX) (POOL).counterFromString(Envoy::statPrefixJoin(PREFIX, FINISH_STAT_DECL_
92
826023
#define POOL_GAUGE_PREFIX(POOL, PREFIX) (POOL).gaugeFromString(Envoy::statPrefixJoin(PREFIX, FINISH_STAT_DECL_MODE_
93
147081
#define POOL_HISTOGRAM_PREFIX(POOL, PREFIX) (POOL).histogramFromString(Envoy::statPrefixJoin(PREFIX, FINISH_STAT_DECL_UNIT_
94
15105
#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
34047539
#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
718160
#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
19593896
  , NAME##_(Envoy::Stats::Utility::counterFromStatNames(scope, {prefix, stat_names.NAME##_}))
123
#define MAKE_STATS_STRUCT_GAUGE_HELPER_(NAME, MODE)                                                \
124
3204053
  , NAME##_(Envoy::Stats::Utility::gaugeFromStatNames(scope, {prefix, stat_names.NAME##_},         \
125
3204053
                                                      Envoy::Stats::Gauge::ImportMode::MODE))
126
#define MAKE_STATS_STRUCT_HISTOGRAM_HELPER_(NAME, UNIT)                                            \
127
2565549
  , NAME##_(Envoy::Stats::Utility::histogramFromStatNames(scope, {prefix, stat_names.NAME##_},     \
128
2565549
                                                          Envoy::Stats::Histogram::Unit::UNIT))
129
#define MAKE_STATS_STRUCT_TEXT_READOUT_HELPER_(NAME)                                               \
130
2
  , 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
1
  struct StatNamesStruct {                                                                         \
142
1
    explicit StatNamesStruct(Envoy::Stats::SymbolTable& symbol_table)                              \
143
2418877
        : pool_(symbol_table)                                                                      \
144
1
              ALL_STATS(GENERATE_STAT_NAME_INIT, GENERATE_STAT_NAME_INIT, GENERATE_STAT_NAME_INIT, \
145
2418877
                        GENERATE_STAT_NAME_INIT, GENERATE_STAT_NAME_INIT) {}                       \
146
1
    Envoy::Stats::StatNamePool pool_;                                                              \
147
1
    ALL_STATS(GENERATE_STAT_NAME_STRUCT, GENERATE_STAT_NAME_STRUCT, GENERATE_STAT_NAME_STRUCT,     \
148
1
              GENERATE_STAT_NAME_STRUCT, GENERATE_STAT_NAME_STRUCT)                                \
149
1
  }
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
1
  struct StatsStruct {                                                                             \
161
1
    /* Also referenced in Stats::createDeferredCompatibleStats. */                                 \
162
1
    using StatNameType = StatNamesStruct;                                                          \
163
76
    static const absl::string_view typeName() { return #StatsStruct; }                             \
164
1
    StatsStruct(const StatNamesStruct& stat_names, Envoy::Stats::Scope& scope,                     \
165
1
                Envoy::Stats::StatName prefix = Envoy::Stats::StatName())                          \
166
1724438
        : stat_names_(stat_names)                                                                  \
167
1
              ALL_STATS(MAKE_STATS_STRUCT_COUNTER_HELPER_, MAKE_STATS_STRUCT_GAUGE_HELPER_,        \
168
1
                        MAKE_STATS_STRUCT_HISTOGRAM_HELPER_,                                       \
169
1
                        MAKE_STATS_STRUCT_TEXT_READOUT_HELPER_,                                    \
170
1724438
                        MAKE_STATS_STRUCT_STATNAME_HELPER_) {}                                     \
171
1
    const StatNameType& stat_names_;                                                               \
172
1
    ALL_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT, GENERATE_HISTOGRAM_STRUCT,           \
173
1
              GENERATE_TEXT_READOUT_STRUCT, GENERATE_STATNAME_STRUCT)                              \
174
1
  }
175
} // namespace Envoy