metrics: add label type in metrics package

This commit is contained in:
j75689 2022-11-28 22:09:27 +08:00
parent d87d697d92
commit d77ebb8511
5 changed files with 131 additions and 1 deletions

@ -95,6 +95,20 @@ func (exp *exp) getFloat(name string) *expvar.Float {
return v return v
} }
func (exp *exp) getMap(name string) *expvar.Map {
var v *expvar.Map
exp.expvarLock.Lock()
p := expvar.Get(name)
if p != nil {
v = p.(*expvar.Map)
} else {
v = new(expvar.Map)
expvar.Publish(name, v)
}
exp.expvarLock.Unlock()
return v
}
func (exp *exp) publishCounter(name string, metric metrics.Counter) { func (exp *exp) publishCounter(name string, metric metrics.Counter) {
v := exp.getInt(name) v := exp.getInt(name)
v.Set(metric.Count()) v.Set(metric.Count())
@ -162,6 +176,60 @@ func (exp *exp) publishResettingTimer(name string, metric metrics.ResettingTimer
exp.getInt(name + ".99-percentile").Set(ps[3]) exp.getInt(name + ".99-percentile").Set(ps[3])
} }
func (exp *exp) publishLabel(name string, metric metrics.Label) {
labels := metric.Value()
for k, v := range labels {
exp.getMap(name).Set(k, exp.interfaceToExpVal(v))
}
}
func (exp *exp) interfaceToExpVal(v interface{}) expvar.Var {
switch i := v.(type) {
case string:
newV := new(expvar.String)
newV.Set(i)
return newV
case int64:
newV := new(expvar.Int)
newV.Set(i)
return newV
case int32:
newV := new(expvar.Int)
newV.Set(int64(i))
return newV
case int16:
newV := new(expvar.Int)
newV.Set(int64(i))
return newV
case int8:
newV := new(expvar.Int)
newV.Set(int64(i))
return newV
case int:
newV := new(expvar.Int)
newV.Set(int64(i))
return newV
case float32:
newV := new(expvar.Float)
newV.Set(float64(i))
return newV
case float64:
newV := new(expvar.Float)
newV.Set(i)
return newV
case map[string]interface{}:
newV := new(expvar.Map)
for k, v := range i {
newV.Set(k, exp.interfaceToExpVal(v))
}
return newV
default:
newV := new(expvar.String)
newV.Set(fmt.Sprint(v))
return newV
}
}
func (exp *exp) syncToExpvar() { func (exp *exp) syncToExpvar() {
exp.registry.Each(func(name string, i interface{}) { exp.registry.Each(func(name string, i interface{}) {
switch i := i.(type) { switch i := i.(type) {
@ -179,6 +247,8 @@ func (exp *exp) syncToExpvar() {
exp.publishTimer(name, i) exp.publishTimer(name, i)
case metrics.ResettingTimer: case metrics.ResettingTimer:
exp.publishResettingTimer(name, i) exp.publishResettingTimer(name, i)
case metrics.Label:
exp.publishLabel(name, i)
default: default:
panic(fmt.Sprintf("unsupported type for '%s': %T", name, i)) panic(fmt.Sprintf("unsupported type for '%s': %T", name, i))
} }

48
metrics/label.go Normal file

@ -0,0 +1,48 @@
package metrics
import "encoding/json"
// Label hold an map[string]interface{} value that can be set arbitrarily.
type Label interface {
Value() map[string]interface{}
String() string
Mark(map[string]interface{})
}
// NewRegisteredLabel constructs and registers a new StandardLabel.
func NewRegisteredLabel(name string, r Registry) Label {
c := NewStandardLabel()
if nil == r {
r = DefaultRegistry
}
r.Register(name, c)
return c
}
// NewStandardLabel constructs a new StandardLabel.
func NewStandardLabel() *StandardLabel {
return &StandardLabel{}
}
// StandardLabel is the standard implementation of a Label.
type StandardLabel struct {
value map[string]interface{}
jsonStr string
}
// Value returns label values.
func (l *StandardLabel) Value() map[string]interface{} {
return l.value
}
// Mark records the label.
func (l *StandardLabel) Mark(value map[string]interface{}) {
buf, _ := json.Marshal(value)
l.jsonStr = string(buf)
l.value = value
}
// String returns label by JSON format.
func (l *StandardLabel) String() string {
return l.jsonStr
}

@ -31,6 +31,7 @@ var (
typeSummaryTpl = "# TYPE %s summary\n" typeSummaryTpl = "# TYPE %s summary\n"
keyValueTpl = "%s %v\n\n" keyValueTpl = "%s %v\n\n"
keyQuantileTagValueTpl = "%s {quantile=\"%s\"} %v\n" keyQuantileTagValueTpl = "%s {quantile=\"%s\"} %v\n"
keyLabelValueTpl = "%s%s %v\n\n"
) )
// collector is a collection of byte buffers that aggregate Prometheus reports // collector is a collection of byte buffers that aggregate Prometheus reports
@ -98,6 +99,15 @@ func (c *collector) addResettingTimer(name string, m metrics.ResettingTimer) {
c.buff.WriteRune('\n') c.buff.WriteRune('\n')
} }
func (c *collector) addLabel(name string, m metrics.Label) {
c.writeLabel(mutateKey(name), m.String())
}
func (c *collector) writeLabel(name string, value interface{}) {
c.buff.WriteString(fmt.Sprintf(typeGaugeTpl, name))
c.buff.WriteString(fmt.Sprintf(keyLabelValueTpl, name, value, 1))
}
func (c *collector) writeGaugeCounter(name string, value interface{}) { func (c *collector) writeGaugeCounter(name string, value interface{}) {
name = mutateKey(name) name = mutateKey(name)
c.buff.WriteString(fmt.Sprintf(typeGaugeTpl, name)) c.buff.WriteString(fmt.Sprintf(typeGaugeTpl, name))

@ -57,6 +57,8 @@ func Handler(reg metrics.Registry) http.Handler {
c.addTimer(name, m.Snapshot()) c.addTimer(name, m.Snapshot())
case metrics.ResettingTimer: case metrics.ResettingTimer:
c.addResettingTimer(name, m.Snapshot()) c.addResettingTimer(name, m.Snapshot())
case metrics.Label:
c.addLabel(name, m)
default: default:
log.Warn("Unknown Prometheus metric type", "type", fmt.Sprintf("%T", i)) log.Warn("Unknown Prometheus metric type", "type", fmt.Sprintf("%T", i))
} }

@ -196,7 +196,7 @@ func (r *StandardRegistry) register(name string, i interface{}) error {
return DuplicateMetric(name) return DuplicateMetric(name)
} }
switch i.(type) { switch i.(type) {
case Counter, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer, ResettingTimer: case Counter, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer, ResettingTimer, Label:
r.metrics[name] = i r.metrics[name] = i
} }
return nil return nil