go_study/fabric-main/common/metrics/internal/namer/namer.go

147 lines
3.1 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package namer
import (
"fmt"
"regexp"
"strings"
"github.com/hyperledger/fabric/common/metrics"
)
type Namer struct {
namespace string
subsystem string
name string
nameFormat string
labelNames map[string]struct{}
}
func NewCounterNamer(c metrics.CounterOpts) *Namer {
return &Namer{
namespace: c.Namespace,
subsystem: c.Subsystem,
name: c.Name,
nameFormat: c.StatsdFormat,
labelNames: sliceToSet(c.LabelNames),
}
}
func NewGaugeNamer(g metrics.GaugeOpts) *Namer {
return &Namer{
namespace: g.Namespace,
subsystem: g.Subsystem,
name: g.Name,
nameFormat: g.StatsdFormat,
labelNames: sliceToSet(g.LabelNames),
}
}
func NewHistogramNamer(h metrics.HistogramOpts) *Namer {
return &Namer{
namespace: h.Namespace,
subsystem: h.Subsystem,
name: h.Name,
nameFormat: h.StatsdFormat,
labelNames: sliceToSet(h.LabelNames),
}
}
func (n *Namer) validateKey(name string) {
if _, ok := n.labelNames[name]; !ok {
panic("invalid label name: " + name)
}
}
func (n *Namer) FullyQualifiedName() string {
switch {
case n.namespace != "" && n.subsystem != "":
return strings.Join([]string{n.namespace, n.subsystem, n.name}, ".")
case n.namespace != "":
return strings.Join([]string{n.namespace, n.name}, ".")
case n.subsystem != "":
return strings.Join([]string{n.subsystem, n.name}, ".")
default:
return n.name
}
}
func (n *Namer) labelsToMap(labelValues []string) map[string]string {
labels := map[string]string{}
for i := 0; i < len(labelValues); i += 2 {
key := labelValues[i]
n.validateKey(key)
if i == len(labelValues)-1 {
labels[key] = "unknown"
} else {
labels[key] = labelValues[i+1]
}
}
return labels
}
var (
formatRegexp = regexp.MustCompile(`%{([#?[:alnum:]_]+)}`)
invalidLabelValueRegexp = regexp.MustCompile(`[.|:\s]`)
)
func (n *Namer) Format(labelValues ...string) string {
labels := n.labelsToMap(labelValues)
cursor := 0
var segments []string
// iterate over the regex groups and convert to formatters
matches := formatRegexp.FindAllStringSubmatchIndex(n.nameFormat, -1)
for _, m := range matches {
start, end := m[0], m[1]
labelStart, labelEnd := m[2], m[3]
if start > cursor {
segments = append(segments, n.nameFormat[cursor:start])
}
key := n.nameFormat[labelStart:labelEnd]
var value string
switch key {
case "#namespace":
value = n.namespace
case "#subsystem":
value = n.subsystem
case "#name":
value = n.name
case "#fqname":
value = n.FullyQualifiedName()
default:
var ok bool
value, ok = labels[key]
if !ok {
panic(fmt.Sprintf("invalid label in name format: %s", key))
}
value = invalidLabelValueRegexp.ReplaceAllString(value, "_")
}
segments = append(segments, value)
cursor = end
}
// handle any trailing suffix
if cursor != len(n.nameFormat) {
segments = append(segments, n.nameFormat[cursor:])
}
return strings.Join(segments, "")
}
func sliceToSet(set []string) map[string]struct{} {
labelSet := map[string]struct{}{}
for _, s := range set {
labelSet[s] = struct{}{}
}
return labelSet
}