Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #572: make kernel warn configurable #579

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 33 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,38 @@ 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
```
In cases where the `Listen` gem is embedded inside another service--such as `guard`--the above configuration
can be set in the environment variable `LISTEN_GEM_ADAPTER_WARN_BEHAVIOR=warn|log|silent`.

## 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 ENV['LISTEN_GEM_ADAPTER_WARN_BEHAVIOR']&.to_sym || 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
2 changes: 1 addition & 1 deletion lib/listen/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Listen
VERSION = '3.8.0'
VERSION = '3.9.0'
end
220 changes: 173 additions & 47 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,201 @@
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") }

if orig_debugging_env_variable == :not_set
ENV.delete(ENV_VARIABLE_NAME)
else
ENV[ENV_VARIABLE_NAME] = orig_debugging_env_variable
around do |spec|
orig_debugging_env_variable = ENV.fetch(ENV_VARIABLE_NAME, :not_set)

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 'defaults to :warn' do
expect(Listen.adapter_warn_behavior).to eq(:warn)

it 'returns default logger if none set' do
expect(Listen.logger).to be_kind_of(::Logger)
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 LISTEN_GEM_ADAPTER_WARN_BEHAVIOR is set to 'log'" do
around do |spec|
orig_debugging_env_variable = ENV.fetch('LISTEN_GEM_ADAPTER_WARN_BEHAVIOR', :not_set)

ENV['LISTEN_GEM_ADAPTER_WARN_BEHAVIOR'] = 'log'

spec.run

if orig_debugging_env_variable == :not_set
ENV.delete('LISTEN_GEM_ADAPTER_WARN_BEHAVIOR')
else
ENV['ENV_VARIABLE_NAME'] = orig_debugging_env_variable
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)
[:silent, nil, false, :warn].each do |behavior|
it "respects the environment variable over #{behavior.inspect}" do
Listen.adapter_warn_behavior = behavior

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

subject
end
end

it "respects the environment variable over a callable config" do
Listen.adapter_warn_behavior = ->(_message) { :warn }

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

subject
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 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

[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

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
Loading