Skip to content

Commit

Permalink
up
Browse files Browse the repository at this point in the history
  • Loading branch information
amberpixels committed Nov 28, 2024
1 parent b6bd3b4 commit 7a3bcd9
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 72 deletions.
141 changes: 72 additions & 69 deletions prometheus/promsafe/safe.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,75 +21,78 @@
// The following example demonstrates how to create and use a CounterVec with
// type-safe labels (compared to how it's done in a regular way):
//
// package main
//
// import (
// "strconv"
//
// "github.com/prometheus/client_golang/prometheus"
// "github.com/prometheus/client_golang/prometheus/promsafe"
// )
//
// // Original unsafe way (no type safety)
// func originalUnsafeWay() {
// counterVec := prometheus.NewCounterVec(
// prometheus.CounterOpts{
// Name: "http_requests_total",
// Help: "Total number of HTTP requests by status code and method.",
// },
// []string{"code", "method"}, / / Labels defined as raw strings
// )
//
// // No compile-time checks; label order and types must be correct
// // You have to know which and how many labels are expected (in proper order)
// counterVec.WithLabelValues("200", "GET").Inc()
//
// // or you can use map, that is even more fragile
// counterVect.WithLabels(prometheus.Labels{"code": "200", "method": "GET"}).Inc()
// }
//
// // Safe way (Quick implementation, reflect-based under-the-hood)
// type Labels1 struct {
// promsafe.StructLabelProvider
// Code int
// Method string
// }
//
// func safeReflectWay() {
// counterVec := promsafe.NewCounterVec[Labels1](prometheus.CounterOpts{
// Name: "http_requests_total_reflection",
// Help: "Total number of HTTP requests by status code and method (reflection-based).",
// })
//
// // Compile-time safe and readable; Will be converted into properly ordered list: "200", "GET"
// counterVec.With(Labels1{Method: "GET", Code: 200}).Inc()
// }
//
// // Safe way with manual implementation (no reflection overhead, as fast as original)
// type Labels2 struct {
// promsafe.StructLabelProvider
// Code int
// Method string
// }
//
// func (c Labels2) ToPrometheusLabels() prometheus.Labels {
// return prometheus.Labels{
// "code": strconv.Itoa(c.Code), // Convert int to string
// "method": c.Method,
// }
// }
//
// func (c Labels2) ToLabelNames() []string {
// return []string{"code", "method"}
// }
//
// func safeManualWay() {
// counterVec := promsafe.NewCounterVec[Labels2](prometheus.CounterOpts{
// Name: "http_requests_total_custom",
// Help: "Total number of HTTP requests by status code and method (manual implementation).",
// })
// counterVec.With(Labels2{Code: 404, Method: "POST"}).Inc()
// }
// package main
//
// import (
// "strconv"
//
// "github.com/prometheus/client_golang/prometheus"
// "github.com/prometheus/client_golang/prometheus/promsafe"
// )
//
// // Original unsafe way (no type safety)
//
// func originalUnsafeWay() {
// counterVec := prometheus.NewCounterVec(
// prometheus.CounterOpts{
// Name: "http_requests_total",
// Help: "Total number of HTTP requests by status code and method.",
// },
// []string{"code", "method"}, // Labels defined as raw strings
// )
//
// // No compile-time checks; label order and types must be correct
// // You have to know which and how many labels are expected (in proper order)
// counterVec.WithLabelValues("200", "GET").Inc()
//
// // or you can use map, that is even more fragile
// counterVec.With(prometheus.Labels{"code": "200", "method": "GET"}).Inc()
// }
//
// // Safe way (Quick implementation, reflect-based under-the-hood)
//
// type Labels1 struct {
// promsafe.StructLabelProvider
// Code int
// Method string
// }
//
// func safeReflectWay() {
// counterVec := promsafe.NewCounterVec[Labels1](prometheus.CounterOpts{
// Name: "http_requests_total_reflection",
// Help: "Total number of HTTP requests by status code and method (reflection-based).",
// })
//
// // Compile-time safe and readable; Will be converted into properly ordered list: "200", "GET"
// counterVec.With(Labels1{Method: "GET", Code: 200}).Inc()
// }
//
// // Safe way with manual implementation (no reflection overhead, as fast as original)
//
// type Labels2 struct {
// promsafe.StructLabelProvider
// Code int
// Method string
// }
//
// func (c Labels2) ToPrometheusLabels() prometheus.Labels {
// return prometheus.Labels{
// "code": strconv.Itoa(c.Code), // Convert int to string
// "method": c.Method,
// }
// }
//
// func (c Labels2) ToLabelNames() []string {
// return []string{"code", "method"}
// }
//
// func safeManualWay() {
// counterVec := promsafe.NewCounterVec[Labels2](prometheus.CounterOpts{
// Name: "http_requests_total_custom",
// Help: "Total number of HTTP requests by status code and method (manual implementation).",
// })
// counterVec.With(Labels2{Code: 404, Method: "POST"}).Inc()
// }
//
// Package promsafe also provides compatibility adapter for integration with Prometheus's
// `promauto` package, ensuring seamless adoption while preserving type-safety.
Expand Down
6 changes: 3 additions & 3 deletions prometheus/promsafe/safe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (

// These are Examples that can be treated as basic smoke tests

func ExampleNewCounterVecT_multiple_labels_manual() {
func ExampleNewCounterVec_multiple_labels_manual() {
// Manually registering with multiple labels

type MyCounterLabels struct {
Expand Down Expand Up @@ -72,7 +72,7 @@ func (f FastMyLabels) ToLabelNames() []string {
return []string{"event_type", "source"}
}

func ExampleNewCounterVecT_fast_safe_labels_provider() {
func ExampleNewCounterVec_fast_safe_labels_provider() {
// Note: fast labels provider has a drawback: they can't be declared as inline structs
// as we need methods

Expand Down Expand Up @@ -118,12 +118,12 @@ func (t TestLabelsFast) ToPrometheusLabels() prometheus.Labels {
"label3": strconv.FormatBool(t.Label3),
}
}

func (t TestLabelsFast) ToLabelNames() []string {
return []string{"label1", "label2", "label3"}
}

func BenchmarkCompareCreatingMetric(b *testing.B) {

// Note: on stage of creation metrics, Unique metric names are not required,
// but let it be for consistency

Expand Down

0 comments on commit 7a3bcd9

Please sign in to comment.