refine composer api a bit
This commit is contained in:
parent
6e2d6f9b01
commit
5c1cf09c50
59
composer.go
59
composer.go
@ -13,22 +13,8 @@ type LabelComposer interface {
|
|||||||
ToLabelsString() string
|
ToLabelsString() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// labelComposerMarker is a marker interface for enforcing type-safety of StructLabelComposer.
|
|
||||||
// Interface is private so it's only be used via StructLabelComposer implementation.
|
|
||||||
// This is made for safety reasons, so it's not allowed to pass a random struct to NameCompose() function.
|
|
||||||
type labelComposerMarker interface {
|
|
||||||
labelComposerMarker()
|
|
||||||
}
|
|
||||||
|
|
||||||
// StructLabelComposer MUST be embedded in any struct that serves as a label composer.
|
|
||||||
// Embedding is required even if you provide custom implementation of LabelComposer (ToLabelsString() method)
|
|
||||||
type StructLabelComposer struct{}
|
|
||||||
|
|
||||||
func (s StructLabelComposer) labelComposerMarker() { panic("should never happen") }
|
|
||||||
|
|
||||||
// NameCompose returns a valid full metric name, composed of a metric name + stringified labels
|
// NameCompose returns a valid full metric name, composed of a metric name + stringified labels
|
||||||
// It will first try to use custom LabelComposer implementation (if any)
|
// It accepts a valid LabelComposer interface, which is used to compose labels string.
|
||||||
// then fallback to slow reflection-based implementation.
|
|
||||||
//
|
//
|
||||||
// The NameCompose can be called for further GetOrCreateCounter/etc func:
|
// The NameCompose can be called for further GetOrCreateCounter/etc func:
|
||||||
//
|
//
|
||||||
@ -37,19 +23,46 @@ func (s StructLabelComposer) labelComposerMarker() { panic("should never happen"
|
|||||||
// Status: "active",
|
// Status: "active",
|
||||||
// Flag: false,
|
// Flag: false,
|
||||||
// })).Inc()
|
// })).Inc()
|
||||||
func NameCompose(name string, lc labelComposerMarker) string {
|
func NameCompose(name string, lc LabelComposer) string {
|
||||||
if lc == nil {
|
if lc == nil {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementing public LabelComposer means we must implement
|
return name + lc.ToLabelsString()
|
||||||
// a custom ToLabelsString() that supposed to be fast.
|
}
|
||||||
if v, ok := lc.(LabelComposer); ok {
|
|
||||||
return name + v.ToLabelsString()
|
//
|
||||||
|
// Auto composer
|
||||||
|
//
|
||||||
|
|
||||||
|
// labelComposerAutoMarker is just a marker interface.
|
||||||
|
// Interface is private so it's only be used via AutoLabelComposer implementation.
|
||||||
|
// This is made for safety reasons, so it's not allowed to pass a random struct to NameCompose() function.
|
||||||
|
type labelComposerAutoMarker interface {
|
||||||
|
autoComposeMarker()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoLabelComposer MUST be embedded in any struct that serves as a label composer.
|
||||||
|
// Embedding is required even if you provide custom implementation of LabelComposer (ToLabelsString() method)
|
||||||
|
type AutoLabelComposer struct{}
|
||||||
|
|
||||||
|
func (s AutoLabelComposer) autoComposeMarker() { panic("should never happen") }
|
||||||
|
|
||||||
|
// NameComposeAuto returns a valid full metric name, composed of a metric name + stringified labels
|
||||||
|
// It accepts a struct who embeds AutoLabelComposer so labels are generated from it.
|
||||||
|
//
|
||||||
|
// The NameComposeAuto can be called for further GetOrCreateCounter/etc func:
|
||||||
|
//
|
||||||
|
// // `my_counter{status="active",flag="false"}`
|
||||||
|
// GetOrCreateCounter(NameComposeAuto("my_counter", MyLabels{
|
||||||
|
// Status: "active",
|
||||||
|
// Flag: false,
|
||||||
|
// })).Inc()
|
||||||
|
func NameComposeAuto(name string, lc labelComposerAutoMarker) string {
|
||||||
|
if lc == nil {
|
||||||
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
// falling back to reflect-based implementation
|
|
||||||
// This is considered to be slow. Implement your own LabelComposer if it's an issue for you.
|
|
||||||
return name + reflectLabelCompose(lc)
|
return name + reflectLabelCompose(lc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +70,7 @@ func NameCompose(name string, lc labelComposerMarker) string {
|
|||||||
// It will use only exported scalar fields, and will skip fields with the `-` tag.
|
// It will use only exported scalar fields, and will skip fields with the `-` tag.
|
||||||
// By default, the snake_cased field name is used as the label name.
|
// By default, the snake_cased field name is used as the label name.
|
||||||
// Label's name can be overridden by using the `labels` tag
|
// Label's name can be overridden by using the `labels` tag
|
||||||
func reflectLabelCompose(lc labelComposerMarker) string {
|
func reflectLabelCompose(lc labelComposerAutoMarker) string {
|
||||||
labelsStr := "{"
|
labelsStr := "{"
|
||||||
|
|
||||||
val := reflect.Indirect(reflect.ValueOf(lc))
|
val := reflect.Indirect(reflect.ValueOf(lc))
|
||||||
|
@ -5,34 +5,11 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MyLabelsSlow will be converted into {hello="world",enabled="true"}
|
|
||||||
// via reflect implementation.
|
|
||||||
// It's slow but completely automatic. You don't need to write any code
|
|
||||||
type MyLabelsSlow struct {
|
|
||||||
StructLabelComposer
|
|
||||||
|
|
||||||
Status string
|
|
||||||
Flag bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLabelComposeWithReflect(t *testing.T) {
|
|
||||||
want := `my_counter{status="active",flag="true"}`
|
|
||||||
|
|
||||||
got := NameCompose("my_counter", MyLabelsSlow{
|
|
||||||
Status: "active",
|
|
||||||
Flag: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
if got != want {
|
|
||||||
t.Fatalf("unexpected full name; got %q; want %q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MyLabelsFast will be converted into string
|
// MyLabelsFast will be converted into string
|
||||||
// via custom implementation (Using ToLabelsString() method of LabelComposer interface)
|
// via custom implementation (Using ToLabelsString() method of LabelComposer interface)
|
||||||
// It's fast but requires manual implementation.
|
// It's fast but requires manual implementation.
|
||||||
type MyLabelsFast struct {
|
type MyLabelsFast struct {
|
||||||
StructLabelComposer
|
AutoLabelComposer
|
||||||
Status string
|
Status string
|
||||||
Flag bool
|
Flag bool
|
||||||
}
|
}
|
||||||
@ -40,7 +17,7 @@ type MyLabelsFast struct {
|
|||||||
func (m *MyLabelsFast) ToLabelsString() string {
|
func (m *MyLabelsFast) ToLabelsString() string {
|
||||||
return "{" +
|
return "{" +
|
||||||
`status="` + m.Status + `",` +
|
`status="` + m.Status + `",` +
|
||||||
`flag="` + fmt.Sprintf("%v", m.Flag) + `"` +
|
`flag="` + fmt.Sprintf("%t", m.Flag) + `"` +
|
||||||
"}"
|
"}"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,3 +31,26 @@ func TestLabelComposeWithoutReflect(t *testing.T) {
|
|||||||
t.Fatalf("unexpected full name; got %q; want %q", got, want)
|
t.Fatalf("unexpected full name; got %q; want %q", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLabelComposeWithReflect(t *testing.T) {
|
||||||
|
want := `my_counter{status="active",flag="true"}`
|
||||||
|
|
||||||
|
// MyLabelsSlow will be converted into {hello="world",enabled="true"}
|
||||||
|
// via reflect implementation.
|
||||||
|
// It's slow but completely automatic. You don't need to write any code
|
||||||
|
type MyLabelsAuto struct {
|
||||||
|
AutoLabelComposer
|
||||||
|
|
||||||
|
Status string
|
||||||
|
Flag bool
|
||||||
|
}
|
||||||
|
|
||||||
|
got := NameComposeAuto("my_counter", MyLabelsAuto{
|
||||||
|
Status: "active",
|
||||||
|
Flag: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("unexpected full name; got %q; want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user