promauto: make it available to avoid reflect when building labels

Signed-off-by: Eugene <eugene@amberpixels.io>
This commit is contained in:
Eugene 2024-09-20 11:13:27 +03:00
parent 83a2a655ae
commit 5b52b012e1
No known key found for this signature in database
GPG Key ID: 51AC89611A689305
2 changed files with 52 additions and 1 deletions

View File

@ -43,6 +43,13 @@ func SetupGlobalPromauto(factoryArg ...promauto.Factory) {
} }
} }
// CustomLabelsProvider is an interface that allows to convert anything to a prometheus.Labels
// It allows to provide your own FAST implementation of Struct->prometheus.Labels conversion
// without using reflection.
type CustomLabelsProvider interface {
ToPrometheusLabels() prometheus.Labels
}
// promsafeTag is the tag name used for promsafe labels inside structs. // promsafeTag is the tag name used for promsafe labels inside structs.
// The tag is optional, as if not present, field is used with snake_cased FieldName. // The tag is optional, as if not present, field is used with snake_cased FieldName.
// It's useful to use a tag when you want to override the default naming or exclude a field from the metric. // It's useful to use a tag when you want to override the default naming or exclude a field from the metric.
@ -273,6 +280,10 @@ func extractLabelsWithValues(labelProvider labelsProviderMarker) prometheus.Labe
return nil return nil
} }
if clp, ok := labelProvider.(CustomLabelsProvider); ok {
return clp.ToPrometheusLabels()
}
// TODO: let's handle defaults as well, why not? // TODO: let's handle defaults as well, why not?
// Here, then, it can be only a struct, that is a parent of StructLabelProvider // Here, then, it can be only a struct, that is a parent of StructLabelProvider
@ -291,13 +302,20 @@ func extractLabelValues(labelProvider labelsProviderMarker) []string {
} }
// extractLabelNames extracts labels names from a given labelsProviderMarker (parent instance of aStructLabelProvider) // extractLabelNames extracts labels names from a given labelsProviderMarker (parent instance of aStructLabelProvider)
// Deprecated: refactor is required. Order of result slice is not guaranteed.
func extractLabelNames(labelProvider labelsProviderMarker) []string { func extractLabelNames(labelProvider labelsProviderMarker) []string {
if any(labelProvider) == nil { if any(labelProvider) == nil {
return nil return nil
} }
var labels prometheus.Labels
if clp, ok := labelProvider.(CustomLabelsProvider); ok {
labels = clp.ToPrometheusLabels()
} else {
labels = extractLabelFromStruct(labelProvider)
}
// Here, then, it can be only a struct, that is a parent of StructLabelProvider // Here, then, it can be only a struct, that is a parent of StructLabelProvider
labels := extractLabelFromStruct(labelProvider)
labelNames := make([]string, 0, len(labels)) labelNames := make([]string, 0, len(labels))
for k := range labels { for k := range labels {
labelNames = append(labelNames, k) labelNames = append(labelNames, k)

View File

@ -21,6 +21,10 @@ import (
"github.com/prometheus/client_golang/prometheus/promsafe" "github.com/prometheus/client_golang/prometheus/promsafe"
) )
// Important: This is not a test file. These are only examples!
// These can be considered smoke tests, but nothing more.
// TODO: Write real tests
func ExampleNewCounterVecT_multiple_labels_manual() { func ExampleNewCounterVecT_multiple_labels_manual() {
// Manually registering with multiple labels // Manually registering with multiple labels
@ -140,6 +144,35 @@ func ExampleNewCounterVecT_pointer_to_labels_promauto() {
// Output: // Output:
} }
// FastMyLabels is a struct that will have a custom method that converts to prometheus.Labels
type FastMyLabels struct {
promsafe.StructLabelProvider
EventType string
Source string
}
// ToPrometheusLabels does a fast conversion to labels. No reflection involved.
func (f FastMyLabels) ToPrometheusLabels() prometheus.Labels {
return prometheus.Labels{"event_type": f.EventType, "source": f.Source}
}
func ExampleNewCounterVecT_fast_safe_labels_provider() {
// It's possible to use pointer to labels struct
myReg := prometheus.NewRegistry()
counterOpts := prometheus.CounterOpts{
Name: "items_counted_fast",
}
c := promsafe.WithAuto[FastMyLabels](myReg).NewCounterVecT(counterOpts)
c.With(FastMyLabels{
EventType: "reservation", Source: "source1",
}).Inc()
// Output:
}
func ExampleNewCounterVecT_single_label_manual() { func ExampleNewCounterVecT_single_label_manual() {
// Manually registering with a single label // Manually registering with a single label
// Example of usage of shorthand: no structs no generics, but one string only // Example of usage of shorthand: no structs no generics, but one string only