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:
- Pass the registry to the text format in Rack::Exporter.
respond_withnow callsformat.marshal(@registry), the registry the middleware already keeps: the one given asregistry:, orPrometheus::Client.registryby default. A new spec requests/metricswith the value class stubbed toSimpleValueand expects the given registry's counter in the text format. It fails on master with theArgumentErrorand passes with the change. - Run the Rack::Exporter spec again.
describein place ofxdescribe, the value class stubbed toSimpleValueascollector_spec.rbdoes (with the defaultMmapedValuethe exporter renders the multiprocess files, not the registry the spec builds), andlast_response.headersin place oflast_response.header, which Rack 3.1 removed. Its ten examples cover the content negotiation: a 200 in the text format for eachAcceptheader 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 sameArgumentError, 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 whatFormats::Text.marshalrenders 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 failuresWith this branch, on the same four versions:
$ bundle exec rspec --format documentation spec/prometheus/client/rack/exporter_spec.rb
[...]
11 examples, 0 failuresThe 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).