github.com/lzy4123/fabric@v2.1.1+incompatible/common/metrics/internal/namer/namer.go (about) 1 /* 2 Copyright IBM Corp. 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/hyperledger/fabric/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 formatRegexp = regexp.MustCompile(`%{([#?[:alnum:]_]+)}`) 89 var invalidLabelValueRegexp = regexp.MustCompile(`[.|:\s]`) 90 91 func (n *Namer) Format(labelValues ...string) string { 92 labels := n.labelsToMap(labelValues) 93 94 cursor := 0 95 var segments []string 96 // iterate over the regex groups and convert to formatters 97 matches := formatRegexp.FindAllStringSubmatchIndex(n.nameFormat, -1) 98 for _, m := range matches { 99 start, end := m[0], m[1] 100 labelStart, labelEnd := m[2], m[3] 101 102 if start > cursor { 103 segments = append(segments, n.nameFormat[cursor:start]) 104 } 105 106 key := n.nameFormat[labelStart:labelEnd] 107 var value string 108 switch key { 109 case "#namespace": 110 value = n.namespace 111 case "#subsystem": 112 value = n.subsystem 113 case "#name": 114 value = n.name 115 case "#fqname": 116 value = n.FullyQualifiedName() 117 default: 118 var ok bool 119 value, ok = labels[key] 120 if !ok { 121 panic(fmt.Sprintf("invalid label in name format: %s", key)) 122 } 123 value = invalidLabelValueRegexp.ReplaceAllString(value, "_") 124 } 125 segments = append(segments, value) 126 127 cursor = end 128 } 129 130 // handle any trailing suffix 131 if cursor != len(n.nameFormat) { 132 segments = append(segments, n.nameFormat[cursor:]) 133 } 134 135 return strings.Join(segments, "") 136 } 137 138 func sliceToSet(set []string) map[string]struct{} { 139 labelSet := map[string]struct{}{} 140 for _, s := range set { 141 labelSet[s] = struct{}{} 142 } 143 return labelSet 144 }