Skip to content

Propagate context.Context through Gather and Collect #1538

Open
@ringerc

Description

@ringerc

I propose new variants of Gatherer and Collector that support golang's context.Context propagation through the request chain to provide a means of passing timeout and request information through to collectors.

This would greatly enhance the capabilities of promhttp middleware and make it much easier (and safer) to handle client-supplied timeout headers, query-params passed to a scrape endpoint, etc.

For example, a metrics endpoint that wishes to support query parameters for selective scraping currently has a difficult time capturing this and passing this through the layers of indirection. It's difficult to pass contextual info through the promhttp.Handler -> prometheus.Gatherer (possibly a Registry) -> prometheus.Collector chain. Especially since promhttp.HandlerForTransactional and the rest of promhttp offers few points of extension. This can be worked around by having an application's generic http handler create a new Prometheus handler, registry, etc in a closure for each request then delegate to it, but that can be inefficient and it loses cross-scrape-persistent instrumentation state.

If a context.Context was propagated from the promhttp.Handler through the layers, applications could pass relevant details transparently through Prometheus without exposing protocol-specifics etc to the Prometheus APIs.

I'm sure there are less intrusive and smarter ways to do this than I can come up with, which is:

  • Add variants of prometheus.Gatherer, prometheus.Gatherers, prometheus.Collector that accept a context.Context argument to Gather(...), Collect(...) etc. Expose Registry API to accept them. Methods aren't added to the existing interfaces as that would break API compatibility with existing implementations.
  • In promhttp.HandlerForTransactional, read the context.Context from the http.Request with Request.Context()
  • Propagate it through HandlerForTransactional's call Gather() as e.g. GatherWithContext(ctx)
  • Propagate it through the context-aware Gatherer as Collect(ctx) calls on the Collector(s)
  • The prometheus.CollectorWithContext (or whatever) implementations accept Collect(ctx context.context, ch chan<- prometheus.Metric) so they can use ctx.Value(...) to access contextual info, check a propagated timeout or cancellation function, etc.

This would also allow the documented limitations around timeout handing in promhttp to be fixed, because a context.WithTimeout(...) could be passed. promhttp could also automatically handle any client-supplied X-Prometheus-Scrape-Timeout-Seconds header; currently it has no way to propagate this knowledge down through the gatherer to the collectors.

It'd also be convenient to have a promhttp.WithContextFunc(...) that accepts a func(r *http.Request) context.Context argument and returns an Option. Akin to WithLabelFromCtx. So the app doesn't have to implement its own http.Handler to delegate to Prometheus's, it can use a Prometheus-supplied wrapper.

Maybe there's a sensible way to do this now. Or maybe it's reasonable to just make a new registry etc for each scrape, and just ensure that any more expensive application state is well separated from the prometheus API implementations so the app's Registry, its Collector instances etc are one-shot and request-specific. But that doesn't work well with the instrumentation wrappers in promhttp, and it's hard to see what would.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions