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  //------------------------------------------------------------------------------