github.com/Jeffail/benthos/v3@v3.65.0/internal/component/metrics/namespaced.go (about)

     1  package metrics
     2  
     3  import (
     4  	"sort"
     5  
     6  	"github.com/Jeffail/benthos/v3/lib/log"
     7  	"github.com/Jeffail/benthos/v3/lib/metrics"
     8  )
     9  
    10  // Namespaced wraps a child metrics exporter and exposes a metrics.Type API that
    11  // adds namespacing labels and name prefixes to new metrics.
    12  type Namespaced struct {
    13  	prefix   string
    14  	labels   map[string]string
    15  	mappings []*Mapping
    16  	child    metrics.Type
    17  }
    18  
    19  // NewNamespaced wraps a metrics exporter and adds prefixes and custom labels.
    20  func NewNamespaced(child metrics.Type) *Namespaced {
    21  	return &Namespaced{
    22  		child: child,
    23  	}
    24  }
    25  
    26  // WithStats returns a namespaced metrics exporter with a different stats
    27  // implementation.
    28  func (n *Namespaced) WithStats(s metrics.Type) *Namespaced {
    29  	newNs := *n
    30  	newNs.child = s
    31  	return &newNs
    32  }
    33  
    34  // WithPrefix returns a namespaced metrics exporter with a new prefix.
    35  func (n *Namespaced) WithPrefix(str string) *Namespaced {
    36  	newNs := *n
    37  	newNs.prefix = str
    38  	return &newNs
    39  }
    40  
    41  // WithLabels returns a namespaced metrics exporter with a new set of labels,
    42  // which are added to any prior labels.
    43  func (n *Namespaced) WithLabels(labels ...string) *Namespaced {
    44  	newLabels := map[string]string{}
    45  	for k, v := range n.labels {
    46  		newLabels[k] = v
    47  	}
    48  	for i := 0; i < len(labels)-1; i += 2 {
    49  		newLabels[labels[i]] = labels[i+1]
    50  	}
    51  	newNs := *n
    52  	newNs.labels = newLabels
    53  	return &newNs
    54  }
    55  
    56  // WithMapping returns a namespaced metrics exporter with a new mapping.
    57  // Mappings are applied _before_ the prefix and static labels are applied.
    58  // Mappings already added are executed after this new mapping.
    59  func (n *Namespaced) WithMapping(m *Mapping) *Namespaced {
    60  	newNs := *n
    61  	newMappings := make([]*Mapping, 0, len(n.mappings)+1)
    62  	newMappings = append(newMappings, m)
    63  	newMappings = append(newMappings, n.mappings...)
    64  	newNs.mappings = newMappings
    65  	return &newNs
    66  }
    67  
    68  // Unwrap to the underlying metrics type.
    69  func (n *Namespaced) Unwrap() metrics.Type {
    70  	u, ok := n.child.(interface {
    71  		Unwrap() metrics.Type
    72  	})
    73  	if ok {
    74  		return u.Unwrap()
    75  	}
    76  	return n.child
    77  }
    78  
    79  //------------------------------------------------------------------------------
    80  
    81  func (n *Namespaced) getPathAndLabels(path string) (newPath string, labelKeys, labelValues []string) {
    82  	newPath = path
    83  	// TODO: V4 Don't do this
    84  	if n.prefix != "" {
    85  		newPath = n.prefix + "." + path
    86  	}
    87  	if n.labels != nil && len(n.labels) > 0 {
    88  		labelKeys = make([]string, 0, len(n.labels))
    89  		for k := range n.labels {
    90  			labelKeys = append(labelKeys, k)
    91  		}
    92  		sort.Strings(labelKeys)
    93  		labelValues = make([]string, 0, len(n.labels))
    94  		for _, k := range labelKeys {
    95  			labelValues = append(labelValues, n.labels[k])
    96  		}
    97  	}
    98  	for _, mapping := range n.mappings {
    99  		if newPath, labelKeys, labelValues = mapping.mapPath(newPath, labelKeys, labelValues); newPath == "" {
   100  			return
   101  		}
   102  	}
   103  	return
   104  }
   105  
   106  type counterVecWithStatic struct {
   107  	staticValues []string
   108  	child        metrics.StatCounterVec
   109  }
   110  
   111  func (c *counterVecWithStatic) With(values ...string) metrics.StatCounter {
   112  	newValues := make([]string, 0, len(c.staticValues)+len(values))
   113  	newValues = append(newValues, c.staticValues...)
   114  	newValues = append(newValues, values...)
   115  	return c.child.With(newValues...)
   116  }
   117  
   118  type timerVecWithStatic struct {
   119  	staticValues []string
   120  	child        metrics.StatTimerVec
   121  }
   122  
   123  func (c *timerVecWithStatic) With(values ...string) metrics.StatTimer {
   124  	newValues := make([]string, 0, len(c.staticValues)+len(values))
   125  	newValues = append(newValues, c.staticValues...)
   126  	newValues = append(newValues, values...)
   127  	return c.child.With(newValues...)
   128  }
   129  
   130  type gaugeVecWithStatic struct {
   131  	staticValues []string
   132  	child        metrics.StatGaugeVec
   133  }
   134  
   135  func (c *gaugeVecWithStatic) With(values ...string) metrics.StatGauge {
   136  	newValues := make([]string, 0, len(c.staticValues)+len(values))
   137  	newValues = append(newValues, c.staticValues...)
   138  	newValues = append(newValues, values...)
   139  	return c.child.With(newValues...)
   140  }
   141  
   142  //------------------------------------------------------------------------------
   143  
   144  // GetCounter returns an editable counter stat for a given path.
   145  func (n *Namespaced) GetCounter(path string) metrics.StatCounter {
   146  	path, labelKeys, labelValues := n.getPathAndLabels(path)
   147  	if path == "" {
   148  		return metrics.DudStat{}
   149  	}
   150  	if len(labelKeys) > 0 {
   151  		return n.child.GetCounterVec(path, labelKeys).With(labelValues...)
   152  	}
   153  	return n.child.GetCounter(path)
   154  }
   155  
   156  // GetCounterVec returns an editable counter stat for a given path with labels,
   157  // these labels must be consistent with any other metrics registered on the same
   158  // path.
   159  func (n *Namespaced) GetCounterVec(path string, labelNames []string) metrics.StatCounterVec {
   160  	path, staticKeys, staticValues := n.getPathAndLabels(path)
   161  	if path == "" {
   162  		return fakeCounterVec(func([]string) metrics.StatCounter {
   163  			return metrics.DudStat{}
   164  		})
   165  	}
   166  	if len(staticKeys) > 0 {
   167  		newNames := make([]string, 0, len(staticKeys)+len(labelNames))
   168  		newNames = append(newNames, staticKeys...)
   169  		newNames = append(newNames, labelNames...)
   170  		return &counterVecWithStatic{
   171  			staticValues: staticValues,
   172  			child:        n.child.GetCounterVec(path, newNames),
   173  		}
   174  	}
   175  	return n.child.GetCounterVec(path, labelNames)
   176  }
   177  
   178  // GetTimer returns an editable timer stat for a given path.
   179  func (n *Namespaced) GetTimer(path string) metrics.StatTimer {
   180  	path, labelKeys, labelValues := n.getPathAndLabels(path)
   181  	if path == "" {
   182  		return metrics.DudStat{}
   183  	}
   184  	if len(labelKeys) > 0 {
   185  		return n.child.GetTimerVec(path, labelKeys).With(labelValues...)
   186  	}
   187  	return n.child.GetTimer(path)
   188  }
   189  
   190  // GetTimerVec returns an editable timer stat for a given path with labels,
   191  // these labels must be consistent with any other metrics registered on the same
   192  // path.
   193  func (n *Namespaced) GetTimerVec(path string, labelNames []string) metrics.StatTimerVec {
   194  	path, staticKeys, staticValues := n.getPathAndLabels(path)
   195  	if path == "" {
   196  		return fakeTimerVec(func([]string) metrics.StatTimer {
   197  			return metrics.DudStat{}
   198  		})
   199  	}
   200  	if len(staticKeys) > 0 {
   201  		newNames := make([]string, 0, len(staticKeys)+len(labelNames))
   202  		newNames = append(newNames, staticKeys...)
   203  		newNames = append(newNames, labelNames...)
   204  		return &timerVecWithStatic{
   205  			staticValues: staticValues,
   206  			child:        n.child.GetTimerVec(path, newNames),
   207  		}
   208  	}
   209  	return n.child.GetTimerVec(path, labelNames)
   210  }
   211  
   212  // GetGauge returns an editable gauge stat for a given path.
   213  func (n *Namespaced) GetGauge(path string) metrics.StatGauge {
   214  	path, labelKeys, labelValues := n.getPathAndLabels(path)
   215  	if path == "" {
   216  		return metrics.DudStat{}
   217  	}
   218  	if len(labelKeys) > 0 {
   219  		return n.child.GetGaugeVec(path, labelKeys).With(labelValues...)
   220  	}
   221  	return n.child.GetGauge(path)
   222  }
   223  
   224  // GetGaugeVec returns an editable gauge stat for a given path with labels,
   225  // these labels must be consistent with any other metrics registered on the same
   226  // path.
   227  func (n *Namespaced) GetGaugeVec(path string, labelNames []string) metrics.StatGaugeVec {
   228  	path, staticKeys, staticValues := n.getPathAndLabels(path)
   229  	if path == "" {
   230  		return fakeGaugeVec(func([]string) metrics.StatGauge {
   231  			return metrics.DudStat{}
   232  		})
   233  	}
   234  	if len(staticKeys) > 0 {
   235  		newNames := make([]string, 0, len(staticKeys)+len(labelNames))
   236  		newNames = append(newNames, staticKeys...)
   237  		newNames = append(newNames, labelNames...)
   238  		return &gaugeVecWithStatic{
   239  			staticValues: staticValues,
   240  			child:        n.child.GetGaugeVec(path, newNames),
   241  		}
   242  	}
   243  	return n.child.GetGaugeVec(path, labelNames)
   244  }
   245  
   246  // SetLogger sets the logging mechanism of the metrics type.
   247  func (n *Namespaced) SetLogger(log log.Modular) {
   248  	n.child.SetLogger(log)
   249  }
   250  
   251  // Close stops aggregating stats and cleans up resources.
   252  func (n *Namespaced) Close() error {
   253  	return n.child.Close()
   254  }