module RailsAnalyzer # Functions to summarize an array of requets. # Can calculate request counts, duratations, mean times etc. of all the requests given. class Summarizer attr_reader :actions attr_reader :errors attr_reader :request_count attr_reader :request_time_graph attr_reader :first_request_at attr_reader :last_request_at attr_accessor :blocker_duration DEFAULT_BLOCKER_DURATION = 1.0 # Initializer. Sets global variables # Options # * :calculate_database Calculate the database times if they are not explicitly logged. def initialize(options = {}) @actions = {} @blockers = {} @errors = {} @request_count = 0 @blocker_duration = DEFAULT_BLOCKER_DURATION @calculate_database = options[:calculate_database] @request_time_graph = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] end # Check if any of the request parsed had a timestamp. def has_timestamps? @first_request_at end # Parse a request string into a hash containing all keys found in the string. # Yields the hash found to the block operator. # request The request string to parse. # &block Block operator def group(request, &block) request[:duration] ||= 0 case request[:type] when :started @first_request_at ||= request[:timestamp] # assume time-based order of file @last_request_at = request[:timestamp] # assume time-based order of file @request_time_graph[request[:timestamp][11..12].to_i] +=1 when :completed @request_count += 1 hash = block_given? ? yield(request) : request.hash @actions[hash] ||= {:count => 0, :total_time => 0.0, :total_db_time => 0.0, :total_rendering_time => 0.0, :min_time => request[:duration], :max_time => request[:duration] } @actions[hash][:count] += 1 @actions[hash][:total_time] += request[:duration] @actions[hash][:total_db_time] += request[:db] if request[:db] @actions[hash][:total_db_time] += request[:duration] - request[:rendering] if @calculate_database @actions[hash][:total_rendering_time] += request[:rendering] if request[:rendering] @actions[hash][:min_time] = [@actions[hash][:min_time], request[:duration]].min @actions[hash][:max_time] = [@actions[hash][:min_time], request[:duration]].max @actions[hash][:mean_time] = @actions[hash][:total_time] / @actions[hash][:count].to_f @actions[hash][:mean_db_time] = @actions[hash][:total_db_time] / @actions[hash][:count].to_f @actions[hash][:mean_rendering_time] = @actions[hash][:total_rendering_time] / @actions[hash][:count].to_f if request[:duration] > @blocker_duration @blockers[hash] ||= { :count => 0, :total_time => 0.0 } @blockers[hash][:count] += 1 @blockers[hash][:total_time] += request[:duration] end when :failed hash = request[:error] @errors[hash] ||= {:count => 0, :exception_strings => {}} @errors[hash][:count] +=1 @errors[hash][:exception_strings][request[:exception_string]] ||= 0 @errors[hash][:exception_strings][request[:exception_string]] += 1 end end # Return a list of requests sorted on a specific action field # field The action field to sort by. # min_count Values which fall below this amount are not returned (default nil). def sort_actions_by(field, min_count = nil) actions = min_count.nil? ? @actions.to_a : @actions.delete_if { |k, v| v[:count] < min_count}.to_a actions.sort { |a, b| (a[1][field.to_sym] <=> b[1][field.to_sym]) } end # Returns a list of request blockers sorted by a specific field # field The action field to sort by. # min_count Values which fall below this amount are not returned (default @blocker_duration). def sort_blockers_by(field, min_count = @blocker_duration) blockers = min_count.nil? ? @blockers.to_a : @blockers.delete_if { |k, v| v[:count] < min_count}.to_a blockers.sort { |a, b| a[1][field.to_sym] <=> b[1][field.to_sym] } end # Returns a list of request blockers sorted by a specific field # field The action field to sort by. # min_count Values which fall below this amount are not returned (default @blocker_duration). def sort_errors_by(field, min_count = nil) errors = min_count.nil? ? @errors.to_a : @errors.delete_if { |k, v| v[:count] < min_count}.to_a errors.sort { |a, b| a[1][field.to_sym] <=> b[1][field.to_sym] } end # Calculate the duration of a request # Returns a DateTime object if possible, 0 otherwise. def duration (@last_request_at && @first_request_at) ? (DateTime.parse(@last_request_at) - DateTime.parse(@first_request_at)).round : 0 end # Check if the request time graph usable data. def request_time_graph? @request_time_graph.uniq != [0] && duration > 0 end end end