Skip to content

Commit 12829c0

Browse files
Add new metric type - Info
1 parent 481df82 commit 12829c0

3 files changed

Lines changed: 103 additions & 13 deletions

File tree

pkg/metrics/info.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package metrics
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
// Info is a metric type that represents a constant information
9+
// that cannot change in the time.
10+
type Info struct {
11+
name string
12+
labels map[string]string
13+
}
14+
15+
// Exposes the info in the text-based exposition format.
16+
func (i *Info) expose() string {
17+
labelsStrings := make([]string, 0)
18+
for name, value := range i.labels {
19+
labelsStrings = append(
20+
labelsStrings,
21+
fmt.Sprintf("%v=\"%v\"", name, value),
22+
)
23+
}
24+
labels := strings.Join(labelsStrings, ",")
25+
26+
return fmt.Sprintf("%v{%v} %v", i.name, labels, "1")
27+
}

pkg/metrics/info_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package metrics
2+
3+
import "testing"
4+
5+
func TestInfoExpose(t *testing.T) {
6+
info := &Info{
7+
name: "test_info",
8+
labels: map[string]string{"label": "value"},
9+
}
10+
11+
actualText := info.expose()
12+
13+
expectedText := "test_info{label=\"value\"} 1"
14+
15+
if actualText != expectedText {
16+
t.Fatalf(
17+
"incorrect gauge expose text:\n"+
18+
"expected: [%v]\n"+
19+
"actual: [%v]",
20+
expectedText,
21+
actualText,
22+
)
23+
}
24+
}

pkg/metrics/registry.go

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ type metric interface {
2828
expose() string
2929
}
3030

31-
// Label represents an arbitrary information which will be attached to all
32-
// metrics managed by the registry.
31+
// Label represents an arbitrary information attached to the metrics.
3332
type Label struct {
3433
name string
3534
value string
@@ -54,11 +53,24 @@ func NewRegistry(
5453
application, identifier string,
5554
additionalLabels ...Label,
5655
) *Registry {
57-
labels := map[string]string{
58-
"application": application,
59-
"identifier": identifier,
56+
labels := mergeLabels(
57+
map[string]string{
58+
"application": application,
59+
"identifier": identifier,
60+
},
61+
additionalLabels,
62+
)
63+
64+
return &Registry{
65+
labels: labels,
66+
metrics: make(map[string]metric),
6067
}
68+
}
6169

70+
func mergeLabels(
71+
labels map[string]string,
72+
additionalLabels []Label,
73+
) map[string]string {
6274
for _, additionalLabel := range additionalLabels {
6375
if additionalLabel.name == "" || additionalLabel.value == "" {
6476
continue
@@ -71,10 +83,7 @@ func NewRegistry(
7183
labels[additionalLabel.name] = additionalLabel.value
7284
}
7385

74-
return &Registry{
75-
labels: labels,
76-
metrics: make(map[string]metric),
77-
}
86+
return labels
7887
}
7988

8089
// EnableServer enables the metrics server on the given port. Data will
@@ -111,28 +120,35 @@ func (r *Registry) exposeMetrics() string {
111120
// NewGauge creates and registers a new gauge metric which will be exposed
112121
// through the metrics server. In case a metric already exists, an error
113122
// will be returned.
114-
func (r *Registry) NewGauge(name string) (*Gauge, error) {
123+
func (r *Registry) NewGauge(
124+
name string,
125+
additionalLabels ...Label,
126+
) (*Gauge, error) {
115127
r.metricsMutex.Lock()
116128
defer r.metricsMutex.Unlock()
117129

118130
if _, exists := r.metrics[name]; exists {
119-
return nil, fmt.Errorf("gauge [%v] already exists", name)
131+
return nil, fmt.Errorf("metric [%v] already exists", name)
120132
}
121133

122134
gauge := &Gauge{
123135
name: name,
124-
labels: r.labels,
136+
labels: mergeLabels(r.labels, additionalLabels),
125137
}
126138

127139
r.metrics[name] = gauge
128140
return gauge, nil
129141
}
130142

143+
// NewGaugeObserver creates and registers a gauge just like `NewGauge` method
144+
// and wrap it with a ready to use observer of the provided input. This allows
145+
// to easily create self-refreshing metrics.
131146
func (r *Registry) NewGaugeObserver(
132147
name string,
133148
input ObserverInput,
149+
additionalLabels ...Label,
134150
) (*Observer, error) {
135-
gauge, err := r.NewGauge(name)
151+
gauge, err := r.NewGauge(name, additionalLabels...)
136152
if err != nil {
137153
return nil, err
138154
}
@@ -142,3 +158,26 @@ func (r *Registry) NewGaugeObserver(
142158
output: gauge,
143159
}, nil
144160
}
161+
162+
// NewInfo creates and registers a new info metric which will be exposed
163+
// through the metrics server. In case a metric already exists, an error
164+
// will be returned.
165+
func (r *Registry) NewInfo(
166+
name string,
167+
additionalLabels ...Label,
168+
) (*Info, error) {
169+
r.metricsMutex.Lock()
170+
defer r.metricsMutex.Unlock()
171+
172+
if _, exists := r.metrics[name]; exists {
173+
return nil, fmt.Errorf("metric [%v] already exists", name)
174+
}
175+
176+
info := &Info{
177+
name: name,
178+
labels: mergeLabels(r.labels, additionalLabels),
179+
}
180+
181+
r.metrics[name] = info
182+
return info, nil
183+
}

0 commit comments

Comments
 (0)