client_golang: lenient version of promauto.Filter

15 views
Skip to first unread message

Chris Taylor

unread,
Apr 22, 2020, 5:04:02 PM4/22/20
to Prometheus Developers
Dear Prometheans,

posting this here because the Github issue template suggested discussing changes on the
mailing list first.

I just discovered the `promauto.With(Reigsterer)` API[1] today, which looked like a nice way
to ensure that metrics are registered with a Registerer when they are created.

In my use-case, the code receives the Registerer as a dependency.
Since it may be called multiple times, I'd like to be able to register metrics multiple times,
As an example, imagine writing a middleware to instrument an HTTP handler, similar to
package promhttp:

func InstrumentedHandler(r Registerer, next http.Handler) http.Handler {
  // register a bunch of metrics on `r` and return a Handler that
  // bumps them appropriately
}

Currently, my code uses prometheus.Register(), and checks whether the returned error is
a prometheus.AlreadyRegisteredError to get access to the previously-registered metric and
uses that instead.

It'd be easy enough to write a "lenient" version of promauto.Filter that uses this logic.
Would it make sense to add this to client_golang? If so, what do you think the API should
look like?

Thanks for your time!
Chris

Message has been deleted

Brian Brazil

unread,
Apr 23, 2020, 4:46:43 AM4/23/20
to Chris Taylor, Prometheus Developers
On Wed, 22 Apr 2020 at 22:04, 'Chris Taylor' via Prometheus Developers <prometheus...@googlegroups.com> wrote:
Dear Prometheans,

posting this here because the Github issue template suggested discussing changes on the
mailing list first.

I just discovered the `promauto.With(Reigsterer)` API[1] today, which looked like a nice way
to ensure that metrics are registered with a Registerer when they are created.

In my use-case, the code receives the Registerer as a dependency.
Since it may be called multiple times, I'd like to be able to register metrics multiple times,
As an example, imagine writing a middleware to instrument an HTTP handler, similar to
package promhttp:

It sounds like your instrumentation might be at the wrong level, have you tried instrumenting this at the http router instead?

Brian
 

func InstrumentedHandler(r Registerer, next http.Handler) http.Handler {
  // register a bunch of metrics on `r` and return a Handler that
  // bumps them appropriately
}

Currently, my code uses prometheus.Register(), and checks whether the returned error is
a prometheus.AlreadyRegisteredError to get access to the previously-registered metric and
uses that instead.

It'd be easy enough to write a "lenient" version of promauto.Filter that uses this logic.
Would it make sense to add this to client_golang? If so, what do you think the API should
look like?

Thanks for your time!
Chris

--
You received this message because you are subscribed to the Google Groups "Prometheus Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to prometheus-devel...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/prometheus-developers/97aa0973-1a26-4a16-af27-b1d926971c21%40googlegroups.com.


--

Chris Taylor

unread,
Apr 23, 2020, 4:08:51 PM4/23/20
to Brian Brazil, Prometheus Developers
On Thu, 23 Apr 2020 at 10:46, Brian Brazil <brian....@robustperception.io> wrote:
On Wed, 22 Apr 2020 at 22:04, 'Chris Taylor' via Prometheus Developers <prometheus...@googlegroups.com> wrote:
Dear Prometheans,

posting this here because the Github issue template suggested discussing changes on the
mailing list first.

I just discovered the `promauto.With(Reigsterer)` API[1] today, which looked like a nice way
to ensure that metrics are registered with a Registerer when they are created.

In my use-case, the code receives the Registerer as a dependency.
Since it may be called multiple times, I'd like to be able to register metrics multiple times,
As an example, imagine writing a middleware to instrument an HTTP handler, similar to
package promhttp:

It sounds like your instrumentation might be at the wrong level, have you tried instrumenting this at the http router instead?

sorry, I wasn't clear enough. The HTTP handler was just supposed to be a familiar stand-in for
"some library code that is instrumented with metrics that users instantiate multiple times".
The concrete use-case I was looking at when I wrote the email related to service discovery
that can be used in various contexts, but my question really applies to any time you'd want to
wrap some interface in a version with instrumentation.

