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 }