Skip to content

Warn instead of failing on a blank source token, and when the Better Stack logger isn't used - #63

Merged
PetrHeinz merged 7 commits into
mainfrom
claude/boot-warning-blank-token
Oct 2, 2026
Merged

PetrHeinz merged 7 commits into
mainfrom
claude/boot-warning-blank-token

Conversation

@PetrHeinz

@PetrHeinz PetrHeinz commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Two ways a Rails app ends up without its logs in Better Stack, or fails to boot at all, without the gem saying why:

  • A missing or blank source token makes Logtail::Logger.create_default_logger raise ArgumentError: The source_token parameter cannot be blank while the app boots. This breaks assets:precompile in the Rails 8 Dockerfile, where the token isn't available. The red-team's bin/rails assets:precompile without the token aborted with exactly that error.
  • Following the setup docs on Rails 7.1 or newer, the generated config.logger = ActiveSupport::TaggedLogging.logger(STDOUT) in config/environments/production.rb runs after config/application.rb and replaces the Better Stack logger. Only Rails.event rows reach Better Stack, and nothing points at the cause. The same happens on Rails 6.1 and 7.0 with RAILS_LOG_TO_STDOUT. The red-team saw it on all seven Rails versions it tested; on 8.1, Rails.logger was a BroadcastLogger whose only broadcast was the STDOUT ActiveSupport::Logger.

What changes:

  • With a nil or blank token, create_default_logger returns the STDOUT logger it already uses in the test environment and with LOGTAIL_SKIP_LOGS, instead of raising. Those two cases don't need the token, so nothing changes for them.
  • Once the app has booted, the Railtie prints at most one warning to stderr:
    • if create_default_logger got a blank token;
    • otherwise, if it built a Better Stack logger but Rails.logger neither is a Logtail::Logger nor broadcasts to one. The warning names the likely cause: config.logger set again later, e.g. in config/environments/production.rb.
  • The check is registered from an initializer, so it runs after the app's own after_initialize blocks, which may still add the logger with broadcast_to.
  • Nothing is printed when the integration is turned off (config.logtail.enabled = false from Add config.logtail.enabled to turn the integration off per environment #60, or Logtail::Integrations::Rails.enabled = false). config/application.rb creates the logger in every environment, and e.g. development often has no token. That's why the blank-token warning waits until the app has booted instead of being printed by create_default_logger itself. It also means the warning appears once per boot, even when the logger is created twice.
  • On Rails < 7.1, ActiveSupport::Logger.broadcast extends the logger with an anonymous module that doesn't reveal its target. When such a module is present, the check stays quiet rather than risk a false warning.

Warnings go through Kernel#warn, never through the Logtail logger:

Logtail: the source token passed to Logtail::Logger.create_default_logger is blank, logging to STDOUT instead of sending logs to Better Stack.
Logtail: Rails.logger isn't the Better Stack logger created by Logtail::Logger.create_default_logger, and doesn't broadcast to it, so your logs don't reach Better Stack. Most likely config.logger is set again later, e.g. in config/environments/production.rb.

Behaviour and compatibility:

  • A blank token no longer raises. Apps that relied on the exception to catch a missing token get the warning and STDOUT logging instead.
  • A blank token passed after boot, e.g. in a console, gives the STDOUT logger without a warning.
  • Apps with the documented setup, or that broadcast to the Better Stack logger, see no change.

The boot cases run in subprocesses, because booting patches Rails classes for the whole process: a missing token, an empty token, a missing token with the integration turned off, an overridden config.logger, a broadcast to the Better Stack logger (BroadcastLogger on Rails 7.1+, ActiveSupport::Logger.broadcast before), and the documented setup.

This targets the 0.3.0 minor; merge after 0.2.15 is released. No dependencies.

The commit "Expect real response durations in the request specs" is the same change as in #59: logtail-rack 0.2.8 times responses with the monotonic clock, which Timecop doesn't freeze, so three existing specs now fail on every branch based on main. It's identical in both PRs, so it merges cleanly whichever lands first.

The first commit only adds the tests and is expected to fail on CI. The implementation follows in the later commits.

🤖 Generated with Claude Code

PetrHeinz and others added 6 commits October 1, 2026 18:50
A nil or empty token makes create_default_logger raise while the app
boots, which breaks e.g. assets:precompile in a Docker build without
the token. And when config.logger is set again after
config/application.rb, as the generated production.rb does on Rails 7.1
and newer, nothing tells the app that its logs no longer reach Better
Stack.

The boot cases run in subprocesses, since booting patches Rails classes
for the whole process.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Logtail:" also matches "Logtail::Logger" inside the warnings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Stack logger isn't used

create_default_logger with a nil or blank token prints a warning and
returns the STDOUT logger it uses in the test environment and with
LOGTAIL_SKIP_LOGS, so the app still boots.

Once the app has booted, the Railtie warns when create_default_logger
built a Better Stack logger but Rails.logger neither is a
Logtail::Logger nor broadcasts to one, which is what happens when
config.logger is set again in config/environments/production.rb.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
config/application.rb creates the logger in every environment, so an
environment that turns the integration off, e.g. development with
config.logtail.enabled = false, may well have no token. The empty
string case now boots an app too, like the missing token.

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

create_default_logger now only records the blank token, and the
Railtie's after-boot check prints the warning, so it appears once per
boot even if the logger is created twice, and not in environments that
turn the integration off and don't need the token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@PetrHeinz
PetrHeinz marked this pull request as ready for review October 2, 2026 09:22
@PetrHeinz
PetrHeinz merged commit 20b394c into main Oct 2, 2026
66 checks passed
@PetrHeinz
PetrHeinz deleted the claude/boot-warning-blank-token branch October 2, 2026 14: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