module TeeLogger::Filter

Constants

DEFAULT_FILTER_WORDS

The default words to filter. It's up to each individual filter to decide what to do when they encounter a word, but these are the words the filters should process. Note that they can be strings or regular expressions. Regular expressions should by and large not be anchored to the beginning or end of strings.

REDACTED_WORD

Word to use in place of original values

Public Instance Methods

apply_filters(*args) click to toggle source

Applies all registered filters.

# File lib/teelogger/filter.rb, line 106
def apply_filters(*args)
  # Pre-process filter words: we need to have regular expressions everywhere
  words = []
  filter_words.each do |word|
    if word.is_a? Regexp
      words << word
    else
      words << Regexp.new(word.to_s)
    end
  end

  # We instanciate each filter once per application, and store the instnaces
  # in a cache for that duration.
  filter_cache = {}

  # Pass state on to apply_filters_internal
  state = {
    :words => words,
    :filter_cache => filter_cache,
    :filters => self,
  }
  return apply_filters_internal(state, *args)
end
apply_filters_internal(state, *args) click to toggle source

Implementation of apply_filters that doesn't initialize state, but carries it over. Used internally only.

# File lib/teelogger/filter.rb, line 134
def apply_filters_internal(state, *args)
  filtered_args = args

  # Iterate through filters
  registered_filters.each do |window, window_filters|
    # Determine actual window size
    window_size = [window, filtered_args.size].min

    # Process each window so that elements are updated in-place. This
    # means we'll start at index 0 and process up to window_size elements.
    idx = 0
    while (idx + window_size - 1) < filtered_args.size
      # We need to use *one* argument to determine whether the filter
      # type applies. The current strategy is to match the first argument
      # only, and let the filter cast to other types if necessary.
      first_arg = filtered_args[idx]

      window_filters.each do |class_match, type_filters|
        # We process with these type filters if first_arg matches the
        # class_match.
        if not first_arg.is_a? class_match
          next
        end

        # Now process with the given filters.
        type_filters.each do |filter|
          # XXX Do not turn this into a one-liner, or we'll instanciate
          #     filters without using them.
          filter_instance = state[:filter_cache].fetch(filter, nil)
          if filter_instance.nil?
            filter_instance = filter.new(state)
            state[:filter_cache][filter] = filter_instance
          end

          # Single item windows need to be processed a bit differently from
          # multi-item windows.
          tuple = filtered_args[idx..idx + window_size - 1]
          filtered = filter_instance.process(*tuple)

          # Sanity check result
          if filtered.size != tuple.size
            raise "Filter #{filter} added or removed items to the log; don't know how to process!"
          end

          filtered.each_with_index do |item, offset|
            filtered_args[idx + offset] = item
          end
        end # type_filters.each
      end # window_filters.each

      # Advance to the next window
      idx += 1
    end # each window
  end # all registered filters

  return filtered_args
end
filter_words() click to toggle source

Filter words

# File lib/teelogger/filter.rb, line 29
def filter_words
  @filter_words ||= DEFAULT_FILTER_WORDS
  return @filter_words
end
filter_words=(arg) click to toggle source
# File lib/teelogger/filter.rb, line 34
def filter_words=(arg)
  # Coerce into array
  begin
    arr = []
    arg.each do |item|
      arr << item
    end
    @filter_words = arr
  rescue NameError, NoMethodError
    raise "Can't set filter words, not iterable: #{arg}"
  end
end
load_filters(*args) click to toggle source

Load all built-in filters.

# File lib/teelogger/filter.rb, line 50
def load_filters(*args)
  require_rel 'filters'
  ::TeeLogger::Filter.constants.collect {|const_sym|
    ::TeeLogger::Filter.const_get(const_sym)
  }.each do |filter|
    begin
      register_filter(filter)
      if not ENV['TEELOGGER_VERBOSE'].nil? and ENV['TEELOGGER_VERBOSE'].to_i > 0
        puts "Registered filter #{filter}."
      end
    rescue StandardError => err
      if not ENV['TEELOGGER_VERBOSE'].nil? and ENV['TEELOGGER_VERBOSE'].to_i > 0
        puts "Not registering filter: #{err}"
      end
    end
  end
end
register_filter(filter) click to toggle source

Expects a class, registers the class for use by the filter function

# File lib/teelogger/filter.rb, line 78
def register_filter(filter)
  # Sanity checks/register filter
  if filter.class != Class
    raise "Ignoring '#{filter}', not a class."
  end

  if not filter < FilterBase
    raise "Class '#{filter}' is not derived from FilterBase."
  end

  begin
    window = filter::WINDOW_SIZE.to_i
    window_filters = registered_filters.fetch(window, {})

    filter::FILTER_TYPES.each do |type|
      type_filters = window_filters.fetch(type, [])
      type_filters.push(filter) unless type_filters.include?(filter)
      window_filters[type] = type_filters
    end

    registered_filters[window] = window_filters
  rescue NameError, NoMethodError
    raise "Class '#{filter}' is missing a FILTER_TYPES Array or a WINDOW_SIZE Integer."
  end
end
registered_filters() click to toggle source

Returns all registered filters.

# File lib/teelogger/filter.rb, line 70
def registered_filters
  # Initialize if it doesn't exist
  @filters ||= {}
  return @filters
end