2014-05-02 15:56:26 -04:00
|
|
|
class Criterion
|
|
|
|
attr_accessor :expression
|
2014-09-30 17:31:36 -04:00
|
|
|
attr_accessor :group_name
|
2014-05-02 15:56:26 -04:00
|
|
|
attr_accessor :count
|
|
|
|
attr_accessor :total_time
|
2014-09-30 17:31:36 -04:00
|
|
|
def initialize(expr, current_group_name='')
|
2014-09-29 14:43:05 -04:00
|
|
|
expr = expr.gsub(/\s+/, ' ')
|
2014-05-02 15:56:26 -04:00
|
|
|
@expression = Regexp.new(expr, Regexp::MULTILINE)
|
2014-09-30 17:31:36 -04:00
|
|
|
@group_name = current_group_name || ''
|
2014-05-02 15:56:26 -04:00
|
|
|
@count = 0
|
|
|
|
@total_time = 0.0
|
|
|
|
end
|
|
|
|
|
|
|
|
def add(log_record)
|
|
|
|
@count += 1
|
|
|
|
@total_time += log_record.time
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s()
|
2014-09-30 17:31:36 -04:00
|
|
|
format("%s: count=%5d, total_time=%9.3f, expression='%s'", self.class, @count, @total_time, @expression.source)
|
2014-05-02 15:56:26 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class QueryCriterion < Criterion
|
|
|
|
def test(log_record)
|
|
|
|
if expression.match(log_record.query)
|
|
|
|
add(log_record)
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class StackCriterion < Criterion
|
|
|
|
def test(log_record)
|
|
|
|
if expression.match(log_record.stack.join())
|
|
|
|
add(log_record)
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class WritingCriterion < Criterion
|
|
|
|
def initialize(expr, filename)
|
|
|
|
super(expr)
|
|
|
|
@file = File.new(filename, "w")
|
|
|
|
end
|
|
|
|
|
|
|
|
def add(log_record)
|
|
|
|
super
|
|
|
|
@file.write("#{@expression}\n\n--------------------------\n\n")
|
|
|
|
@file.write(log_record.to_s)
|
|
|
|
@file.write("\n--------------------------\n\n")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class UnmatchedCriterion < WritingCriterion
|
|
|
|
def initialize(filename)
|
|
|
|
super("UNMATCHED", filename)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class OverlapCriterion < WritingCriterion
|
|
|
|
def initialize(filename)
|
|
|
|
super("OVERLAP", filename)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-09-30 17:31:36 -04:00
|
|
|
class GroupList
|
|
|
|
def initialize(criteria)
|
|
|
|
@list = []
|
|
|
|
criteria.each do |c|
|
|
|
|
add_to_group(c)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def add_to_group(c)
|
|
|
|
@list.each do |g|
|
|
|
|
if g.name == c.group_name
|
|
|
|
g.add(c)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
@list << Group.new(c)
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s()
|
|
|
|
total_count = @list.inject(0) {|sum, g| sum + g.total_count}
|
|
|
|
total_time = @list.inject(0.0) {|sum, g| sum + g.total_time}
|
|
|
|
format("%s \nALL GROUPS: total count = %5d, total time = %9.3f seconds \n", @list.join("\n"), total_count, total_time)
|
|
|
|
end
|
|
|
|
|
|
|
|
class Group
|
|
|
|
attr :name
|
|
|
|
attr :criteria
|
|
|
|
attr :total_count
|
|
|
|
attr :total_time
|
|
|
|
def initialize(c)
|
|
|
|
@name = c.group_name
|
|
|
|
@criteria = []
|
|
|
|
@total_count = 0
|
|
|
|
@total_time = 0.0
|
|
|
|
add(c)
|
|
|
|
end
|
|
|
|
|
|
|
|
def add(c)
|
|
|
|
@criteria << c
|
|
|
|
@total_count += c.count
|
|
|
|
@total_time += c.total_time
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s()
|
|
|
|
format("GROUP '%s' \n %s\n %20s total count = %5d, total time = %9.3f seconds \n", @name, @criteria.join("\n "), ' ', @total_count, @total_time)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-05-02 15:56:26 -04:00
|
|
|
class Partition
|
|
|
|
# ------------------------------------------------------------------------------------
|
|
|
|
private
|
|
|
|
# ------------------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
def parse_partition_file(partition_file)
|
|
|
|
@criteria = []
|
2014-09-30 17:31:36 -04:00
|
|
|
current_group_name = ''
|
2014-05-02 15:56:26 -04:00
|
|
|
File.open(partition_file).each() do |line|
|
|
|
|
next if line.strip().empty? || line.start_with?('#')
|
|
|
|
if line.start_with?("QUERY ")
|
2014-09-30 17:31:36 -04:00
|
|
|
@criteria << QueryCriterion.new(line.slice(6..-1).strip, current_group_name)
|
2014-05-02 15:56:26 -04:00
|
|
|
elsif line.start_with?("STACK ")
|
2014-09-30 17:31:36 -04:00
|
|
|
@criteria << StackCriterion.new(line.slice(6..-1).strip, current_group_name)
|
|
|
|
elsif line.start_with?("GROUP ")
|
|
|
|
current_group_name = line.slice(6..-1).strip
|
|
|
|
elsif line.start_with?("GROUP")
|
|
|
|
current_group_name = ''
|
2014-05-02 15:56:26 -04:00
|
|
|
else
|
|
|
|
raise "Invalid line in partition file: '#{line}'"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def match_against_criteria(log_record)
|
|
|
|
matches = 0
|
|
|
|
@criteria.each do |c|
|
|
|
|
matches += c.test(log_record) ? 1 : 0
|
|
|
|
end
|
|
|
|
if matches == 0
|
|
|
|
@unmatched.add(log_record)
|
|
|
|
elsif matches > 1
|
|
|
|
@overlap.add(log_record)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------------
|
|
|
|
public
|
|
|
|
# ------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
def initialize(partition_file, overlap_file, unmatched_file)
|
|
|
|
parse_partition_file(partition_file)
|
|
|
|
@overlap = OverlapCriterion.new(overlap_file)
|
|
|
|
@unmatched = UnmatchedCriterion.new(unmatched_file)
|
|
|
|
end
|
|
|
|
|
|
|
|
def accumulate(log_record)
|
|
|
|
match_against_criteria(log_record)
|
|
|
|
end
|
|
|
|
|
|
|
|
def report
|
2014-09-30 17:31:36 -04:00
|
|
|
puts "#{GroupList.new(@criteria)}\n#{@overlap}\n#{@unmatched}\n"
|
2014-05-02 15:56:26 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|