Pass the registry to the text format in Rack::Exporter

What does this MR do and why?

When Prometheus::Client.configuration.value_class is a class whose multiprocess returns false, such as Prometheus::Client::SimpleValue, every request Prometheus::Client::Rack::Exporter would answer with the metrics raises ArgumentError: wrong number of arguments (given 0, expected 1) instead. On that branch Exporter#respond_with calls format.marshal with no argument, and Formats::Text.marshal takes the registry it renders as a required argument. The default value class, MmapedValue, is multiprocess and takes the other branch, so only an application that configures another value class runs into it.

The argument has been missing since cde3e544 brought the non-multiprocess branch back in 2017, and nothing caught it because spec/prometheus/client/rack/exporter_spec.rb had been switched to xdescribe in b8a23764 13 days earlier, and still is.

This MR has two commits:

  1. Pass the registry to the text format in Rack::Exporter. respond_with now calls format.marshal(@registry), the registry the middleware already keeps: the one given as registry:, or Prometheus::Client.registry by default. A new spec requests /metrics with the value class stubbed to SimpleValue and expects the given registry's counter in the text format. It fails on master with the ArgumentError and passes with the change.
  2. Run the Rack::Exporter spec again. describe in place of xdescribe, the value class stubbed to SimpleValue as collector_spec.rb does (with the default MmapedValue the exporter renders the multiprocess files, not the registry the spec builds), and last_response.headers in place of last_response.header, which Rack 3.1 removed. Its ten examples cover the content negotiation: a 200 in the text format for each Accept header the exporter can serve, a 406 for each one it cannot, and the application's own response on any other path. The seven that expect the metrics fail on master with the same ArgumentError, and all ten pass with the first commit. The example from the first commit moves into this block, since it now has the same setup, and stays because it pins the body as text where the other examples compare it with what Formats::Text.marshal renders for the same registry.

Each commit passes the whole suite on its own, on the four Ruby versions the ruby CI job runs (details below).

Closes #81 (closed)

What this does not change

With the fix, counters and gauges render. A registry holding a histogram or a summary still fails, one step later and inside Formats::Text itself: a histogram with the NoMethodError described in #79 (closed), which !299 (merged) fixes, and a summary with a NoMethodError for map on a Float, because Summary#values still hands out only the sum, as #77 (closed) describes (closed in August for inactivity, not fixed). Those are defects of the text format rather than of the exporter, and the format is what Prometheus::Client::Push uses too, so I kept them out of this MR. The multiprocess branch is untouched.

I did not add a CHANGELOG.md entry, since the entries are nearly always written at release time (as for !245 (merged) and !280 (merged)), but I can add one under Unreleased if you prefer, for example: Fix Rack::Exporter raising ArgumentError when the value class is not multiprocess.

The exporter spec on master and on this branch

With spec/prometheus/client/rack/exporter_spec.rb from this branch and lib/prometheus/client/rack/exporter.rb from master b4cf84b, Ruby 3.4.11 (3.2.11, 3.3.12 and 4.0.6 give the same 8 failures):

$ bundle exec rspec --format documentation spec/prometheus/client/rack/exporter_spec.rb
Prometheus::Client::Rack::Exporter
  when requesting app endpoints
    returns the app response
  when requesting /metrics
    responds with the metrics of the given registry in the text format (FAILED - 1)
    when client does not send a Accept header
      responds with 200 OK and Content-Type text/plain; version=0.0.4 (FAILED - 2)
    when client accpets any media type
      responds with 200 OK and Content-Type text/plain; version=0.0.4 (FAILED - 3)
    when client requests application/json
      responds with 406 Not Acceptable
    when client requests text/plain
      responds with 200 OK and Content-Type text/plain; version=0.0.4 (FAILED - 4)
    when client uses different white spaces in Accept header
      responds with 200 OK and Content-Type text/plain; version=0.0.4 (FAILED - 5)
    when client does not include quality attribute
      responds with 200 OK and Content-Type text/plain; version=0.0.4 (FAILED - 6)
    when client accepts some unknown formats
      responds with 200 OK and Content-Type text/plain; version=0.0.4 (FAILED - 7)
    when client accepts only unknown formats
      responds with 406 Not Acceptable
    when client accepts unknown formats and wildcard
      responds with 200 OK and Content-Type text/plain; version=0.0.4 (FAILED - 8)

Failures:

  1) Prometheus::Client::Rack::Exporter when requesting /metrics responds with the metrics of the given registry in the text format
     Failure/Error:
[...]
     ArgumentError:
       wrong number of arguments (given 0, expected 1)
     # ./lib/prometheus/client/formats/text.rb:17:in 'marshal'
     # ./lib/prometheus/client/rack/exporter.rb:68:in 'Prometheus::Client::Rack::Exporter#respond_with'
     # ./lib/prometheus/client/rack/exporter.rb:27:in 'Prometheus::Client::Rack::Exporter#call'
[...]
11 examples, 8 failures

With this branch, on the same four versions:

$ bundle exec rspec --format documentation spec/prometheus/client/rack/exporter_spec.rb
[...]
11 examples, 0 failures

The first commit's spec file alone (its own example, the other ten still under xdescribe) gives 11 examples, 1 failure, 10 pending against master's exporter.rb with the same ArgumentError, and 11 examples, 0 failures, 10 pending with the first commit.

The whole suite and RuboCop, per commit

bundle exec rake spec with prometheus_multiproc_dir=tmp/ and SKIP_C_EXTENSION_TESTING=1, as the ruby job runs it, after bundle exec rake compile with Rust 1.89.0, on Ruby 3.2.11, 3.3.12, 3.4.11 and 4.0.6 (the same counts on all four):

Tree Result
master 179 examples, 0 failures, 20 pending
first commit 180 examples, 0 failures, 20 pending
second commit 180 examples, 0 failures, 10 pending

bundle exec rubocop with the project's .rubocop.yml stops with exit 2 on master and on both commits before inspecting anything, because RuboCop 1.75.6 (the version Gemfile.lock pins) refuses its AlignHash and Style/TrailingCommaInLiteral keys as obsolete. With a copy of the config that renames them as the error asks (Layout/HashAlignment, Style/TrailingCommaInArrayLiteral and Style/TrailingCommaInHashLiteral), over lib, spec, examples, Rakefile and the gemspec, master and each commit have 288 offenses, five of them in the two files this MR touches, and they are the same five: the second commit only lengthens the two blocks of the exporter spec that Metrics/BlockLength already reported (68 to 77 lines and 51 to 57).

Merge request reports

Loading
Loading