listen 2.7.11 → 2.7.12

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -2,4 +2,11 @@ require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
- task default: :spec
5
+
6
+ if ENV["CI"] != "true"
7
+ require "rubocop/rake_task"
8
+ RuboCop::RakeTask.new(:rubocop)
9
+ task default: [:spec, :rubocop]
10
+ else
11
+ task default: [:spec]
12
+ end
@@ -0,0 +1,139 @@
1
+ # Issues and troubleshooting
2
+
3
+ ## 3 steps before you start diagnosing problems
4
+
5
+ These 3 steps will:
6
+ * help quickly troubleshoot issues caused by obscure problems
7
+ * help quickly identify the area of the problem (a full list is [below](#known-issues))
8
+ * help you get familiar with listen's diagnostic mode
9
+ * help you create relevant output before you submit an issue
10
+
11
+ 1) For effective troubleshooting set the `LISTEN_GEM_DEBUGGING=1` variable
12
+ before starting listen.
13
+
14
+ 2) Verify polling works (see `force_polling` option).
15
+
16
+ After starting listen, you should see something like:
17
+ ```
18
+ INFO -- : Celluloid loglevel set to: 1
19
+ INFO -- : Record.build(): 0.06773114204406738 seconds
20
+ ```
21
+
22
+ (Listen uses [Celluloid](https://github.com/celluloid/celluloid) for logging, so if you don't see anything, `Celluloid.logger` might have been disabled by a different gem, e.g. sidekiq)
23
+
24
+ If you don't see the line `Record.build()`:
25
+ * and there's a lot of disk activity, you may have to wait a few seconds
26
+ * you may be using an outdated version of Listen
27
+ * listen may have got stuck on a recursive symlink, see #259
28
+
29
+ 3) Make changes e.g. `touch foo` or `echo "a" >> foo` (for troubleshooting, avoid using an editor which could generate too many misleading events)
30
+
31
+ You should see something like:
32
+
33
+ ```
34
+ INFO -- : listen: raw changes: [[:added, "/home/me/foo"]]
35
+ INFO -- : listen: final changes: {:modified=>[], :added=>["/home/me/foo"], :removed=>[]}
36
+ ```
37
+
38
+ "raw changes" contains changes collected during the `:wait_for_delay` and `:latency` intervals, while "final changes" is what listen decided are relevant changes (for better editor support).
39
+
40
+ ## Adapter-specific diagnostics
41
+
42
+ Use the `LISTEN_GEM_DEBUGGING` set to `2` for additional info.
43
+
44
+ E.g. you'll get:
45
+
46
+ ```
47
+ INFO -- : Celluloid loglevel set to: 0
48
+ DEBUG -- : Broadcaster: starting tcp server: 127.0.0.1:4000
49
+ DEBUG -- : Adapter: considering TCP ...
50
+ DEBUG -- : Adapter: considering polling ...
51
+ DEBUG -- : Adapter: considering optimized backend...
52
+ INFO -- : Record.build(): 0.0007264614105224609 seconds
53
+ DEBUG -- : inotify: foo ([:create])
54
+ DEBUG -- : raw queue: [:file, #<Pathname:/tmp/x>, "foo", {:change=>:added}]
55
+ DEBUG -- : added: file:/tmp/x/foo ({:change=>:added})
56
+ DEBUG -- : inotify: foo ([:attrib])
57
+ DEBUG -- : raw queue: [:file, #<Pathname:/tmp/x>, "foo", {:change=>:modified}]
58
+ DEBUG -- : inotify: foo ([:close_write, :close])
59
+ DEBUG -- : raw queue: [:file, #<Pathname:/tmp/x>, "foo", {:change=>:modified}]
60
+ DEBUG -- : modified: file:/tmp/x/foo ({:change=>:modified})
61
+ DEBUG -- : modified: file:/tmp/x/foo ({:change=>:modified})
62
+ INFO -- : listen: raw changes: [[:added, "/tmp/x/foo"]]
63
+ INFO -- : listen: final changes: {:modified=>[], :added=>["/tmp/x/foo"], :removed=>[]}
64
+ DEBUG -- : Callback took 4.410743713378906e-05 seconds
65
+ ```
66
+
67
+ This shows:
68
+ * host port listened to (for forwarding events)
69
+ * the actual adapter used (here, it's "optimized backend")
70
+ * the event received (here it's `:create` from rb-inotify)
71
+ * "raw queue" - events queued for processing (collected during `:latency`)
72
+ * "Callback took" - how long it took your app to process changes
73
+
74
+ #### Known issues
75
+
76
+ Here are common issues grouped by area in which they occur:
77
+
78
+ 1. System/OS
79
+ * [Update your Dropbox client](http://www.dropbox.com/downloading), if you have Dropbox installed.
80
+ * old MacOS (< 10.6)
81
+ * certain old versions of Ruby (try a newer Ruby on Windows for `wdm` and TCP mode to work)
82
+ * system limits
83
+ * threads for Celluloid (e.g. Virtual Machine CPU/RAM limitations)
84
+ * [inotify limits (Linux)](https://github.com/guard/listen/wiki/Increasing-the-amount-of-inotify-watchers)
85
+ * system in an inconsistent state or stuck (try rebooting/updating on Windows/Mac - seriously!)
86
+ * FSEvent bug: (http://feedback.livereload.com/knowledgebase/articles/86239)
87
+
88
+ 2. Installation/gems/config
89
+ * not running listen or your app (e.g. guard) with `bundle exec` first
90
+ * old version of listen
91
+ * problems with adapter gems (`wdm`, `rb-fsevent`, `rb-inotify`) not installed, not detected properly (Windows) or not in Gemfile (Windows)
92
+ * Celluloid actors are silently crashing (when no LISTEN_GEM_DEBUGGING variable is present)
93
+ * see the [Performance](https://github.com/guard/listen/blob/master/README.md#Performance) section in the README
94
+
95
+ 3. Filesystem
96
+ * VM shared folders and network folders (NFS, Samba, SSHFS) don't work with optimized backends (workaround: polling, [TCP mode](https://github.com/guard/listen/blob/master/README.md#forwarding-file-events-over-tcp), Vagrant's rsync-auto mode, rsync/unison)
97
+ * FAT/HFS timestamps have 1-second precision, which can cause polling and rb-fsevent to be very slow on large files (try `LISTEN_GEM_DISABLE_HASHING` variable)
98
+ * virtual filesystems may not implement event monitoring
99
+ * restrictive file/folder permissions
100
+ * watched folders moved/removed while listen was running (try restarting listen and moving/copying watched folder to a new location)
101
+
102
+ 4. Insufficient latency (for polling and rb-fsevent)
103
+ * too many files being watched (polling) and not enough threads or CPU power
104
+ * slow editor save (see below)
105
+ * slow hard drive
106
+ * encryption
107
+ * a combination of factors
108
+
109
+ 5. Too few or too many callbacks (`:wait_for_delay` option)
110
+ * complex editor file-save causes events to happen during callback (result: multiple callbacks if wait_for_delay is too small)
111
+ * too large when using TCP mode (see timestamps in output to debug)
112
+ * too many changes happening too frequently (use ignore rules to filter them out)
113
+
114
+ 6. Paths
115
+ * default ignore rules
116
+ * encoding-related issues (bad filenames, mounted FS encoding mismatch)
117
+ * symlinks may cause listen to hang (#259)
118
+ * symlinks may not work as you expect or work differently for polling vs non-polling
119
+ * TCP paths don't match with client's current working directory
120
+
121
+ 7. Editors
122
+ * "atomic save" in editors may confuse listen (disable it and try again)
123
+ * listen's default ignore rules may need tweaking
124
+ * your editor may not be supported yet (see default ignore rules for editors)
125
+ * use `touch foo` or `echo "a" >> foo` to confirm it's an editor issue
126
+ * slow terminal/GFX card, slow font, transparency effects in terminal
127
+ * complex/lengthy editor save (due to e.g. many plugins running during save)
128
+ * listen has complex rules for detecting atomic file saves (Linux)
129
+
130
+ 8. TCP (tcp mode) issues
131
+ * not a recent listen gem (before 2.7.11)
132
+ * additional network delay and collecting may need a higher `:wait_for_delay` value
133
+ * changes (added, removed, deleted) not matching actual changes
134
+
135
+ If your application keeps using the polling-adapter and you can't figure out why, feel free to [open an issue](https://github.com/guard/listen/issues/new) (and be sure to [give all the details](https://github.com/guard/listen/blob/master/CONTRIBUTING.md)).
136
+
137
+ Listen traps SIGINT signal to properly finalize listeners. If you plan
138
+ on trapping this signal yourself - make sure to call `Listen.stop` in
139
+ signal handler.
@@ -1,6 +1,8 @@
1
1
  require 'celluloid'
2
2
  require 'listen/listener'
3
3
 
4
+ require 'listen/internals/thread_pool'
5
+
4
6
  module Listen
5
7
  class << self
6
8
  # Listens to file system modifications on a either single directory or
@@ -21,11 +23,9 @@ module Listen
21
23
  def to(*args, &block)
22
24
  Celluloid.boot unless Celluloid.running?
23
25
  options = args.last.is_a?(Hash) ? args.last : {}
24
- if target = options.delete(:forward_to)
25
- _add_listener(target, :broadcaster, *args, &block)
26
- else
27
- _add_listener(*args, &block)
28
- end
26
+ target = options.delete(:forward_to)
27
+ args = ([target, :broadcaster] + args) if target
28
+ _add_listener(*args, &block)
29
29
  end
30
30
 
31
31
  # Stop all listeners & Celluloid
@@ -36,6 +36,7 @@ module Listen
36
36
  # This is used by the `listen` binary to handle Ctrl-C
37
37
  #
38
38
  def stop
39
+ Internals::ThreadPool.stop
39
40
  @listeners ||= []
40
41
 
41
42
  # TODO: should use a mutex for this
@@ -22,7 +22,8 @@ module Listen
22
22
  _warn_polling_fallback(options)
23
23
  Polling
24
24
  rescue
25
- _log :warn, "Adapter: failed: #{$!.inspect}:#{$@.join("\n")}"
25
+ _log :warn, format('Adapter: failed: %s:%s', $ERROR_POSITION.inspect,
26
+ $ERROR_POSITION * "\n")
26
27
  raise
27
28
  end
28
29
 
@@ -28,7 +28,7 @@ module Listen
28
28
  defaults = self.class.const_get('DEFAULTS')
29
29
  @options = Listen::Options.new(options, defaults)
30
30
  rescue
31
- _log :error, "adapter config failed: #{$!}:#{$@.join("\n")}"
31
+ _log_exception 'adapter config failed: %s:%s'
32
32
  raise
33
33
  end
34
34
 
@@ -53,11 +53,11 @@ module Listen
53
53
 
54
54
  def start
55
55
  configure
56
- Thread.new do
56
+ Listen::Internals::ThreadPool.add do
57
57
  begin
58
58
  _run
59
59
  rescue
60
- _log :error, "run() in thread failed: #{$!}:#{$@.join("\n")}"
60
+ _log_exception 'run() in thread failed: %s:%s'
61
61
  raise
62
62
  end
63
63
  end
@@ -83,6 +83,10 @@ module Listen
83
83
  self.class.send(:_log, *args)
84
84
  end
85
85
 
86
+ def _log_exception(msg)
87
+ _log :error, format(msg, $ERROR_INFO, $ERROR_POSITION * "\n")
88
+ end
89
+
86
90
  def self._log(*args)
87
91
  Celluloid::Logger.send(*args)
88
92
  end
@@ -1,3 +1,5 @@
1
+ require 'listen/internals/thread_pool'
2
+
1
3
  module Listen
2
4
  module Adapter
3
5
  # Adapter implementation for Mac OS X `FSEvents`.
@@ -10,6 +12,7 @@ module Listen
10
12
 
11
13
  private
12
14
 
15
+ # NOTE: each directory gets a DIFFERENT callback!
13
16
  def _configure(dir, &callback)
14
17
  require 'rb-fsevent'
15
18
  opts = { latency: options.latency }
@@ -20,8 +23,20 @@ module Listen
20
23
  end
21
24
  end
22
25
 
26
+ # NOTE: _run is called within a thread, so run every other
27
+ # worker in it's own thread
23
28
  def _run
24
- @workers.pop.run while !@workers.empty?
29
+ first = @workers.pop
30
+ until @workers.empty?
31
+ Listen::Internals::ThreadPool.add do
32
+ begin
33
+ @workers.pop.run
34
+ rescue
35
+ _log_exception 'run() in extra thread(s) failed: %s: %s'
36
+ end
37
+ end
38
+ end
39
+ first.run
25
40
  end
26
41
 
27
42
  def _process_event(dir, event)
@@ -26,16 +26,18 @@ module Listen
26
26
  @buffer = ''
27
27
  async.run
28
28
  rescue Celluloid::Task::TerminatedError
29
- _log :debug, "TCP adapter was terminated: #{$!.inspect}"
29
+ _log :debug, "TCP adapter was terminated: #{$ERROR_INFO.inspect}"
30
30
  rescue Errno::ECONNREFUSED
31
31
  sleep 1
32
32
  attempts -= 1
33
- _log :warn, "TCP.start: #{$!.inspect}"
33
+ _log :warn, "TCP.start: #{$ERROR_INFO.inspect}"
34
34
  retry if attempts > 0
35
- _log :error, "TCP.start: #{$!.inspect}:#{$@.join("\n")}"
35
+ _log :error, format('TCP.start: %s:%s', $ERROR_INFO.inspect,
36
+ $ERROR_POSITION * "\n")
36
37
  raise
37
38
  rescue
38
- _log :error, "TCP.start: #{$!.inspect}:#{$@.join("\n")}"
39
+ _log :error, format('TCP.start: %s:%s', $ERROR_INFO.inspect,
40
+ $ERROR_POSITION * "\n")
39
41
  raise
40
42
  end
41
43
 
@@ -65,7 +67,8 @@ module Listen
65
67
  handle_message(message)
66
68
  end
67
69
  rescue
68
- _log :error, "TCP.handle_data crashed: #{$!}:#{$@.join("\n")}"
70
+ _log :error, format('TCP.handle_data crashed: %s:%s', $ERROR_INFO,
71
+ $ERROR_POSITION * "\n")
69
72
  raise
70
73
  end
71
74
 
@@ -7,10 +7,7 @@ module Listen
7
7
 
8
8
  BUNDLER_DECLARE_GEM = <<-EOS.gsub(/^ {6}/, '')
9
9
  Please add the following to your Gemfile to avoid polling for changes:
10
- require 'rbconfig'
11
- if RbConfig::CONFIG['target_os'] =~ /mswin|mingw|cygwin/i
12
- gem 'wdm', '>= 0.1.0'
13
- end
10
+ gem 'wdm', '>= 0.1.0' if Gem.win_platform?
14
11
  EOS
15
12
 
16
13
  def self.usable?
@@ -18,7 +15,9 @@ module Listen
18
15
  require 'wdm'
19
16
  true
20
17
  rescue LoadError
21
- _log :debug, "wdm - load failed: #{$!}:#{$@.join("\n")}"
18
+ _log :debug, format('wdm - load failed: %s:%s', $ERROR_INFO,
19
+ $ERROR_POSITION * "\n")
20
+
22
21
  Kernel.warn BUNDLER_DECLARE_GEM
23
22
  false
24
23
  end
@@ -82,7 +81,8 @@ module Listen
82
81
  end
83
82
  rescue
84
83
  details = event.inspect
85
- _log :error, "wdm - callback (#{details}): #{$!}:#{$@.join("\n")}"
84
+ _log :error, format('wdm - callback (%): %s:%s', details, $ERROR_INFO,
85
+ $ERROR_POSITION * "\n")
86
86
  raise
87
87
  end
88
88
 
@@ -43,9 +43,10 @@ module Listen
43
43
  end
44
44
  end
45
45
  rescue Celluloid::Task::TerminatedError
46
- _log :debug, "Change#change was terminated: #{$!.inspect}"
46
+ _log :debug, "Change#change was terminated: #{$ERROR_INFO.inspect}"
47
47
  rescue RuntimeError
48
- _log :error, "Change#change crashed #{$!.inspect}:#{$@.join("\n")}"
48
+ _log :error, format('Change#change crashed %s:%s', $ERROR_INFO.inspect,
49
+ $ERROR_POSITION * "\n")
49
50
  raise
50
51
  end
51
52
 
@@ -55,9 +55,7 @@ module Listen
55
55
  listener = Listen.to directory, forward_to: address, &callback
56
56
  listener.start
57
57
 
58
- while listener.listen?
59
- sleep 0.5
60
- end
58
+ sleep 0.5 while listener.listen?
61
59
  end
62
60
  end
63
61
  end
@@ -13,16 +13,10 @@ module Listen
13
13
  path = dir + rel_path
14
14
  current = Set.new(path.children)
15
15
 
16
- if options[:silence]
17
- _log(:debug) do
18
- "Recording: #{rel_path}: #{options.inspect}"\
19
- " [#{previous.inspect}] -> (#{current.inspect})"
20
- end
21
- else
22
- _log(:debug) do
23
- "Scanning: #{rel_path}: #{options.inspect}"\
24
- " [#{previous.inspect}] -> (#{current.inspect})"
25
- end
16
+ _log(:debug) do
17
+ format('%s: %s(%s): %s -> %s',
18
+ (options[:silence] ? 'Recording' : 'Scanning'),
19
+ rel_path, options.inspect, previous.inspect, current.inspect)
26
20
  end
27
21
 
28
22
  current.each do |full_path|
@@ -46,7 +40,9 @@ module Listen
46
40
  _async_changes(dir, path, queue, previous, options)
47
41
  _change(queue, :file, dir, rel_path, options)
48
42
  rescue
49
- _log(:warn) { "scanning DIED: #{$!}:#{$@.join("\n")}" }
43
+ _log(:warn) do
44
+ format('scan DIED: %s:%s', $ERROR_INFO, $ERROR_POSITION * "\n")
45
+ end
50
46
  raise
51
47
  end
52
48
 
@@ -23,52 +23,46 @@ module Listen
23
23
  return :modified
24
24
  end
25
25
 
26
- unless /1|true/ =~ ENV['LISTEN_GEM_DISABLE_HASHING']
27
- if self.inaccurate_mac_time?(lstat)
28
- # Check if change happened within 1 second (maybe it's even
29
- # too much, e.g. 0.3-0.5 could be sufficient).
30
- #
31
- # With rb-fsevent, there's a (configurable) latency between
32
- # when file was changed and when the event was triggered.
33
- #
34
- # If a file is saved at ???14.998, by the time the event is
35
- # actually received by Listen, the time could already be e.g.
36
- # ???15.7.
37
- #
38
- # And since Darwin adapter uses directory scanning, the file
39
- # mtime may be the same (e.g. file was changed at ???14.001,
40
- # then at ???14.998, but the fstat time would be ???14.0 in
41
- # both cases).
42
- #
43
- # If change happend at ???14.999997, the mtime is 14.0, so for
44
- # an mtime=???14.0 we assume it could even be almost ???15.0
45
- #
46
- # So if Time.now.to_f is ???15.999998 and stat reports mtime
47
- # at ???14.0, then event was due to that file'd change when:
48
- #
49
- # ???15.999997 - ???14.999998 < 1.0s
50
- #
51
- # So the "2" is "1 + 1" (1s to cover rb-fsevent latency +
52
- # 1s maximum difference between real mtime and that recorded
53
- # in the file system)
54
- #
55
- if data[:mtime].to_i + 2 > Time.now.to_f
56
- begin
57
- md5 = Digest::MD5.file(path).digest
58
- record.async.update_file(dir, rel_path, data.merge(md5: md5))
59
- :modified if record_data[:md5] && md5 != record_data[:md5]
26
+ return if /1|true/ =~ ENV['LISTEN_GEM_DISABLE_HASHING']
27
+ return unless self.inaccurate_mac_time?(lstat)
60
28
 
61
- rescue SystemCallError
62
- # ignore failed md5
63
- end
64
- end
65
- end
66
- end
29
+ # Check if change happened within 1 second (maybe it's even
30
+ # too much, e.g. 0.3-0.5 could be sufficient).
31
+ #
32
+ # With rb-fsevent, there's a (configurable) latency between
33
+ # when file was changed and when the event was triggered.
34
+ #
35
+ # If a file is saved at ???14.998, by the time the event is
36
+ # actually received by Listen, the time could already be e.g.
37
+ # ???15.7.
38
+ #
39
+ # And since Darwin adapter uses directory scanning, the file
40
+ # mtime may be the same (e.g. file was changed at ???14.001,
41
+ # then at ???14.998, but the fstat time would be ???14.0 in
42
+ # both cases).
43
+ #
44
+ # If change happend at ???14.999997, the mtime is 14.0, so for
45
+ # an mtime=???14.0 we assume it could even be almost ???15.0
46
+ #
47
+ # So if Time.now.to_f is ???15.999998 and stat reports mtime
48
+ # at ???14.0, then event was due to that file'd change when:
49
+ #
50
+ # ???15.999997 - ???14.999998 < 1.0s
51
+ #
52
+ # So the "2" is "1 + 1" (1s to cover rb-fsevent latency +
53
+ # 1s maximum difference between real mtime and that recorded
54
+ # in the file system)
55
+ #
56
+ return if data[:mtime].to_i + 2 <= Time.now.to_f
57
+
58
+ md5 = Digest::MD5.file(path).digest
59
+ record.async.update_file(dir, rel_path, data.merge(md5: md5))
60
+ :modified if record_data[:md5] && md5 != record_data[:md5]
67
61
  rescue SystemCallError
68
62
  record.async.unset_path(dir, rel_path)
69
63
  :removed
70
64
  rescue
71
- Celluloid::Logger.debug "lstat failed for: #{rel_path} (#{$!})"
65
+ Celluloid::Logger.debug "lstat failed for: #{rel_path} (#{$ERROR_INFO})"
72
66
  raise
73
67
  end
74
68