Own-and-Ship / oas_agent

The Own & Ship Ruby agent
2 stars 0 forks source link

Enabling Ruby 2.7+ Warning categories at runtime #43

Open caius opened 6 months ago

caius commented 6 months ago

Been experimenting in our application with not adding RUBYOPT=-W2 but trying to enable the Warning categories at runtime instead. Makes it easier for me to dis/enable warnings per-environment for … Reasons™.

https://www.fastruby.io/blog/exploring-ruby-warnings.html has been quite useful in figuring this out as well. Seems it's not enough to just have the categories enabled, but $VERBOSE needs to be set to true as well.

These are all run on Ruby 3.2 for now (Ruby 3.3 brings a :performance category in as well.) To see whether Warning.warn has been called, I'm overwriting it directly to dump output. To trigger a warning, we're defining the foo method twice as well at the end.

Starting with defaults, we see no ruby warnings emitted:

$ ruby -e 'module Warning; def warn(*a); p a; end; end' \
  -e 'puts "RUBY_VERSION: #{RUBY_VERSION}", "$VERBOSE: #{$VERBOSE.inspect}", "Warning[:deprecated]: #{Warning[:deprecated].inspect}", "Warning[:experimental]: #{Warning[:experimental].inspect}"' \
  -e 'def foo; end; def foo; end'
RUBY_VERSION: 3.2.3
$VERBOSE: false
Warning[:deprecated]: false
Warning[:experimental]: true

Adding -W2 to the mix does what we expect (and tells us we've redefined Warning.warn too). We get warnings output for foo method redefinition, and also see both Warning categories and $VERBOSE get set to true.

$ ruby -W2 \
  -e 'module Warning; def warn(*a); p a; end; end' \
  -e 'puts "RUBY_VERSION: #{RUBY_VERSION}", "$VERBOSE: #{$VERBOSE.inspect}", "Warning[:deprecated]: #{Warning[:deprecated].inspect}", "Warning[:experimental]: #{Warning[:experimental].inspect}"' \
  -e 'def foo; end; def foo; end'
  -e:1: warning: method redefined; discarding old warn
RUBY_VERSION: 3.2.3
$VERBOSE: true
Warning[:deprecated]: true
Warning[:experimental]: true
["-e:3: warning: method redefined; discarding old foo\n"]
["-e:3: warning: previous definition of foo was here\n"]

Perhaps we can replicate this at runtime? First try setting just the Warning deprecated category to true, we want warnings in that category to be output after all. We observe the category being changed but no warning is output.

$ ruby -e 'module Warning; def warn(*a); p a; end; end' \
  -e 'Warning[:deprecated] = true' -e 'puts "RUBY_VERSION: #{RUBY_VERSION}", "$VERBOSE: #{$VERBOSE.inspect}", "Warning[:deprecated]: #{Warning[:deprecated].inspect}", "Warning[:experimental]: #{Warning[:experimental].inspect}"' \
  -e 'def foo; end; def foo; end'
RUBY_VERSION: 3.2.3
$VERBOSE: false
Warning[:deprecated]: true
Warning[:experimental]: true

Given $VERBOSE is mentioned in Fast Ruby's blog post and -W2 sets it to true, what if we also set that and the deprecated category at runtime. Seems that's the magic trick, we end up with warnings being output where we expect without having to pass -W2 at invocation time.

$ ruby -e 'module Warning; def warn(*a); p a; end; end' \
  -e '$VERBOSE = true; Warning[:deprecated] = true' \
  -e 'puts "RUBY_VERSION: #{RUBY_VERSION}", "$VERBOSE: #{$VERBOSE.inspect}", "Warning[:deprecated]: #{Warning[:deprecated].inspect}", "Warning[:experimental]: #{Warning[:experimental].inspect}"' \
  -e 'def foo; end; def foo; end'
RUBY_VERSION: 3.2.3
$VERBOSE: true
Warning[:deprecated]: true
Warning[:experimental]: true
["-e:4: warning: method redefined; discarding old foo\n"]
["-e:4: warning: previous definition of foo was here\n"]
caius commented 4 months ago

I had this wired up in a Rails 6.1 & 7.0 app by adding the following to the config/boot.rb file:

require "oas_agent" # Load O&S Agent before other dependencies

And then in config/application.rb inside the application class definition I had

# Enable ruby warnings if OAS is wired up in this environment
if OasAgent::AgentContext.config[:enabled]
  # Same as setting RUBYOPT="-W2" in environment
  $VERBOSE = true
  Warning[:deprecated] = true
end