github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/common/metrics/internal/namer/namer.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package namer
     8  
     9  import (
    10  	"fmt"
    11  	"regexp"
    12  	"strings"
    13  
    14  	"github.com/hechain20/hechain/common/metrics"
    15  )
    16  
    17  type Namer struct {
    18  	namespace  string
    19  	subsystem  string
    20  	name       string
    21  	nameFormat string
    22  	labelNames map[string]struct{}
    23  }
    24  
    25  func NewCounterNamer(c metrics.CounterOpts) *Namer {
    26  	return &Namer{
    27  		namespace:  c.Namespace,
    28  		subsystem:  c.Subsystem,
    29  		name:       c.Name,
    30  		nameFormat: c.StatsdFormat,
    31  		labelNames: sliceToSet(c.LabelNames),
    32  	}
    33  }
    34  
    35  func NewGaugeNamer(g metrics.GaugeOpts) *Namer {
    36  	return &Namer{
    37  		namespace:  g.Namespace,
    38  		subsystem:  g.Subsystem,
    39  		name:       g.Name,
    40  		nameFormat: g.StatsdFormat,
    41  		labelNames: sliceToSet(g.LabelNames),
    42  	}
    43  }
    44  
    45  func NewHistogramNamer(h metrics.HistogramOpts) *Namer {
    46  	return &Namer{
    47  		namespace:  h.Namespace,
    48  		subsystem:  h.Subsystem,
    49  		name:       h.Name,
    50  		nameFormat: h.StatsdFormat,
    51  		labelNames: sliceToSet(h.LabelNames),
    52  	}
    53  }
    54  
    55  func (n *Namer) validateKey(name string) {
    56  	if _, ok := n.labelNames[name]; !ok {
    57  		panic("invalid label name: " + name)
    58  	}
    59  }
    60  
    61  func (n *Namer) FullyQualifiedName() string {
    62  	switch {
    63  	case n.namespace != "" && n.subsystem != "":
    64  		return strings.Join([]string{n.namespace, n.subsystem, n.name}, ".")
    65  	case n.namespace != "":
    66  		return strings.Join([]string{n.namespace, n.name}, ".")
    67  	case n.subsystem != "":
    68  		return strings.Join([]string{n.subsystem, n.name}, ".")
    69  	default:
    70  		return n.name
    71  	}
    72  }
    73  
    74  func (n *Namer) labelsToMap(labelValues []string) map[string]string {
    75  	labels := map[string]string{}
    76  	for i := 0; i < len(labelValues); i += 2 {
    77  		key := labelValues[i]
    78  		n.validateKey(key)
    79  		if i == len(labelValues)-1 {
    80  			labels[key] = "unknown"
    81  		} else {
    82  			labels[key] = labelValues[i+1]
    83  		}
    84  	}
    85  	return labels
    86  }
    87  
    88  var (
    89  	formatRegexp            = regexp.MustCompile(`%{([#?[:alnum:]_]+)}`)
    90  	invalidLabelValueRegexp = regexp.MustCompile(`[.|:\s]`)
    91  )
    92  
    93  func (n *Namer) Format(labelValues ...string) string {
    94  	labels := n.labelsToMap(labelValues)
    95  
    96  	cursor := 0
    97  	var segments []string
    98  	// iterate over the regex groups and convert to formatters
    99  	matches := formatRegexp.FindAllStringSubmatchIndex(n.nameFormat, -1)
   100  	for _, m := range matches {
   101  		start, end := m[0], m[1]
   102  		labelStart, labelEnd := m[2], m[3]
   103  
   104  		if start > cursor {
   105  			segments = append(segments, n.nameFormat[cursor:start])
   106  		}
   107  
   108  		key := n.nameFormat[labelStart:labelEnd]
   109  		var value string
   110  		switch key {
   111  		case "#namespace":
   112  			value = n.namespace
   113  		case "#subsystem":
   114  			value = n.subsystem
   115  		case "#name":
   116  			value = n.name
   117  		case "#fqname":
   118  			value = n.FullyQualifiedName()
   119  		default:
   120  			var ok bool
   121  			value, ok = labels[key]
   122  			if !ok {
   123  				panic(fmt.Sprintf("invalid label in name format: %s", key))
   124  			}
   125  			value = invalidLabelValueRegexp.ReplaceAllString(value, "_")
   126  		}
   127  		segments = append(segments, value)
   128  
   129  		cursor = end
   130  	}
   131  
   132  	// handle any trailing suffix
   133  	if cursor != len(n.nameFormat) {
   134  		segments = append(segments, n.nameFormat[cursor:])
   135  	}
   136  
   137  	return strings.Join(segments, "")
   138  }
   139  
   140  func sliceToSet(set []string) map[string]struct{} {
   141  	labelSet := map[string]struct{}{}
   142  	for _, s := range set {
   143  		labelSet[s] = struct{}{}
   144  	}
   145  	return labelSet
   146  }