class Fluent::ExceptionDetector

State machine that consumes individual log lines and detects multi-line stack traces.

Public Class Methods

new(*languages) click to toggle source
# File lib/fluent/plugin/exception_detector.rb, line 140
def initialize(*languages)
  @state = :start_state
  @rules = Hash.new { |h, k| h[k] = [] }

  languages = [:all] if languages.empty?

  languages.each do |lang|
    rule_config =
      ExceptionDetectorConfig::RULES_BY_LANG.fetch(lang.downcase) do |_k|
        raise ArgumentError, "Unknown language: #{lang}"
      end

    rule_config.each do |r|
      target = ExceptionDetectorConfig::RuleTarget.new(r[:pattern],
                                                       r[:to_state])
      @rules[r[:from_state]] << target
    end
  end

  @rules.each_value(&:uniq!)
end

Public Instance Methods

reset() click to toggle source
# File lib/fluent/plugin/exception_detector.rb, line 188
def reset
  @state = :start_state
end
update(line) click to toggle source

Updates the state machine and returns the trace detection status:

  • no_trace: ‘line’ does not belong to an exception trace,

  • start_trace: ‘line’ starts a detected exception trace,

  • inside: ‘line’ is part of a detected exception trace,

  • end: the detected exception trace ends after ‘line’.

# File lib/fluent/plugin/exception_detector.rb, line 167
def update(line)
  trace_seen_before = transition(line)
  # If the state machine fell back to the start state because there is no
  # defined transition for 'line', trigger another state transition because
  # 'line' may contain the beginning of another exception.
  transition(line) unless trace_seen_before
  new_state = @state
  trace_seen_after = new_state != :start_state

  case [trace_seen_before, trace_seen_after]
  when [true, true]
    :inside_trace
  when [true, false]
    :end_trace
  when [false, true]
    :start_trace
  else
    :no_trace
  end
end

Private Instance Methods

transition(line) click to toggle source

Executes a transition of the state machine for the given line. Returns false if the line does not match any transition rule and the state machine was reset to the initial state.

# File lib/fluent/plugin/exception_detector.rb, line 197
def transition(line)
  @rules[@state].each do |r|
    next unless line =~ r.pattern
    @state = r.to_state
    return true
  end
  @state = :start_state
  false
end