module RequestLogAnalyzer::Tracker
# Analyze the duration of a specific attribute
#
# === Options
# * :category Proc that handles request categorization for given fileformat (REQUEST_CATEGORIZER)
# * :duration The field containing the duration in the request hash.
# * :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).
# * :title Title do be displayed above the report
# * :unless Handle request if this proc is false for the handled request.
#
# The items in the update request hash are set during the creation of the Duration tracker.
#
# Example output:
# Request duration - top 20 by cumulative time | Hits | Sum. | Avg.
# ---------------------------------------------------------------------------------
# EmployeeController#show.html [GET] | 4742 | 4922.56s | 1.04s
# EmployeeController#update.html [POST] | 4647 | 2731.23s | 0.59s
# EmployeeController#index.html [GET] | 5802 | 1477.32s | 0.25s
# .............
class Duration < Base
include RequestLogAnalyzer::Tracker::StatisticsTracking
attr_reader :categories
# Check if duration and catagory option have been received,
def prepare
raise "No duration field set up for category tracker #{self.inspect}" unless options[:duration]
raise "No categorizer set up for duration tracker #{self.inspect}" unless options[:category]
unless options[:multiple]
@categorizer = create_lambda(options[:category])
@durationizer = create_lambda(options[:duration])
end
@categories = {}
end
# Get the duration information fron the request and store it in the different categories.
# request The request.
def update(request)
if options[:multiple]
found_categories = request.every(options[:category])
found_durations = request.every(options[:duration])
raise "Capture mismatch for multiple values in a request" unless found_categories.length == found_durations.length
found_categories.each_with_index { |cat, index| update_statistics(cat, found_durations[index]) }
else
category = @categorizer.call(request)
duration = @durationizer.call(request)
update_statistics(category, duration) if duration.kind_of?(Numeric) && category
end
end
# Block function to build a result table using a provided sorting function.
# output The output object.
# amount The number of rows in the report table (default 10).
# === Options
# * :title The title of the table
# * :sort The key to sort on (:hits, :cumulative, :average, :min or :max)
def report_table(output, sort, options = {}, &block)
output.puts
top_categories = output.slice_results(sorted_by(sort))
output.with_style(:top_line => true) do
output.table(*statistics_header(:title => options[:title], :highlight => sort)) do |rows|
top_categories.each { |(cat, info)| rows << statistics_row(cat) }
end
end
end
# Display a duration
def display_value(time)
case time
when nil then '-'
when 0...60 then "%0.02fs" % time
when 60...3600 then "%dm%02ds" % [time / 60, (time % 60).round]
else "%dh%02dm%02ds" % [time / 3600, (time % 3600) / 60, (time % 60).round]
end
end
# Generate a request duration report to the given output object
# By default colulative and average duration are generated.
# Any options for the report should have been set during initialize.
# output The output object
def report(output)
sortings = output.options[:sort] || [:sum, :mean]
sortings.each do |sorting|
report_table(output, sorting, :title => "#{title} - by #{sorting}")
end
end
# Returns the title of this tracker for reports
def title
options[:title] || 'Request duration'
end
# Returns all the categories and the tracked duration as a hash than can be exported to YAML
def to_yaml_object
return nil if @categories.empty?
@categories
end
end
end