Skip to content

Commit

Permalink
issue #572: add Listen.adapter_warn_behavior =
Browse files Browse the repository at this point in the history
  • Loading branch information
ColinDKelley committed Feb 24, 2024
1 parent 0f600f3 commit eea2891
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 52 deletions.
35 changes: 31 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ Note: `:only` regexp patterns are evaluated only against relative **file** paths

All the following options can be set through the `Listen.to` after the directory path(s) params.

```ruby
``` ruby
ignore: [%r{/foo/bar}, /\.pid$/, /\.coffee$/] # Ignore a list of paths
# default: See DEFAULT_IGNORED_FILES and DEFAULT_IGNORED_EXTENSIONS in Listen::Silencer

Expand Down Expand Up @@ -187,7 +187,7 @@ This is the primary method of debugging.

### Custom Logger
You can call `Listen.logger =` to set a custom `listen` logger for the process. For example:
```
``` ruby
Listen.logger = Rails.logger
```

Expand All @@ -197,7 +197,7 @@ If no custom logger is set, a default `listen` logger which logs to to `STDERR`
The default logger defaults to the `error` logging level (severity).
You can override the logging level by setting the environment variable `LISTEN_GEM_DEBUGGING=<level>`.
For `<level>`, all standard `::Logger` levels are supported, with any mix of upper-/lower-case:
```
``` ruby
export LISTEN_GEM_DEBUGGING=debug # or 2 [deprecated]
export LISTEN_GEM_DEBUGGING=info # or 1 or true or yes [deprecated]
export LISTEN_GEM_DEBUGGING=warn
Expand All @@ -210,9 +210,36 @@ Note: The alternate values `1`, `2`, `true` and `yes` shown above are deprecated

### Disabling Logging
If you want to disable `listen` logging, set
```
``` ruby
Listen.logger = ::Logger.new('/dev/null')
```

### Adapter Warnings
If listen is having trouble with the underlying adapter, it will display warnings with `Kernel#warn` by default,
which in turn writes to STDERR.
Sometimes this is not desirable, for example in an environment where STDERR is ignored.
For these reasons, the behavior can be configured using `Listen.adapter_warn_behavior =`:
``` ruby
Listen.adapter_warn_behavior = :warn # default (true means the same)
Listen.adapter_warn_behavior = :log # send to logger.warn
Listen.adapter_warn_behavior = :silent # suppress all adapter warnings (nil or false mean the same)
```
Also there are some cases where specific warnings are not helpful.
For example, if you are using the polling adapter--and expect to--you can suppress the warning about it
by providing a callable object like a lambda or proc that determines the behavior based on the `message`:
``` ruby
Listen.adapter_warn_behavior = ->(message) do
case message
when /Listen will be polling for changes/
:silent
when /directory is already being watched/
:log
else
:warn
end
end
```

## Listen Adapters

The `Listen` gem has a set of adapters to notify it when there are changes.
Expand Down
29 changes: 29 additions & 0 deletions lib/listen/logger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,27 @@ module Listen
# Listen.logger will always be present.
# If you don't want logging, set Listen.logger = ::Logger.new('/dev/null', level: ::Logger::UNKNOWN)

@adapter_warn_behavior = :warn

class << self
attr_writer :logger
attr_accessor :adapter_warn_behavior

def logger
@logger ||= default_logger
end

def adapter_warn(message)
case adapter_warn_behavior_callback(message)
when :log
logger.warn(message)
when :silent, nil, false
# do nothing
else # :warn
warn(message)
end
end

private

def default_logger
Expand All @@ -32,5 +46,20 @@ def default_logger

::Logger.new(STDERR, level: level)
end

def adapter_warn_behavior_callback(message)
if adapter_warn_behavior.respond_to?(:call)
case behavior = adapter_warn_behavior.call(message)
when Symbol
behavior
when false, nil
:silent
else
:warn
end
else
adapter_warn_behavior
end
end
end
end
188 changes: 140 additions & 48 deletions spec/lib/listen/logger_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@

require 'listen/logger'

RSpec.describe 'Listen.logger' do
ENV_VARIABLE_NAME = 'LISTEN_GEM_DEBUGGING'

let(:logger) { instance_double(::Logger, "logger") }

RSpec.describe 'logger.rb' do
around do |spec|
orig_logger = Listen.instance_variable_get(:@logger)

Expand All @@ -15,71 +11,167 @@
Listen.logger = orig_logger
end

around do |spec|
orig_debugging_env_variable = ENV.fetch(ENV_VARIABLE_NAME, :not_set)
describe 'Listen.logger' do
ENV_VARIABLE_NAME = 'LISTEN_GEM_DEBUGGING'

spec.run
let(:logger) { instance_double(::Logger, "logger") }

around do |spec|
orig_debugging_env_variable = ENV.fetch(ENV_VARIABLE_NAME, :not_set)

if orig_debugging_env_variable == :not_set
ENV.delete(ENV_VARIABLE_NAME)
else
ENV[ENV_VARIABLE_NAME] = orig_debugging_env_variable
spec.run

if orig_debugging_env_variable == :not_set
ENV.delete(ENV_VARIABLE_NAME)
else
ENV[ENV_VARIABLE_NAME] = orig_debugging_env_variable
end
end
end

describe 'logger=' do
it 'allows the logger to be set' do
Listen.logger = logger
expect(Listen.logger).to be(logger)
describe 'logger=' do
it 'allows the logger to be set' do
Listen.logger = logger
expect(Listen.logger).to be(logger)
end

it 'allows nil to be set (implying default logger)' do
Listen.logger = nil
expect(Listen.logger).to be_kind_of(::Logger)
end
end

