github.com/Jeffail/benthos/v3@v3.65.0/lib/metrics/whitelist.go (about) 1 package metrics 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "net/http" 8 "regexp" 9 "strings" 10 11 "github.com/Jeffail/benthos/v3/internal/docs" 12 "github.com/Jeffail/benthos/v3/lib/log" 13 ) 14 15 //------------------------------------------------------------------------------ 16 17 func init() { 18 Constructors[TypeWhiteList] = TypeSpec{ 19 constructor: NewWhitelist, 20 Status: docs.StatusDeprecated, 21 Summary: ` 22 Whitelist metric paths within Benthos so that only matching metric paths are 23 aggregated by a child metric target.`, 24 Description: ` 25 Whitelists can either be path prefixes or regular expression patterns, if either 26 a path prefix or regular expression matches a metric path it will be included. 27 28 Metrics must be matched using dot notation even if the chosen output uses a 29 different form. For example, the path would be 'foo.bar' rather than 'foo_bar' 30 even when sending metrics to Prometheus. A full list of metrics paths that 31 Benthos registers can be found in 32 [this list](/docs/components/metrics/about#paths).`, 33 FieldSpecs: docs.FieldSpecs{ 34 docs.FieldCommon("paths", `A list of path prefixes to include. This can be used, for example, to allow all of the child specific metrics paths from an output broker with the path `+"`output.broker`"+`.`).Array(), 35 docs.FieldCommon("patterns", `A list of RE2 regular expressions to include. This can be used, for example, to allow all of the latency based metrics with the pattern `+"`.*\\.latency`"+`.`).Array(), 36 docs.FieldCommon("child", "A child metric type, this is where whitelisted metrics will be routed.").HasType(docs.FieldTypeMetrics), 37 }, 38 Footnotes: ` 39 ## Debugging 40 41 In order to see logs breaking down which metrics are registered and whether they 42 pass your whitelists enable logging at the TRACE level.`, 43 } 44 } 45 46 //------------------------------------------------------------------------------ 47 48 // WhitelistConfig allows for the placement of filtering rules to only allow 49 // select metrics to be displayed or retrieved. It consists of a set of 50 // prefixes (direct string comparison) that are checked, and a standard 51 // metrics configuration that is wrapped by the whitelist. 52 type WhitelistConfig struct { 53 Paths []string `json:"paths" yaml:"paths"` 54 Patterns []string `json:"patterns" yaml:"patterns"` 55 Child *Config `json:"child" yaml:"child"` 56 } 57 58 // NewWhitelistConfig returns the default configuration for a whitelist 59 func NewWhitelistConfig() WhitelistConfig { 60 return WhitelistConfig{ 61 Paths: []string{}, 62 Patterns: []string{}, 63 Child: nil, 64 } 65 } 66 67 //------------------------------------------------------------------------------ 68 69 type dummyWhitelistConfig struct { 70 Paths []string `json:"paths" yaml:"paths"` 71 Patterns []string `json:"patterns" yaml:"patterns"` 72 Child interface{} `json:"child" yaml:"child"` 73 } 74 75 // MarshalJSON prints an empty object instead of nil. 76 func (w WhitelistConfig) MarshalJSON() ([]byte, error) { 77 dummy := dummyWhitelistConfig{ 78 Paths: w.Paths, 79 Patterns: w.Patterns, 80 Child: w.Child, 81 } 82 83 if w.Child == nil { 84 dummy.Child = struct{}{} 85 } 86 87 return json.Marshal(dummy) 88 } 89 90 // MarshalYAML prints an empty object instead of nil. 91 func (w WhitelistConfig) MarshalYAML() (interface{}, error) { 92 dummy := dummyWhitelistConfig{ 93 Paths: w.Paths, 94 Patterns: w.Patterns, 95 Child: w.Child, 96 } 97 if w.Child == nil { 98 dummy.Child = struct{}{} 99 } 100 return dummy, nil 101 } 102 103 //------------------------------------------------------------------------------ 104 105 // Whitelist is a statistics object that wraps a separate statistics object 106 // and only permits statistics that pass through the whitelist to be recorded. 107 type Whitelist struct { 108 paths []string 109 patterns []*regexp.Regexp 110 s Type 111 log log.Modular 112 } 113 114 // NewWhitelist creates and returns a new Whitelist object 115 func NewWhitelist(config Config, opts ...func(Type)) (Type, error) { 116 if config.Whitelist.Child == nil { 117 return nil, errors.New("cannot create a whitelist metric without a child") 118 } 119 120 child, err := New(*config.Whitelist.Child) 121 if err != nil { 122 return nil, err 123 } 124 125 w := &Whitelist{ 126 paths: config.Whitelist.Paths, 127 patterns: make([]*regexp.Regexp, len(config.Whitelist.Patterns)), 128 s: child, 129 log: log.Noop(), 130 } 131 132 for _, opt := range opts { 133 opt(w) 134 } 135 136 for i, p := range config.Whitelist.Patterns { 137 re, err := regexp.Compile(p) 138 if err != nil { 139 return nil, fmt.Errorf("invalid regular expression: '%s': %v", p, err) 140 } 141 w.patterns[i] = re 142 } 143 144 return w, nil 145 } 146 147 //------------------------------------------------------------------------------ 148 149 // allowPath checks whether or not a given path is in the allowed set of 150 // paths for the Whitelist metrics stat. 151 func (h *Whitelist) allowPath(path string) bool { 152 for _, p := range h.paths { 153 if strings.HasPrefix(path, p) { 154 h.log.Tracef("Allowing metric path '%v' as per whitelisted path prefix '%v'\n", path, p) 155 return true 156 } 157 } 158 for _, re := range h.patterns { 159 if re.MatchString(path) { 160 h.log.Tracef("Allowing metric path '%v' as per whitelisted pattern '%v'\n", path, re.String()) 161 return true 162 } 163 } 164 h.log.Tracef("Rejecting metric path '%v'\n", path) 165 return false 166 } 167 168 //------------------------------------------------------------------------------ 169 170 // GetCounter returns a stat counter object for a path. 171 func (h *Whitelist) GetCounter(path string) StatCounter { 172 if h.allowPath(path) { 173 return h.s.GetCounter(path) 174 } 175 return DudStat{} 176 } 177 178 // GetCounterVec returns a stat counter object for a path with the labels 179 // discarded. 180 func (h *Whitelist) GetCounterVec(path string, n []string) StatCounterVec { 181 if h.allowPath(path) { 182 return h.s.GetCounterVec(path, n) 183 } 184 return fakeCounterVec(func([]string) StatCounter { 185 return DudStat{} 186 }) 187 } 188 189 // GetTimer returns a stat timer object for a path. 190 func (h *Whitelist) GetTimer(path string) StatTimer { 191 if h.allowPath(path) { 192 return h.s.GetTimer(path) 193 } 194 return DudStat{} 195 } 196 197 // GetTimerVec returns a stat timer object for a path with the labels 198 // discarded. 199 func (h *Whitelist) GetTimerVec(path string, n []string) StatTimerVec { 200 if h.allowPath(path) { 201 return h.s.GetTimerVec(path, n) 202 } 203 return fakeTimerVec(func([]string) StatTimer { 204 return DudStat{} 205 }) 206 } 207 208 // GetGauge returns a stat gauge object for a path. 209 func (h *Whitelist) GetGauge(path string) StatGauge { 210 if h.allowPath(path) { 211 return h.s.GetGauge(path) 212 } 213 return DudStat{} 214 } 215 216 // GetGaugeVec returns a stat timer object for a path with the labels 217 // discarded. 218 func (h *Whitelist) GetGaugeVec(path string, n []string) StatGaugeVec { 219 if h.allowPath(path) { 220 return h.s.GetGaugeVec(path, n) 221 } 222 return fakeGaugeVec(func([]string) StatGauge { 223 return DudStat{} 224 }) 225 } 226 227 // SetLogger sets the logger used to print connection errors. 228 func (h *Whitelist) SetLogger(log log.Modular) { 229 h.log = log.NewModule(".whitelist") 230 h.s.SetLogger(log) 231 } 232 233 // Close stops the Statsd object from aggregating metrics and cleans up 234 // resources. 235 func (h *Whitelist) Close() error { 236 return h.s.Close() 237 } 238 239 //------------------------------------------------------------------------------ 240 241 // HandlerFunc returns an http.HandlerFunc for accessing metrics for appropriate 242 // child types 243 func (h *Whitelist) HandlerFunc() http.HandlerFunc { 244 if wHandlerFunc, ok := h.s.(WithHandlerFunc); ok { 245 return wHandlerFunc.HandlerFunc() 246 } 247 248 return func(w http.ResponseWriter, r *http.Request) { 249 w.WriteHeader(501) 250 w.Write([]byte("The child of this whitelist does not support HTTP metrics.")) 251 } 252 } 253 254 //------------------------------------------------------------------------------