Skip to content

Keep Rails requests and their logs working with json 3 on Rails 8.0 and older - #62

Merged
PetrHeinz merged 6 commits into
mainfrom
claude/json-generate
Oct 2, 2026
Merged

PetrHeinz merged 6 commits into
mainfrom
claude/json-generate

Conversation

@PetrHeinz

@PetrHeinz PetrHeinz commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

With json ≥ 3 on Rails 8.0 and older (Rails 7.2 before 7.2.4), every request of a Rails app using logtail-rails fails with a 500 and nothing is logged. ActiveSupport's encoder passes quirks_mode: to JSON.generate whenever to_json is called directly, and json 3 rejects unknown options. Evidence: Rails 7.1 and 8.0 apps with json 3.0.2 answered 500 to every request, and in the new spec here a request raises ArgumentError: unknown keyword: :quirks_mode from logtail-rack/http_request.rb:29.

logtail-rails itself has no to_json call in lib. All the calls on this path are in the gems it depends on:

So this PR adds the Rails-level regression test and, at merge time, the dependency floors:

  • A spec dispatches a request and a raising request through the whole middleware stack, with ActiveSupport::JSON.encode stubbed to raise the way json 3 makes it raise. That covers every Rails version in CI, json 3 or not. It expects a 200, the request, controller call, render and response events with parseable headers_json and params_json, and an error event with backtrace_json, while the app's own exception gets through unchanged. Today the logger's ArgumentError replaces it.
  • A third example logs a Time and NaN through Rails.logger with that encoder raising, and expects what ActiveSupport's to_json wrote before: "2016-09-01T12:00:00.000Z" and null. Plain JSON.generate would write Time#to_s and raise on NaN; logtail-ruby#53 converts with as_json first. There is no to_json or JSON.generate call in logtail-rails' own lib to convert the same way.
  • On TruffleRuby, json's pure-Ruby generator writes Symbols and other non-JSON values with a stateless to_s.to_json, which goes back through ActiveSupport's encoder. The spec caught that on the TruffleRuby legs (the log level is a Symbol), and logtail-ruby#53 now converts with as_json before generating.
  • The spec raises exceptions instead of rendering them for its own examples (show_exceptions = :none on Rails 7.1+), because since Rails 7.2 the spec app's false means :all.
  • gemfiles/rails-7.1.gemfile and rails-8.0.gemfile keep their json < 3 pin. The spec app's own render json: hits the same ActiveSupport bug, so the stub is what covers json 3 there.

Behaviour and compatibility, once the fixed gems are in: headers_json, params_json and backtrace_json no longer escape HTML entities, so <, > and & (and U+2028/U+2029) stay as they are instead of \u003c, \u003e, \u0026. The JSON means the same. Everything else reads as before: logtail-ruby#53 converts values with as_json first, as ActiveSupport's to_json did, so times stay ISO 8601, NaN stays null and file uploads in params_json keep their hash form.

Targets the logtail-rails 0.2.15 patch release. It depends on logtail 0.1.21 (logtail-ruby#53) and logtail-rack 0.2.9 (logtail-ruby-rack#28):

  • The TEMP: run CI against the logtail and logtail-rack branches until 0.1.21 and 0.2.9 are released commit points the root Gemfile and every gemfiles/*.gemfile at their claude/json-generate branches, so CI exercises the real fixes.
  • At merge time, after both releases, replace that commit with logtail ~> 0.1, >= 0.1.21 and logtail-rack ~> 0.2, >= 0.2.9 in the gemspec, so that updating logtail-rails pulls in the fixed gems.

Commits and CI:

  • Expect real response durations in the request specs is unrelated test maintenance that every logtail-rails PR needs right now: logtail-rack 0.2.8 times responses with the monotonic clock, which Timecop doesn't freeze, so three specs expecting "in 0.0ms" fail on every CI leg. It's identical to the same commit in Stop logging "Rendering" twice per template at debug level on Rails 7.2 to 8.1 #59, so the two merge cleanly in either order.
  • The tests commit is expected to be red on every CI leg. The TEMP commit follows once both dependency branches carry their fixes, and the Time/NaN test is a follow-up commit on top.

🤖 Generated with Claude Code

PetrHeinz and others added 2 commits October 1, 2026 18:43
logtail-rack 0.2.8 times responses with the monotonic clock, which Timecop
doesn't freeze, so "Completed 200 OK in 0.0ms" no longer holds. The last CI
run on main still resolved logtail-rack 0.2.7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pport 8.0

With json 3, ActiveSupport 8.0 and older raise on every direct to_json
call, because their encoder passes quirks_mode: to JSON.generate. The gems
below logtail-rails call to_json for headers_json (logtail-rack), params_json,
backtrace_json and each JSON log line (logtail), so every request fails with
ArgumentError and nothing is logged. The tests stub ActiveSupport::JSON.encode
to raise the same way on every Rails version in CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PetrHeinz and others added 4 commits October 1, 2026 18:51
…21 and 0.2.9 are released

Points the root Gemfile and every gemfiles/*.gemfile at the json-generate
branches of logtail-ruby (logtail-ruby#53) and logtail-ruby-rack
(logtail-ruby-rack#28), which replace to_json with JSON.generate.

At merge time, after both releases, replace this commit with
logtail ~> 0.1, >= 0.1.21 and logtail-rack ~> 0.2, >= 0.2.9 in the gemspec.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
logtail-ruby#53 converts with as_json before JSON.generate, so with json 3
a Time keeps ActiveSupport's ISO 8601 format and NaN still becomes null.
Plain JSON.generate would write Time#to_s and raise on NaN. The test logs
both through Rails.logger with ActiveSupport's encoder raising and expects
what ActiveSupport's to_json produced before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…til 0.1.21 and 0.2.9 are released"

This reverts commit 85f4056.
@PetrHeinz
PetrHeinz marked this pull request as ready for review October 2, 2026 12:13
@PetrHeinz
PetrHeinz merged commit 4512399 into main Oct 2, 2026
66 checks passed
@PetrHeinz
PetrHeinz deleted the claude/json-generate branch October 2, 2026 12:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant