1package metrics
2
3import (
4	"fmt"
5	"sort"
6)
7
8type Registry struct {
9	descriptors []*groupDesc
10	groups      []groupRegistry
11}
12
13const (
14	// DefaultGroup is the identifier for the default group.
15	DefaultGroup = GID(0)
16)
17
18// NewRegistry creates a new Registry with a single group identified by DefaultGroup.
19func NewRegistry() *Registry {
20	var r Registry
21	r.MustRegisterGroup("global")
22	return &r
23}
24
25func (r *Registry) register(gd *groupDesc) error {
26	p := sort.Search(len(r.descriptors), func(i int) bool {
27		return r.descriptors[i].Name == gd.Name
28	})
29
30	if p != len(r.descriptors) {
31		return fmt.Errorf("group name '%s' already in use", gd.Name)
32	}
33
34	r.descriptors = append(r.descriptors, gd)
35	sort.Slice(r.descriptors, func(i, j int) bool {
36		return r.descriptors[i].Name < r.descriptors[j].Name
37	})
38
39	gd.id = GID(len(r.groups))
40	r.groups = append(r.groups, groupRegistry{desc: gd})
41
42	return nil
43}
44
45func (r *Registry) mustRegister(gd *groupDesc) {
46	if err := r.register(gd); err != nil {
47		panic(err.Error())
48	}
49}
50
51// MustRegisterGroup registers a new group and panics if a group already exists with the same name.
52//
53// MustRegisterGroup is not safe to call from concurrent goroutines.
54func (r *Registry) MustRegisterGroup(name string) GID {
55	gd := &groupDesc{Name: name}
56	r.mustRegister(gd)
57	return gd.id
58}
59
60func (r *Registry) mustGetGroupRegistry(id GID) *groupRegistry {
61	if int(id) >= len(r.groups) {
62		panic(fmt.Sprintf("invalid group ID"))
63	}
64	return &r.groups[id]
65}
66
67// MustRegisterCounter registers a new counter metric using the provided descriptor.
68// If the metric name is not unique within the group, MustRegisterCounter will panic.
69//
70// MustRegisterCounter is not safe to call from concurrent goroutines.
71func (r *Registry) MustRegisterCounter(name string, opts ...descOption) ID {
72	desc := newDesc(name, opts...)
73	return r.mustGetGroupRegistry(desc.gid).mustRegisterCounter(desc)
74}
75
76// MustRegisterTimer registers a new timer metric using the provided descriptor.
77// If the metric name is not unique within the group, MustRegisterTimer will panic.
78//
79// MustRegisterTimer is not safe to call from concurrent goroutines.
80func (r *Registry) MustRegisterTimer(name string, opts ...descOption) ID {
81	desc := newDesc(name, opts...)
82	return r.mustGetGroupRegistry(desc.gid).mustRegisterTimer(desc)
83}
84
85func (r *Registry) NewGroup(gid GID) *Group {
86	return r.mustGetGroupRegistry(gid).newGroup()
87}
88