it 'allows nil to be set (implying default logger)' do
Listen.logger = nil
expect(Listen.logger).to be_kind_of(::Logger)
describe 'logger' do
before do
Listen.instance_variable_set(:@logger, nil)
end

it 'returns default logger if none set' do
expect(Listen.logger).to be_kind_of(::Logger)
end

['debug', 'DEBUG', '2', 'level2', '2 '].each do |env_value|
it "infers DEBUG level from #{ENV_VARIABLE_NAME}=#{env_value.inspect}" do
ENV[ENV_VARIABLE_NAME] = env_value
expect(Listen.logger.level).to eq(::Logger::DEBUG)
end
end

['info', 'INFO', 'true', ' true', 'TRUE', 'TRUE ', 'yes', 'YES', ' yesss!', '1', 'level1'].each do |env_value|
it "infers INFO level from #{ENV_VARIABLE_NAME}=#{env_value.inspect}" do
ENV[ENV_VARIABLE_NAME] = env_value
expect(Listen.logger.level).to eq(::Logger::INFO)
end
end

['warn', 'WARN', ' warn', 'warning'].each do |env_value|
it "infers WARN level from #{ENV_VARIABLE_NAME}=#{env_value.inspect}" do
ENV[ENV_VARIABLE_NAME] = env_value
expect(Listen.logger.level).to eq(::Logger::WARN)
end
end

['error', 'ERROR', 'OTHER'].each do |env_value|
it "infers ERROR level from #{ENV_VARIABLE_NAME}=#{env_value.inspect}" do
ENV[ENV_VARIABLE_NAME] = env_value
expect(Listen.logger.level).to eq(::Logger::ERROR)
end
end

['fatal', 'FATAL', ' fatal'].each do |env_value|
it "infers FATAL level from #{ENV_VARIABLE_NAME}=#{env_value.inspect}" do
ENV[ENV_VARIABLE_NAME] = env_value
expect(Listen.logger.level).to eq(::Logger::FATAL)
end
end
end
end

describe 'logger' do
before do
Listen.instance_variable_set(:@logger, nil)
describe 'Listen.adapter_warn_behavior' do
subject { Listen.adapter_warn(message) }

after do
Listen.adapter_warn_behavior = :warn
end
let(:message) { "warning message" }

it 'returns default logger if none set' do
expect(Listen.logger).to be_kind_of(::Logger)
it 'defaults to :warn' do
expect(Listen.adapter_warn_behavior).to eq(:warn)

expect(Listen).to receive(:warn).with(message)

subject
end

['debug', 'DEBUG', '2', 'level2', '2 '].each do |env_value|
it "infers DEBUG level from #{ENV_VARIABLE_NAME}=#{env_value.inspect}" do
ENV[ENV_VARIABLE_NAME] = env_value
expect(Listen.logger.level).to eq(::Logger::DEBUG)
end
it 'allows the adapter_warn_behavior to be set to :log' do
Listen.adapter_warn_behavior = :log

expect(Listen.logger).to receive(:warn).with(message)

subject
end

['info', 'INFO', 'true', ' true', 'TRUE', 'TRUE ', 'yes', 'YES', ' yesss!', '1', 'level1'].each do |env_value|
it "infers INFO level from #{ENV_VARIABLE_NAME}=#{env_value.inspect}" do
ENV[ENV_VARIABLE_NAME] = env_value
expect(Listen.logger.level).to eq(::Logger::INFO)
[:silent, nil, false].each do |behavior|
it "allows the adapter_warn_behavior to be set to #{behavior} to silence the warnings" do
Listen.adapter_warn_behavior = behavior

expect(Listen.logger).not_to receive(:warn)
expect(Listen).not_to receive(:warn)

subject
end
end

['warn', 'WARN', ' warn', 'warning'].each do |env_value|
it "infers WARN level from #{ENV_VARIABLE_NAME}=#{env_value.inspect}" do
ENV[ENV_VARIABLE_NAME] = env_value
expect(Listen.logger.level).to eq(::Logger::WARN)
context 'when adapter_warn_behavior is set to a callable object like a proc' do
before do
Listen.adapter_warn_behavior = ->(message) do
case message
when /USE warn/
:warn
when /USE log/
:log
when /USE silent/
:silent
when /USE false/
false
when /USE nil/
nil
else
true
end
end
end
end

['error', 'ERROR', 'OTHER'].each do |env_value|
it "infers ERROR level from #{ENV_VARIABLE_NAME}=#{env_value.inspect}" do
ENV[ENV_VARIABLE_NAME] = env_value
expect(Listen.logger.level).to eq(::Logger::ERROR)
[true, :warn].each do |behavior|
context "when the message matches a #{behavior.inspect} pattern" do
let(:message) { "USE #{behavior.inspect}" }
it 'respects :warn' do
expect(Listen).to receive(:warn).with(message)

subject
end
end
end
end

['fatal', 'FATAL', ' fatal'].each do |env_value|
it "infers FATAL level from #{ENV_VARIABLE_NAME}=#{env_value.inspect}" do
ENV[ENV_VARIABLE_NAME] = env_value
expect(Listen.logger.level).to eq(::Logger::FATAL)
context 'when the message matches a :silent pattern' do
let(:message) { "USE silent" }
it 'respects :silent' do
expect(Listen).not_to receive(:warn).with(message)
expect(Listen).not_to receive(:warn)

subject
end
end

[false, nil].each do |behavior|
context 'when the message matches a #{behavior} pattern' do
let(:message) { "USE #{behavior.inspect}" }
it 'respects :silent' do
expect(Listen).not_to receive(:warn).with(message)
expect(Listen).not_to receive(:warn)

subject
end
end
end
end
end
Expand Down

0 comments on commit eea2891

Please sign in to comment.