Skip to content

Commit 5b52b01

Browse files
committed
promauto: make it available to avoid reflect when building labels
Signed-off-by: Eugene <[email protected]>
1 parent 83a2a65 commit 5b52b01

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

prometheus/promsafe/safe.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ func SetupGlobalPromauto(factoryArg ...promauto.Factory) {
4343
}
4444
}
4545

46+
// CustomLabelsProvider is an interface that allows to convert anything to a prometheus.Labels
47+
// It allows to provide your own FAST implementation of Struct->prometheus.Labels conversion
48+
// without using reflection.
49+
type CustomLabelsProvider interface {
50+
ToPrometheusLabels() prometheus.Labels
51+
}
52+
4653
// promsafeTag is the tag name used for promsafe labels inside structs.
4754
// The tag is optional, as if not present, field is used with snake_cased FieldName.
4855
// 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
273280
return nil
274281
}
275282

283+
if clp, ok := labelProvider.(CustomLabelsProvider); ok {
284+
return clp.ToPrometheusLabels()
285+
}
286+
276287
// TODO: let's handle defaults as well, why not?
277288

278289
// Here, then, it can be only a struct, that is a parent of StructLabelProvider
@@ -291,13 +302,20 @@ func extractLabelValues(labelProvider labelsProviderMarker) []string {
291302
}
292303

293304
// extractLabelNames extracts labels names from a given labelsProviderMarker (parent instance of aStructLabelProvider)
305+
// Deprecated: refactor is required. Order of result slice is not guaranteed.
294306
func extractLabelNames(labelProvider labelsProviderMarker) []string {
295307
if any(labelProvider) == nil {
296308
return nil
297309
}
298310

311+
var labels prometheus.Labels
312+
if clp, ok := labelProvider.(CustomLabelsProvider); ok {
313+
labels = clp.ToPrometheusLabels()
314+
} else {
315+
labels = extractLabelFromStruct(labelProvider)
316+
}
317+
299318
// Here, then, it can be only a struct, that is a parent of StructLabelProvider
300-
labels := extractLabelFromStruct(labelProvider)
301319
labelNames := make([]string, 0, len(labels))
302320
for k := range labels {
303321
labelNames = append(labelNames, k)

prometheus/promsafe/safe_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import (
2121
"github.com/prometheus/client_golang/prometheus/promsafe"
2222
)
2323

24+
// Important: This is not a test file. These are only examples!
25+
// These can be considered smoke tests, but nothing more.
26+
// TODO: Write real tests
27+
2428
func ExampleNewCounterVecT_multiple_labels_manual() {
2529
// Manually registering with multiple labels
2630

@@ -140,6 +144,35 @@ func ExampleNewCounterVecT_pointer_to_labels_promauto() {
140144
// Output:
141145
}
142146

147+
// FastMyLabels is a struct that will have a custom method that converts to prometheus.Labels
148+
type FastMyLabels struct {
149+
promsafe.StructLabelProvider
150+
EventType string
151+
Source string
152+
}
153+
154+
// ToPrometheusLabels does a fast conversion to labels. No reflection involved.
155+
func (f FastMyLabels) ToPrometheusLabels() prometheus.Labels {
156+
return prometheus.Labels{"event_type": f.EventType, "source": f.Source}
157+
}
158+
159+
func ExampleNewCounterVecT_fast_safe_labels_provider() {
160+
// It's possible to use pointer to labels struct
161+
myReg := prometheus.NewRegistry()
162+
163+
counterOpts := prometheus.CounterOpts{
164+
Name: "items_counted_fast",
165+
}
166+
167+
c := promsafe.WithAuto[FastMyLabels](myReg).NewCounterVecT(counterOpts)
168+
169+
c.With(FastMyLabels{
170+
EventType: "reservation", Source: "source1",
171+
}).Inc()
172+
173+
// Output:
174+
}
175+
143176
func ExampleNewCounterVecT_single_label_manual() {
144177
// Manually registering with a single label
145178
// Example of usage of shorthand: no structs no generics, but one string only

0 commit comments

Comments
 (0)