class LogStash::Filters::Math

Public Instance Methods

filter(event) click to toggle source
# File lib/logstash/filters/math.rb, line 109
def filter(event)
  event_changed = false # can exit if none of the calculations are are suitable
  context = EventRegisterContext.new(event) # creates a new registers array, each element can use the event or register from the store
  @calculate_copy.each do |function, left_element, right_element, result_element|
    logger.debug("executing", "function" => function.name, "left_field" => left_element, "right_field" => right_element, "target" => result_element)
    # TODO add support for automatic conversion to Numeric if String
    operand1 = left_element.get(context)
    operand2 = right_element.get(context)
    # allow all the validation warnings to be logged before we skip to next
    next if operand1.nil? || operand2.nil?
    next if function.invalid?(operand1, operand2, event)

    result = function.call(operand1, operand2)
    result_element.set(result, context)
    logger.debug("calculation result stored", "function" => function.name, "target" => result_element, "result" => result)
    event_changed = true
  end
  return unless event_changed
  filter_matched(event)
end
register() click to toggle source
# File lib/logstash/filters/math.rb, line 42
def register
  functions = {}
  [
    [MathFunctions::Add.new, '+', 'add', 'plus'],
    [MathFunctions::Subtract.new, '-', 'sub', 'subtract'],
    [MathFunctions::Multiply.new, '*', 'mpx', 'times', 'multiply'],
    [MathFunctions::Round.new, 'round'],
    [MathFunctions::Power.new, '**', '^', 'to the power of'],
    [MathFunctions::Divide.new, '/', 'div', 'divide'],
    [MathFunctions::Modulo.new, 'mod', 'modulo'],
    [MathFunctions::FloatDivide.new, 'fdiv', 'float divide']
  ].each do |list|
    value = list.shift
    list.each{|key| functions[key] = value}
  end

  # Do some sanity checks that calculate is actually an array of arrays, and that each calculation (inner array)
  # is exactly 4 fields and the first field is a valid calculation operator name.
  @calculate_copy = []
  all_function_keys = functions.keys
  calculate.each do |calculation|
    if calculation.size != 4
      raise LogStash::ConfigurationError, I18n.t(
        "logstash.runner.configuration.invalid_plugin_register",
        :plugin => "filter",
        :type => "math",
        :error => "Invalid number of elements in a calculation setting: expected 4, got: #{calculation.size}. You specified: #{calculation}"
      )
    end
    function_key, operand1, operand2, target = calculation
    if !all_function_keys.include?(function_key)
      raise LogStash::ConfigurationError, I18n.t(
        "logstash.runner.configuration.invalid_plugin_register",
        :plugin => "filter",
        :type => "math",
        :error => "Invalid first element of a calculation: expected one of #{all_function_keys.join(',')}, got: #{function_key}. You specified: #{calculation.join(',')}"
      )
    end
    function = functions[function_key]

    left_element = MathCalculationElements.build(operand1, 1)
    right_element = MathCalculationElements.build(operand2, 2)
    if right_element.literal?
      lhs = left_element.literal? ? left_element.get : 1
      warning = function.invalid?(lhs, right_element.get)
      unless warning.nil?
        raise LogStash::ConfigurationError, I18n.t(
          "logstash.runner.configuration.invalid_plugin_register",
          :plugin => "filter",
          :type => "math",
          :error => "Numeric literals are specified as in the calculation but the function invalidates with '#{warning}'. You specified: #{calculation.join(',')}"
        )
      end
    end
    result_element = MathCalculationElements.build(target, 3)
    @calculate_copy << [function, left_element, right_element, result_element]
  end
  if @calculate_copy.last.last.is_a?(MathCalculationElements::RegisterElement)
    raise LogStash::ConfigurationError, I18n.t(
      "logstash.runner.configuration.invalid_plugin_register",
      :plugin => "filter",
      :type => "math",
      :error => "The final target is a Register, the overall calculation result will not be set in the event"
    )
  end
end