listen Ruby Gem

repository·master·Indexed 24 days ago

https://github.com/guard/listen

A cross-platform, OS-optimized Ruby gem for monitoring file system modifications. It provides a way to notify applications of file additions, removals, or changes using OS-specific adapters for macOS, Linux, Windows (via wdm), and BSD (via rb-kqueue), with a polling fallback for all platforms. It includes a CLI for directory monitoring and a programmatic API via Listen.to and Listen::Listener to manage file system watches with support for regex-based filtering (ignore/only) and custom logging.

Tokens
3.5K
Snippets
5
Records
22
Agent score
83%

What's inside listen

  1. Configure Listen Adapters for different Operating Systems

    master

    The listen gem uses OS-specific adapters for high-performance file monitoring. It automatically selects the best available adapter.

    • Darwin (macOS) & Linux: Supported out-of-the-box.
    • Windows: It is highly recommended to use the wdm adapter instead of the slower polling fallback.
    • BSD: You can use the rb-kqueue adapter.
    • Polling: A fallback adapter that works on all platforms (including network filesystems/VM shared folders) but is significantly slower.

    To force the use of the polling adapter, use the :force_polling option during initialization.

  2. Install optimized adapters for Windows and BSD

    master

    To avoid the slow polling fallback on Windows or BSD, add the following gems to your Gemfile:

    For Windows (WDM adapter):

    gem 'wdm', '>= 0.1.0'

    *For BSD (rb-kqueue adapter):

    gem 'rb-kqueue', '>= 0.2'
    gem 'wdm', '>= 0.1.0'
    gem 'rb-kqueue', '>= 0.2'
  3. Debug Listen using environment variables

    master

    If listen is not working as expected, enable diagnostic logging by setting the LISTEN_GEM_DEBUGGING environment variable before starting your process.

    • LISTEN_GEM_DEBUGGING=info: Provides general information and helps verify if polling is working.
    • LISTEN_GEM_DEBUGGING=debug: Provides deep technical details about raw and final changes detected.

    Example of triggering changes for testing: Once debugging is enabled, use CLI commands like touch foo or echo "a" >> foo to trigger events. You should see output like:

    INFO -- : listen: raw changes: [[:added, "/home/me/foo"]]
    INFO -- : listen: final changes: {:modified=>[], :added=>["/home/me/foo"], :removed=>[]}
  4. Optimize Listen performance

    master

    If listen is slow or unresponsive, consider these optimizations:

    1. Avoid Polling: Ensure an optimized adapter (like wdm or inotify) is active. If you see a warning about polling at startup, you are not using an optimized adapter.
    2. Filter Directories: Use the :ignore and :only options to prevent tracking unnecessary directories (especially important for Polling and macOS).
    3. Tune Latency: Adjust the :latency and :wait_for_delay options to balance responsiveness and CPU usage.
    4. Exclude Volatile Files: Do not watch directories containing frequently changing files like logs or database files.
    5. Avoid Multiple Instances: Do not run multiple instances of listen in the background simultaneously.
  5. Configure logging and debugging

    master

    Custom Logger

    You can set a custom logger for the entire process using Listen.logger =.

    Listen.logger = Rails.logger

    To disable logging entirely:

    Listen.logger = ::Logger.new('/dev/null')

    Debugging via Environment Variables

    You can override the default logging level (which is error) by setting the LISTEN_GEM_DEBUGGING environment variable. Supported levels are debug, info, warn, fatal, and error.

    export LISTEN_GEM_DEBUGGING=debug

    Adapter Warnings

    If the underlying adapter has issues, Listen uses Kernel#warn by default. You can change this behavior using Listen.adapter_warn_behavior =:

    • :warn: Default behavior.
    • :log: Sends warnings to Listen.logger.warn.
    • :silent: Suppresses all adapter warnings.

    You can also provide a lambda to Listen.adapter_warn_behavior to selectively suppress specific messages based on the warning string.

    # Customizing warning behavior with a lambda
    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
  6. Configure Listen options

    master

    All options can be passed to Listen.to after the directory paths.

    OptionDescription
    ignore:A list of regex patterns to ignore (e.g. [\r{/foo/bar}, /\.pid$/]).
    ignore!:Overwrites default ignored paths with the provided pattern.
    only:Regex pattern to only listen to specific files.
    latency:Delay in seconds between checking for changes (default: 0.25, polling: 1.0).
    wait_for_delay:Delay in seconds between calls to the callback when changes exist (default: 0.10).
    force_polling:Boolean to force the use of the polling adapter.
    relative:Whether changes should be reported relative to the current directory (default: false).
    polling_fallback_message:Custom message for polling fallback (or false to disable).
  7. Increase inotify watcher limits on Linux

    master

    On Linux distributions (Debian, RedHat, etc.), listen uses inotify. If you are monitoring many files, you may hit the system limit.

    Check current limit:

    $ cat /proc/sys/fs/inotify/max_user_watches

    Increase limit temporarily:

    $ sudo sysctl fs.inotify.max_user_watches=524288
    $ sudo sysctl -p

    Increase limit permanently (Debian/RedHat):

    $ sudo sh -c "echo fs.inotify.max_user_watches=524288 >> /etc/sysctl.conf"
    $ sudo sysctl -p

    Increase limit permanently (ArchLinux): Find the config file in /etc/sysctl.d/ containing fs.inotify.max_user_watches, then overwrite it:

    $ sudo sh -c "echo fs.inotify.max_user_watches=524288 > /etc/sysctl.d/40-max-user-watches.conf"
    $ sudo sysctl --system
  8. Troubleshoot 'Listen will be polling for changes' message

    master

    If you see the message Listen will be polling for changes, an optimized adapter was not found.

    Common causes and solutions:

    • Windows: Install the wdm gem.
    • Running without Bundler: Ensure you are running your application using bundle exec.
    • Sass users: Ensure you are using a modern version of the listen gem. A simple way to fix this is to create a Gemfile:
    source 'https://rubygems.org'
    gem 'listen'
    gem 'sass'

    Then run:

    $ bundle update
    $ bundle exec sass --watch
  9. Filter watched files with ignore and only

    master

    By default, Listen watches all files. You can restrict what is watched using ignore or only patterns.

    Ignore paths

    Use ignore to add regex patterns to the default ignored list, or ignore! to overwrite the default list entirely. :ignore patterns are evaluated against relative paths.

    Only specific files

    Use only to watch only files matching a specific pattern (e.g., only .rb files). This overwrites existing patterns. :only patterns are evaluated against relative file paths.

  10. How to use Listen.to to watch directories

    master

    Call Listen.to with one or more directory paths and provide a block as the changes callback. The callback receives three array parameters in this order: modified, added, and removed. Each array contains absolute paths to the files that changed.

    Note that listener.start starts a listener thread and does not block execution. You must keep the process alive (e.g., using sleep) to continue receiving notifications.

    require 'listen'
    
    listener = Listen.to('dir/to/listen', 'dir/to/listen2') do |modified, added, removed|
      puts "modified absolute path array: #{modified}"
      puts "added absolute path array: #{added}"
      puts "removed absolute path array: #{removed}"
    end
    listener.start
    
    # Keep the process running
    sleep
  11. Manage listener state: pause, start, and stop

    master

    You can control the lifecycle of a listener to pause processing changes without stopping the underlying watch mechanism, or to stop it entirely.

    • listener.start: Starts the listener thread (non-blocking) or resumes processing changes if the listener was paused.
    • listener.pause: Stops processing changes but continues to collect them in the background.
    • listener.stop: Stops both listening to changes and processing them. Use this to clean up listeners on finish.
    • listener.paused?: Returns true if the listener is currently paused.
    • listener.processing?: Returns true if the listener is actively processing changes.
    listener = Listen.to('dir/path/to/listen') { |modified, added, removed| puts 'handle changes here...' }
    
    listener.start
    listener.paused?     # => false
    listener.processing? # => true
    
    listener.pause       # stops processing changes (but keeps on collecting them)
    listener.paused?     # => true
    listener.processing? # => false
    
    listener.start       # resumes processing changes
    listener.stop        # stop both listening to changes and processing them