Skip to content

Commit 2f3a0f8

Browse files
committed
Make the AlreadyRegisteredError useful for wrapped registries
Signed-off-by: beorn7 <[email protected]>
1 parent 92d8f4a commit 2f3a0f8

File tree

3 files changed

+132
-20
lines changed

3 files changed

+132
-20
lines changed

prometheus/registry.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,9 +325,17 @@ func (r *Registry) Register(c Collector) error {
325325
return nil
326326
}
327327
if existing, exists := r.collectorsByID[collectorID]; exists {
328-
return AlreadyRegisteredError{
329-
ExistingCollector: existing,
330-
NewCollector: c,
328+
switch e := existing.(type) {
329+
case *wrappingCollector:
330+
return AlreadyRegisteredError{
331+
ExistingCollector: e.unwrapRecursively(),
332+
NewCollector: c,
333+
}
334+
default:
335+
return AlreadyRegisteredError{
336+
ExistingCollector: e,
337+
NewCollector: c,
338+
}
331339
}
332340
}
333341
// If the collectorID is new, but at least one of the descs existed

prometheus/registry_test.go

Lines changed: 100 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -746,37 +746,120 @@ func BenchmarkHandler(b *testing.B) {
746746
}
747747

748748
func TestAlreadyRegistered(t *testing.T) {
749-
reg := prometheus.NewRegistry()
750749
original := prometheus.NewCounterVec(
750+
prometheus.CounterOpts{
751+
Name: "test",
752+
Help: "help",
753+
ConstLabels: prometheus.Labels{"const": "label"},
754+
},
755+
[]string{"foo", "bar"},
756+
)
757+
equalButNotSame := prometheus.NewCounterVec(
758+
prometheus.CounterOpts{
759+
Name: "test",
760+
Help: "help",
761+
ConstLabels: prometheus.Labels{"const": "label"},
762+
},
763+
[]string{"foo", "bar"},
764+
)
765+
originalWithoutConstLabel := prometheus.NewCounterVec(
751766
prometheus.CounterOpts{
752767
Name: "test",
753768
Help: "help",
754769
},
755770
[]string{"foo", "bar"},
756771
)
757-
equalButNotSame := prometheus.NewCounterVec(
772+
equalButNotSameWithoutConstLabel := prometheus.NewCounterVec(
758773
prometheus.CounterOpts{
759774
Name: "test",
760775
Help: "help",
761776
},
762777
[]string{"foo", "bar"},
763778
)
764-
var err error
765-
if err = reg.Register(original); err != nil {
766-
t.Fatal(err)
767-
}
768-
if err = reg.Register(equalButNotSame); err == nil {
769-
t.Fatal("expected error when registering equal collector")
779+
780+
scenarios := []struct {
781+
name string
782+
originalCollector prometheus.Collector
783+
registerWith func(prometheus.Registerer) prometheus.Registerer
784+
newCollector prometheus.Collector
785+
reRegisterWith func(prometheus.Registerer) prometheus.Registerer
786+
}{
787+
{
788+
"RegisterNormallyReregisterNormally",
789+
original,
790+
func(r prometheus.Registerer) prometheus.Registerer { return r },
791+
equalButNotSame,
792+
func(r prometheus.Registerer) prometheus.Registerer { return r },
793+
},
794+
{
795+
"RegisterNormallyReregisterWrapped",
796+
original,
797+
func(r prometheus.Registerer) prometheus.Registerer { return r },
798+
equalButNotSameWithoutConstLabel,
799+
func(r prometheus.Registerer) prometheus.Registerer {
800+
return prometheus.WrapRegistererWith(prometheus.Labels{"const": "label"}, r)
801+
},
802+
},
803+
{
804+
"RegisterWrappedReregisterWrapped",
805+
originalWithoutConstLabel,
806+
func(r prometheus.Registerer) prometheus.Registerer {
807+
return prometheus.WrapRegistererWith(prometheus.Labels{"const": "label"}, r)
808+
},
809+
equalButNotSameWithoutConstLabel,
810+
func(r prometheus.Registerer) prometheus.Registerer {
811+
return prometheus.WrapRegistererWith(prometheus.Labels{"const": "label"}, r)
812+
},
813+
},
814+
{
815+
"RegisterWrappedReregisterNormally",
816+
originalWithoutConstLabel,
817+
func(r prometheus.Registerer) prometheus.Registerer {
818+
return prometheus.WrapRegistererWith(prometheus.Labels{"const": "label"}, r)
819+
},
820+
equalButNotSame,
821+
func(r prometheus.Registerer) prometheus.Registerer { return r },
822+
},
823+
{
824+
"RegisterDoublyWrappedReregisterDoublyWrapped",
825+
originalWithoutConstLabel,
826+
func(r prometheus.Registerer) prometheus.Registerer {
827+
return prometheus.WrapRegistererWithPrefix(
828+
"wrap_",
829+
prometheus.WrapRegistererWith(prometheus.Labels{"const": "label"}, r),
830+
)
831+
},
832+
equalButNotSameWithoutConstLabel,
833+
func(r prometheus.Registerer) prometheus.Registerer {
834+
return prometheus.WrapRegistererWithPrefix(
835+
"wrap_",
836+
prometheus.WrapRegistererWith(prometheus.Labels{"const": "label"}, r),
837+
)
838+
},
839+
},
770840
}
771-
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
772-
if are.ExistingCollector != original {
773-
t.Error("expected original collector but got something else")
774-
}
775-
if are.ExistingCollector == equalButNotSame {
776-
t.Error("expected original callector but got new one")
777-
}
778-
} else {
779-
t.Error("unexpected error:", err)
841+
842+
for _, s := range scenarios {
843+
t.Run(s.name, func(t *testing.T) {
844+
var err error
845+
reg := prometheus.NewRegistry()
846+
if err = s.registerWith(reg).Register(s.originalCollector); err != nil {
847+
t.Fatal(err)
848+
}
849+
if err = s.reRegisterWith(reg).Register(s.newCollector); err == nil {
850+
t.Fatal("expected error when registering new collector")
851+
}
852+
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
853+
if are.ExistingCollector != s.originalCollector {
854+
t.Error("expected original collector but got something else")
855+
}
856+
if are.ExistingCollector == s.newCollector {
857+
t.Error("expected original collector but got new one")
858+
}
859+
} else {
860+
t.Error("unexpected error:", err)
861+
}
862+
})
780863
}
781864
}
782865

prometheus/wrap.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ import (
3232
// WrapRegistererWith provides a way to add fixed labels to a subset of
3333
// Collectors. It should not be used to add fixed labels to all metrics exposed.
3434
//
35+
// Conflicts between Collectors registered through the original Registerer with
36+
// Collectors registered through the wrapping Registerer will still be
37+
// detected. Any AlreadyRegisteredError returned by the Register method of
38+
// either Registerer will contain the ExistingCollector in the form it was
39+
// provided to the respective registry.
40+
//
3541
// The Collector example demonstrates a use of WrapRegistererWith.
3642
func WrapRegistererWith(labels Labels, reg Registerer) Registerer {
3743
return &wrappingRegisterer{
@@ -54,6 +60,12 @@ func WrapRegistererWith(labels Labels, reg Registerer) Registerer {
5460
// (see NewGoCollector) and the process collector (see NewProcessCollector). (In
5561
// fact, those metrics are already prefixed with “go_” or “process_”,
5662
// respectively.)
63+
//
64+
// Conflicts between Collectors registered through the original Registerer with
65+
// Collectors registered through the wrapping Registerer will still be
66+
// detected. Any AlreadyRegisteredError returned by the Register method of
67+
// either Registerer will contain the ExistingCollector in the form it was
68+
// provided to the respective registry.
5769
func WrapRegistererWithPrefix(prefix string, reg Registerer) Registerer {
5870
return &wrappingRegisterer{
5971
wrappedRegisterer: reg,
@@ -123,6 +135,15 @@ func (c *wrappingCollector) Describe(ch chan<- *Desc) {
123135
}
124136
}
125137

138+
func (c *wrappingCollector) unwrapRecursively() Collector {
139+
switch wc := c.wrappedCollector.(type) {
140+
case *wrappingCollector:
141+
return wc.unwrapRecursively()
142+
default:
143+
return wc
144+
}
145+
}
146+
126147
type wrappingMetric struct {
127148
wrappedMetric Metric
128149
prefix string

0 commit comments

Comments
 (0)