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 }