module RequestLogAnalyzer::Tracker
# Catagorize requests by frequency.
# Count and analyze requests for a specific attribute
#
# === Options
# * :category Proc that handles the request categorization.
# * :if Proc that has to return !nil for a request to be passed to the tracker.
# * :line_type The line type that contains the duration field (determined by the category proc).
# * :nils Track undetermined methods.
# * :title Title do be displayed above the report.
# * :unless Proc that has to return nil for a request to be passed to the tracker.
#
# The items in the update request hash are set during the creation of the Duration tracker.
#
# Example output:
# HTTP methods
# ----------------------------------------------------------------------
# GET | 22248 hits (46.2%) |=================
# PUT | 13685 hits (28.4%) |===========
# POST | 11662 hits (24.2%) |=========
# DELETE | 512 hits (1.1%) |
class Frequency < Base
attr_reader :categories
# Check if categories are set up
def prepare
options[:category] = options[:value] if options[:value] && !options[:category]
fail "No categorizer set up for category tracker #{inspect}" unless options[:category]
@categorizer = create_lambda(options[:category]) unless options[:multiple]
# Initialize the categories. Use the list of category names to
@categories = {}
options[:all_categories].each { |cat| @categories[cat] = 0 } if options[:all_categories].is_a?(Enumerable)
end
# Check HTTP method of a request and store that in the categories hash.
# request The request.
def update(request)
if options[:multiple]
cats = request.every(options[:category])
cats.each do |cat|
if cat || options[:nils]
@categories[cat] ||= 0
@categories[cat] += 1
end
end
else
cat = @categorizer.call(request)
if cat || options[:nils]
@categories[cat] ||= 0
@categories[cat] += 1
end
end
end
# Return the amount of times a HTTP method has been encountered
# cat The HTTP method (:get, :put, :post or :delete)
def frequency(cat)
categories[cat] || 0
end
# Return the overall frequency
def overall_frequency
categories.reduce(0) { |carry, item| carry + item[1] }
end
# Return the methods sorted by frequency
def sorted_by_frequency
@categories.sort { |a, b| b[1] <=> a[1] }
end
# Generate a HTTP method frequency report to the given output object.
# Any options for the report should have been set during initialize.
# output The output object
def report(output)
output.title(options[:title]) if options[:title]
if @categories.empty?
output << "None found.\n"
else
sorted_categories = output.slice_results(sorted_by_frequency)
total_hits = overall_frequency
output.table({ align: :left }, { align: :right }, { align: :right }, { type: :ratio, width: :rest }) do |rows|
sorted_categories.each do |(cat, count)|
rows << [cat, "#{count} hits", '%0.1f%%' % ((count.to_f / total_hits.to_f) * 100.0), (count.to_f / total_hits.to_f)]
end
end
end
end
# Returns a hash with the categories of every category that can be exported to YAML
def to_yaml_object
return nil if @categories.empty?
@categories
end
# Returns the title of this tracker for reports
def title
options[:title] || 'Request frequency'
end
end
end