122 lines
3 KiB
Go
122 lines
3 KiB
Go
// +build go1.3
|
||
package prometheus
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
|
||
"regexp"
|
||
|
||
"github.com/armon/go-metrics"
|
||
"github.com/prometheus/client_golang/prometheus"
|
||
)
|
||
|
||
type PrometheusSink struct {
|
||
mu sync.Mutex
|
||
gauges map[string]prometheus.Gauge
|
||
summaries map[string]prometheus.Summary
|
||
counters map[string]prometheus.Counter
|
||
}
|
||
|
||
func NewPrometheusSink() (*PrometheusSink, error) {
|
||
return &PrometheusSink{
|
||
gauges: make(map[string]prometheus.Gauge),
|
||
summaries: make(map[string]prometheus.Summary),
|
||
counters: make(map[string]prometheus.Counter),
|
||
}, nil
|
||
}
|
||
|
||
var forbiddenChars = regexp.MustCompile("[ .=\\-]")
|
||
|
||
func (p *PrometheusSink) flattenKey(parts []string, labels []metrics.Label) (string, string) {
|
||
key := strings.Join(parts, "_")
|
||
key = forbiddenChars.ReplaceAllString(key, "_")
|
||
|
||
hash := key
|
||
for _, label := range labels {
|
||
hash += fmt.Sprintf(";%s=%s", label.Name, label.Value)
|
||
}
|
||
|
||
return key, hash
|
||
}
|
||
|
||
func prometheusLabels(labels []metrics.Label) prometheus.Labels {
|
||
l := make(prometheus.Labels)
|
||
for _, label := range labels {
|
||
l[label.Name] = label.Value
|
||
}
|
||
return l
|
||
}
|
||
|
||
func (p *PrometheusSink) SetGauge(parts []string, val float32) {
|
||
p.SetGaugeWithLabels(parts, val, nil)
|
||
}
|
||
|
||
func (p *PrometheusSink) SetGaugeWithLabels(parts []string, val float32, labels []metrics.Label) {
|
||
p.mu.Lock()
|
||
defer p.mu.Unlock()
|
||
key, hash := p.flattenKey(parts, labels)
|
||
g, ok := p.gauges[hash]
|
||
if !ok {
|
||
g = prometheus.NewGauge(prometheus.GaugeOpts{
|
||
Name: key,
|
||
Help: key,
|
||
ConstLabels: prometheusLabels(labels),
|
||
})
|
||
prometheus.MustRegister(g)
|
||
p.gauges[key] = g
|
||
}
|
||
g.Set(float64(val))
|
||
}
|
||
|
||
func (p *PrometheusSink) AddSample(parts []string, val float32) {
|
||
p.AddSampleWithLabels(parts, val, nil)
|
||
}
|
||
|
||
func (p *PrometheusSink) AddSampleWithLabels(parts []string, val float32, labels []metrics.Label) {
|
||
p.mu.Lock()
|
||
defer p.mu.Unlock()
|
||
key, hash := p.flattenKey(parts, labels)
|
||
g, ok := p.summaries[hash]
|
||
if !ok {
|
||
g = prometheus.NewSummary(prometheus.SummaryOpts{
|
||
Name: key,
|
||
Help: key,
|
||
MaxAge: 10 * time.Second,
|
||
ConstLabels: prometheusLabels(labels),
|
||
})
|
||
prometheus.MustRegister(g)
|
||
p.summaries[key] = g
|
||
}
|
||
g.Observe(float64(val))
|
||
}
|
||
|
||
// EmitKey is not implemented. Prometheus doesn’t offer a type for which an
|
||
// arbitrary number of values is retained, as Prometheus works with a pull
|
||
// model, rather than a push model.
|
||
func (p *PrometheusSink) EmitKey(key []string, val float32) {
|
||
}
|
||
|
||
func (p *PrometheusSink) IncrCounter(parts []string, val float32) {
|
||
p.IncrCounterWithLabels(parts, val, nil)
|
||
}
|
||
|
||
func (p *PrometheusSink) IncrCounterWithLabels(parts []string, val float32, labels []metrics.Label) {
|
||
p.mu.Lock()
|
||
defer p.mu.Unlock()
|
||
key, hash := p.flattenKey(parts, labels)
|
||
g, ok := p.counters[hash]
|
||
if !ok {
|
||
g = prometheus.NewCounter(prometheus.CounterOpts{
|
||
Name: key,
|
||
Help: key,
|
||
ConstLabels: prometheusLabels(labels),
|
||
})
|
||
prometheus.MustRegister(g)
|
||
p.counters[key] = g
|
||
}
|
||
g.Add(float64(val))
|
||
}
|