68 lines
1.5 KiB
Go
68 lines
1.5 KiB
Go
package metrics
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
)
|
|
|
|
type Registry struct {
|
|
mu sync.RWMutex
|
|
counters map[string]*atomic.Uint64
|
|
gauges map[string]*atomic.Int64
|
|
}
|
|
|
|
func New() *Registry {
|
|
return &Registry{counters: map[string]*atomic.Uint64{}, gauges: map[string]*atomic.Int64{}}
|
|
}
|
|
func (r *Registry) Counter(name string) *atomic.Uint64 {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
if c := r.counters[name]; c != nil {
|
|
return c
|
|
}
|
|
c := &atomic.Uint64{}
|
|
r.counters[name] = c
|
|
return c
|
|
}
|
|
func (r *Registry) Gauge(name string) *atomic.Int64 {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
if g := r.gauges[name]; g != nil {
|
|
return g
|
|
}
|
|
g := &atomic.Int64{}
|
|
r.gauges[name] = g
|
|
return g
|
|
}
|
|
func (r *Registry) Handler() http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.Header().Set("Content-Type", "text/plain; version=0.0.4")
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
var lines []string
|
|
for n, c := range r.counters {
|
|
lines = append(lines, fmt.Sprintf("%s %d", sanitize(n), c.Load()))
|
|
}
|
|
for n, g := range r.gauges {
|
|
lines = append(lines, fmt.Sprintf("%s %d", sanitize(n), g.Load()))
|
|
}
|
|
sort.Strings(lines)
|
|
fmt.Fprintln(w, strings.Join(lines, "\n"))
|
|
})
|
|
}
|
|
func sanitize(s string) string {
|
|
var b strings.Builder
|
|
for i, r := range s {
|
|
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9' && i > 0) || r == '_' || r == ':' {
|
|
b.WriteRune(r)
|
|
} else {
|
|
b.WriteByte('_')
|
|
}
|
|
}
|
|
return b.String()
|
|
}
|