+++
title = "Metrics"
description = "Monitor Watermill in realtime"
date = 2019-02-12T21:00:00+01:00
weight = -400
draft = false
bref = "Monitor Watermill in realtime using Prometheus"
+++
Monitoring of Watermill may be performed by using decorators for publishers/subscribers and middlewares for handlers.
We provide a default implementation using Prometheus, based on the official [Prometheus client](https://github.com/prometheus/client_golang) for Go.
The `components/metrics` package exports `PrometheusMetricsBuilder`, which provides convenience functions to wrap publishers, subscribers and handlers so that they update the relevant Prometheus registry:
{{% load-snippet-partial file="src-link/components/metrics/builder.go" first_line_contains="// PrometheusMetricsBuilder" last_line_contains="func (b PrometheusMetricsBuilder)" %}}
## Wrapping publishers, subscribers and handlers
If you are using Watermill's [router](/docs/messages-router) (which is recommended in most cases), you can use a single convenience function `AddPrometheusRouterMetrics` to ensure that all the handlers added to this router are wrapped to update the Prometheus registry, together with their publishers and subscribers:
{{% load-snippet-partial file="src-link/components/metrics/builder.go" first_line_contains="// AddPrometheusRouterMetrics" last_line_contains="AddMiddleware" padding_after="1" %}}
Example use of `AddPrometheusRouterMetrics`:
{{% load-snippet-partial file="src-link/_examples/basic/4-metrics/main.go" first_line_contains="// we leave the namespace" last_line_contains="metricsBuilder.AddPrometheusRouterMetrics" %}}
In the snippet above, we have left the `namespace` and `subsystem` arguments empty. The Prometheus client library [uses these](https://godoc.org/github.com/prometheus/client_golang/prometheus#BuildFQName) to prefix the metric names. You may want to use namespace or subsystem, but be aware that this will impact the metric names and you will have to adjust the Grafana dashboard accordingly.
The `PrometheusMetricsBuilder` allows for custom configuration of histogram buckets by setting the `PublishBuckets` or `HandlerBuckets` field.
If `HandlerBuckets` is not provided, default watermill's values will be used, which are one order of magnitude smaller than default buckets (5ms~10s), because the handler execution times are typically shorter (µs~ms range).
For `PublishBuckets`, the default values are the same as the default Prometheus buckets (5ms~10s).
Standalone publishers and subscribers may also be decorated through the use of dedicated methods of `PrometheusMetricBuilder`:
{{% load-snippet-partial file="src-link/_examples/basic/4-metrics/main.go" first_line_contains="subWithMetrics, err := " last_line_contains="pubWithMetrics, err := " padding_after="3" %}}
## Exposing the /metrics endpoint
In accordance with how Prometheus works, the service needs to expose a HTTP endpoint for scraping. By convention, it is a GET endpoint, and its path is usually `/metrics`.
To serve this endpoint, there are two convenience functions, one using a previously created Prometheus Registry, while the other also creates a new registry:
{{% load-snippet-partial file="src-link/components/metrics/http.go" first_line_contains="// CreateRegistryAndServeHTTP" last_line_contains="func ServeHTTP(" %}}
Here is an example of its use in practice:
{{% load-snippet-partial file="src-link/_examples/basic/4-metrics/main.go" first_line_contains="prometheusRegistry, closeMetricsServer :=" last_line_contains="metricsBuilder.AddPrometheusRouterMetrics" %}}
## Example application
To see how the metrics dashboard works in practice, you can check out the [metrics example](https://github.com/ThreeDotsLabs/watermill/tree/master/_examples/basic/4-metrics).
Follow the instructions in the example's [README](https://github.com/ThreeDotsLabs/watermill/blob/master/_examples/basic/4-metrics/README.md) to make it run and add the Prometheus data source to Grafana.
## Grafana dashboard
We have prepared a [Grafana dashboard](https://grafana.com/grafana/dashboards/9777-watermill/) to use with the metrics implementation described above. It provides basic information about the throughput, failure rates and publish/handler durations.
If you want to check out the dashboard on your machine, you can use the [Example application](#example-application).
To find out more about the metrics that are exported to Prometheus, see [Exported metrics](#exported-metrics).
### Importing the dashboard
To import the Grafana dashboard, select Dashboard/Manage from the left menu, and then click on `+Import`.
Enter the dashboard URL https://grafana.com/dashboards/9777 (or just the ID, 9777), and click on Load.

Then select your Prometheus data source that scrapes the `/metrics` endpoint. Click on `Import`, and you're done!
## Exported metrics
Listed below are all the metrics that are registered on the Prometheus Registry by `PrometheusMetricsBuilder`.
For more information on Prometheus metric types, please refer to [Prometheus docs](https://prometheus.io/docs/concepts/metric_types).
| Object | Metric | Description | Labels/Values |
|---|---|---|---|
| Subscriber | subscriber_messages_received_total |
A Prometheus Counter. Counts the number of messages obtained by the subscriber. |
acked is either "acked" or "nacked". |
handler_name is set if the subscriber operates within a handler; "<no handler>" otherwise. |
|||
subscriber_name identifies the subscriber. If it implements fmt.Stringer, it is the result of `String()`, package.structName otherwise. |
|||
| Handler | handler_execution_time_seconds |
A Prometheus Histogram. Registers the execution time of the handler function wrapped by the middleware. |
handler_name is the name of the handler. |
success is either "true" or "false", depending on whether the wrapped handler function returned an error or not. |
|||
| Publisher | publish_time_seconds |
A Prometheus Histogram. Registers the time of execution of the Publish function of the decorated publisher. |
success is either "true" or "false", depending on whether the decorated publisher returned an error or not. |
handler_name is set if the publisher operates within a handler; "<no handler>" otherwise. |
|||
publisher_name identifies the publisher. If it implements fmt.Stringer, it is the result of `String()`, package.structName otherwise. |