github.com/wfusion/gofusion@v1.1.14/common/infra/watermill/components/metrics/builder.go (about)

     1  package metrics
     2  
     3  import (
     4  	"github.com/pkg/errors"
     5  	"github.com/prometheus/client_golang/prometheus"
     6  
     7  	"github.com/wfusion/gofusion/common/infra/watermill/message"
     8  	"github.com/wfusion/gofusion/common/infra/watermill/pkg"
     9  )
    10  
    11  func NewPrometheusMetricsBuilder(prometheusRegistry prometheus.Registerer,
    12  	namespace string, subsystem string) PrometheusMetricsBuilder {
    13  	return PrometheusMetricsBuilder{
    14  		Namespace:          namespace,
    15  		Subsystem:          subsystem,
    16  		PrometheusRegistry: prometheusRegistry,
    17  	}
    18  }
    19  
    20  // PrometheusMetricsBuilder provides methods to decorate publishers, subscribers and handlers.
    21  type PrometheusMetricsBuilder struct {
    22  	// PrometheusRegistry may be filled with a pre-existing Prometheus registry, or left empty for the default registry.
    23  	PrometheusRegistry prometheus.Registerer
    24  
    25  	Namespace string
    26  	Subsystem string
    27  }
    28  
    29  // AddPrometheusRouterMetrics is a convenience function that acts on the message router to add the metrics middleware
    30  // to all its handlers. The handlers' publishers and subscribers are also decorated.
    31  func (b PrometheusMetricsBuilder) AddPrometheusRouterMetrics(r *message.Router) {
    32  	r.AddPublisherDecorators(b.DecoratePublisher)
    33  	r.AddSubscriberDecorators(b.DecorateSubscriber)
    34  	r.AddMiddleware(b.NewRouterMiddleware().Middleware)
    35  }
    36  
    37  // DecoratePublisher wraps the underlying publisher with Prometheus metrics.
    38  func (b PrometheusMetricsBuilder) DecoratePublisher(pub message.Publisher) (message.Publisher, error) {
    39  	var err error
    40  	d := PublisherPrometheusMetricsDecorator{
    41  		pub:           pub,
    42  		publisherName: pkg.StructName(pub),
    43  	}
    44  
    45  	d.publishTimeSeconds, err = b.registerHistogramVec(prometheus.NewHistogramVec(
    46  		prometheus.HistogramOpts{
    47  			Namespace: b.Namespace,
    48  			Subsystem: b.Subsystem,
    49  			Name:      "publish_time_seconds",
    50  			Help:      "The time that a publishing attempt (success or not) took in seconds",
    51  		},
    52  		publisherLabelKeys,
    53  	))
    54  	if err != nil {
    55  		return nil, errors.Wrap(err, "could not register publish time metric")
    56  	}
    57  	return d, nil
    58  }
    59  
    60  // DecorateSubscriber wraps the underlying subscriber with Prometheus metrics.
    61  func (b PrometheusMetricsBuilder) DecorateSubscriber(sub message.Subscriber) (message.Subscriber, error) {
    62  	var err error
    63  	d := &SubscriberPrometheusMetricsDecorator{
    64  		closing:        make(chan struct{}),
    65  		subscriberName: pkg.StructName(sub),
    66  	}
    67  
    68  	d.subscriberMessagesReceivedTotal, err = b.registerCounterVec(prometheus.NewCounterVec(
    69  		prometheus.CounterOpts{
    70  			Namespace: b.Namespace,
    71  			Subsystem: b.Subsystem,
    72  			Name:      "subscriber_messages_received_total",
    73  			Help:      "The total number of messages received by the subscriber",
    74  		},
    75  		append(subscriberLabelKeys, labelAcked),
    76  	))
    77  	if err != nil {
    78  		return nil, errors.Wrap(err, "could not register time to ack metric")
    79  	}
    80  
    81  	d.Subscriber, err = message.MessageTransformSubscriberDecorator(d.recordMetrics)(sub)
    82  	if err != nil {
    83  		return nil, errors.Wrap(err, "could not decorate subscriber with metrics decorator")
    84  	}
    85  
    86  	return d, nil
    87  }
    88  
    89  func (b PrometheusMetricsBuilder) register(c prometheus.Collector) (prometheus.Collector, error) {
    90  	err := b.PrometheusRegistry.Register(c)
    91  	if err == nil {
    92  		return c, nil
    93  	}
    94  
    95  	if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
    96  		return are.ExistingCollector, nil
    97  	}
    98  
    99  	return nil, err
   100  }
   101  
   102  func (b PrometheusMetricsBuilder) registerCounterVec(c *prometheus.CounterVec) (*prometheus.CounterVec, error) {
   103  	col, err := b.register(c)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	return col.(*prometheus.CounterVec), nil
   108  }
   109  
   110  func (b PrometheusMetricsBuilder) registerHistogramVec(h *prometheus.HistogramVec) (*prometheus.HistogramVec, error) {
   111  	col, err := b.register(h)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	return col.(*prometheus.HistogramVec), nil
   116  }