Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for custom labels on prometheus metrics #393

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 29 additions & 10 deletions components/metrics/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,27 @@ import (
"github.com/prometheus/client_golang/prometheus"
)

func NewPrometheusMetricsBuilder(prometheusRegistry prometheus.Registerer, namespace string, subsystem string) PrometheusMetricsBuilder {
return PrometheusMetricsBuilder{
Namespace: namespace,
Subsystem: subsystem,
type PrometheusMetricsBuilderConfig struct {
Namespace string
Subsystem string
AdditionalLabels []MetricLabel
}

func NewPrometheusMetricsBuilderWithConfig(prometheusRegistry prometheus.Registerer, config PrometheusMetricsBuilderConfig) PrometheusMetricsBuilder {
builder := PrometheusMetricsBuilder{
Namespace: config.Namespace,
Subsystem: config.Subsystem,
PrometheusRegistry: prometheusRegistry,
additionalLabels: config.AdditionalLabels,
}
return builder
}

func NewPrometheusMetricsBuilder(prometheusRegistry prometheus.Registerer, namespace string, subsystem string) PrometheusMetricsBuilder {
return NewPrometheusMetricsBuilderWithConfig(prometheusRegistry, PrometheusMetricsBuilderConfig{
Namespace: namespace,
Subsystem: subsystem,
})
}

// PrometheusMetricsBuilder provides methods to decorate publishers, subscribers and handlers.
Expand All @@ -22,6 +37,8 @@ type PrometheusMetricsBuilder struct {

Namespace string
Subsystem string

additionalLabels []MetricLabel
}

// AddPrometheusRouterMetrics is a convenience function that acts on the message router to add the metrics middleware
Expand All @@ -36,8 +53,9 @@ func (b PrometheusMetricsBuilder) AddPrometheusRouterMetrics(r *message.Router)
func (b PrometheusMetricsBuilder) DecoratePublisher(pub message.Publisher) (message.Publisher, error) {
var err error
d := PublisherPrometheusMetricsDecorator{
pub: pub,
publisherName: internal.StructName(pub),
pub: pub,
publisherName: internal.StructName(pub),
additionalLabels: b.additionalLabels,
}

d.publishTimeSeconds, err = b.registerHistogramVec(prometheus.NewHistogramVec(
Expand All @@ -47,7 +65,7 @@ func (b PrometheusMetricsBuilder) DecoratePublisher(pub message.Publisher) (mess
Name: "publish_time_seconds",
Help: "The time that a publishing attempt (success or not) took in seconds",
},
publisherLabelKeys,
toLabelsSlice(publisherLabelKeys, b.additionalLabels),
))
if err != nil {
return nil, errors.Wrap(err, "could not register publish time metric")
Expand All @@ -59,8 +77,9 @@ func (b PrometheusMetricsBuilder) DecoratePublisher(pub message.Publisher) (mess
func (b PrometheusMetricsBuilder) DecorateSubscriber(sub message.Subscriber) (message.Subscriber, error) {
var err error
d := &SubscriberPrometheusMetricsDecorator{
closing: make(chan struct{}),
subscriberName: internal.StructName(sub),
closing: make(chan struct{}),
subscriberName: internal.StructName(sub),
additionalLabels: b.additionalLabels,
}

d.subscriberMessagesReceivedTotal, err = b.registerCounterVec(prometheus.NewCounterVec(
Expand All @@ -70,7 +89,7 @@ func (b PrometheusMetricsBuilder) DecorateSubscriber(sub message.Subscriber) (me
Name: "subscriber_messages_received_total",
Help: "The total number of messages received by the subscriber",
},
append(subscriberLabelKeys, labelAcked),
toLabelsSlice(append(subscriberLabelKeys, labelAcked), b.additionalLabels),
))
if err != nil {
return nil, errors.Wrap(err, "could not register time to ack metric")
Expand Down
10 changes: 8 additions & 2 deletions components/metrics/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var (
// HandlerPrometheusMetricsMiddleware is a middleware that captures Prometheus metrics.
type HandlerPrometheusMetricsMiddleware struct {
handlerExecutionTimeSeconds *prometheus.HistogramVec
additionalLabels []MetricLabel
}

// Middleware returns the middleware ready to be used with watermill's Router.
Expand All @@ -45,6 +46,9 @@ func (m HandlerPrometheusMetricsMiddleware) Middleware(h message.HandlerFunc) me
labels := prometheus.Labels{
labelKeyHandlerName: message.HandlerNameFromCtx(ctx),
}
for _, lb := range m.additionalLabels {
labels[lb.Label] = lb.ComputeFn(ctx)
}

defer func() {
if err != nil {
Expand All @@ -62,7 +66,9 @@ func (m HandlerPrometheusMetricsMiddleware) Middleware(h message.HandlerFunc) me
// NewRouterMiddleware returns new middleware.
func (b PrometheusMetricsBuilder) NewRouterMiddleware() HandlerPrometheusMetricsMiddleware {
var err error
m := HandlerPrometheusMetricsMiddleware{}
m := HandlerPrometheusMetricsMiddleware{
additionalLabels: b.additionalLabels,
}

m.handlerExecutionTimeSeconds, err = b.registerHistogramVec(prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Expand All @@ -72,7 +78,7 @@ func (b PrometheusMetricsBuilder) NewRouterMiddleware() HandlerPrometheusMetrics
Help: "The total time elapsed while executing the handler function in seconds",
Buckets: handlerExecutionTimeBuckets,
},
handlerLabelKeys,
toLabelsSlice(handlerLabelKeys, b.additionalLabels),
))
if err != nil {
panic(errors.Wrap(err, "could not register handler execution time metric"))
Expand Down
27 changes: 27 additions & 0 deletions components/metrics/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,30 @@ func labelsFromCtx(ctx context.Context, labels ...string) prometheus.Labels {

return ctxLabels
}

type LabelComputeFn func(msgCtx context.Context) string
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to reflect on that signature. Is it enough to use only the context to get a label's value? Should we also pass the message pointer? For my use case, the additional labels are static, so I basically do this

        metrics.MetricLabel{
		Label: "service",
		ComputeFn: func(ctx context.Context) string {
			return "my_service"
		},
	},

but there might be a use case for getting something from the message itself as a label value.


type MetricLabel struct {
Label string
ComputeFn LabelComputeFn
}

func toLabelsSlice(baseLabels []string, customs []MetricLabel) []string {
labels := make([]string, len(baseLabels), len(baseLabels)+len(customs))
copy(labels, baseLabels)
for _, label := range customs {
//Check if the additional label is already in the base labels. We cannot have duplicate labels
//If it's in the base, just skip it as the compute function is going to overwrite the default value
contains := false
for _, baseLabel := range baseLabels {
if baseLabel == label.Label {
contains = true
break
}
}
if !contains {
labels = append(labels, label.Label)
}
}
return labels
}
4 changes: 4 additions & 0 deletions components/metrics/publisher.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type PublisherPrometheusMetricsDecorator struct {
pub message.Publisher
publisherName string
publishTimeSeconds *prometheus.HistogramVec
additionalLabels []MetricLabel
}

// Publish updates the relevant publisher metrics and calls the wrapped publisher's Publish.
Expand All @@ -37,6 +38,9 @@ func (m PublisherPrometheusMetricsDecorator) Publish(topic string, messages ...*
if labels[labelKeyHandlerName] == "" {
labels[labelKeyHandlerName] = labelValueNoHandler
}
for _, lb := range m.additionalLabels {
labels[lb.Label] = lb.ComputeFn(ctx)
}
start := time.Now()

defer func() {
Expand Down
4 changes: 4 additions & 0 deletions components/metrics/subscriber.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type SubscriberPrometheusMetricsDecorator struct {
subscriberName string
subscriberMessagesReceivedTotal *prometheus.CounterVec
closing chan struct{}
additionalLabels []MetricLabel
}

func (s SubscriberPrometheusMetricsDecorator) recordMetrics(msg *message.Message) {
Expand All @@ -33,6 +34,9 @@ func (s SubscriberPrometheusMetricsDecorator) recordMetrics(msg *message.Message
if labels[labelKeyHandlerName] == "" {
labels[labelKeyHandlerName] = labelValueNoHandler
}
for _, lb := range s.additionalLabels {
labels[lb.Label] = lb.ComputeFn(ctx)
}

go func() {
if subscribeAlreadyObserved(ctx) {
Expand Down