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