Brian Brazil

unread,
Apr 23, 2020, 5:32:21 PM4/23/20
to chris....@soundcloud.com, Prometheus Developers
On Thu, 23 Apr 2020 at 21:08, Chris Taylor <christoph...@soundcloud.com> wrote:


On Thu, 23 Apr 2020 at 10:46, Brian Brazil <brian....@robustperception.io> wrote:
On Wed, 22 Apr 2020 at 22:04, 'Chris Taylor' via Prometheus Developers <prometheus...@googlegroups.com> wrote:
Dear Prometheans,

posting this here because the Github issue template suggested discussing changes on the
mailing list first.

I just discovered the `promauto.With(Reigsterer)` API[1] today, which looked like a nice way
to ensure that metrics are registered with a Registerer when they are created.

In my use-case, the code receives the Registerer as a dependency.
Since it may be called multiple times, I'd like to be able to register metrics multiple times,
As an example, imagine writing a middleware to instrument an HTTP handler, similar to
package promhttp:

It sounds like your instrumentation might be at the wrong level, have you tried instrumenting this at the http router instead?

sorry, I wasn't clear enough. The HTTP handler was just supposed to be a familiar stand-in for
"some library code that is instrumented with metrics that users instantiate multiple times".
The concrete use-case I was looking at when I wrote the email related to service discovery
that can be used in various contexts, but my question really applies to any time you'd want to
wrap some interface in a version with instrumentation.

It sounds then like you'd curry the registry with the relevant labels before passing it in then.

Brian
 
 

Brian
 

func InstrumentedHandler(r Registerer, next http.Handler) http.Handler {
  // register a bunch of metrics on `r` and return a Handler that
  // bumps them appropriately
}

Currently, my code uses prometheus.Register(), and checks whether the returned error is
a prometheus.AlreadyRegisteredError to get access to the previously-registered metric and
uses that instead.

It'd be easy enough to write a "lenient" version of promauto.Filter that uses this logic.
Would it make sense to add this to client_golang? If so, what do you think the API should
look like?

Thanks for your time!
Chris

--
You received this message because you are subscribed to the Google Groups "Prometheus Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to prometheus-devel...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/prometheus-developers/97aa0973-1a26-4a16-af27-b1d926971c21%40googlegroups.com.


--

Bjoern Rabenstein

unread,
Apr 23, 2020, 7:03:13 PM4/23/20
to Chris Taylor, Prometheus Developers
On 22.04.20 14:04, 'Chris Taylor' via Prometheus Developers wrote:
>
> Currently, my code uses prometheus.Register(), and checks whether the returned
> error is
> a prometheus.AlreadyRegisteredError to get access to the previously-registered
> metric and
> uses that instead.
>
> It'd be easy enough to write a "lenient" version of promauto.Filter that uses
> this logic.
> Would it make sense to add this to client_golang? If so, what do you think the
> API should
> look like?

When creating the promauto package, I was actually considering if it
should work that way by default, i.e. instead of creating a new
pre-registered collector, return the already existing one.

However, it appeared to prone to surprises. Also, as you can see from
Brian's answer, often you can avoid the multiple registrations by
structuring your instrumentation code differently. Currying the
registry with `WrapRegistererWith` is a possibility, see
https://pkg.go.dev/github.com/prometheus/client_golang/prometheus?tab=doc#WrapRegistererWith

Or you could use const labels.

There might be a few cases left where you would really like to test
for the double registration. That's exactly why the pattern with the
`AlreadyRegisteredError` exist. However, even that is not completely
uncontroversial. https://github.com/prometheus/prometheus/pull/7005 is
an example where such a case was discussed (without an outcome – the
discussion was postponed, because the PR was primarily about something
else).

Another point is that there was a function `RegisterOrGet` in previous
version and removed it, because the use case was so rare.

That sets the bar for a (re-)introduction of a similar feature quite
high.

--
Björn Rabenstein
[PGP-ID] 0x851C3DA17D748D03
[email] bjo...@rabenste.in
Reply all
Reply to author
Forward
0 new messages