module RequestLogAnalyzer::FileFormat # Default FileFormat class for Rails 3 logs. # # For now, this is just a basic implementation. It will probaby change after # Rails 3 final has been released. class Rails3 < Base extend CommonRegularExpressions # beta4: Started GET "/" for 127.0.0.1 at Wed Jul 07 09:13:27 -0700 2010 (different time format) line_definition :started do |line| line.header = true line.teaser = /Started / line.regexp = /Started ([A-Z]+) "([^"]+)" for (#{ip_address}) at (#{timestamp('%a %b %d %H:%M:%S %z %Y')}|#{timestamp('%Y-%m-%d %H:%M:%S %z')})/ line.capture(:method) line.capture(:path) line.capture(:ip) line.capture(:timestamp).as(:timestamp) end # Processing by QueriesController#index as HTML line_definition :processing do |line| line.teaser = /Processing by / line.regexp = /Processing by ([A-Za-z0-9\-:]+)\#(\w+) as ([\w\/\*]*)/ line.capture(:controller) line.capture(:action) line.capture(:format) end # Parameters: {"action"=>"cached", "controller"=>"cached"} line_definition :parameters do |line| line.teaser = /\bParameters:/ line.regexp = /\bParameters:\s+(\{.*\})/ line.capture(:params).as(:eval) end # Completed 200 OK in 224ms (Views: 200.2ms | ActiveRecord: 3.4ms) # Completed 302 Found in 23ms # Completed in 189ms line_definition :completed do |line| line.footer = true line.teaser = /Completed / line.regexp = /Completed (\d+)? .*in (\d+(?:\.\d+)?)ms(?:[^\(]*\(Views: (\d+(?:\.\d+)?)ms .* ActiveRecord: (\d+(?:\.\d+)?)ms.*\))?/ line.capture(:status).as(:integer) line.capture(:duration).as(:duration, unit: :msec) line.capture(:view).as(:duration, unit: :msec) line.capture(:db).as(:duration, unit: :msec) end # ActionController::RoutingError (No route matches [GET] "/missing_stuff"): line_definition :routing_errors do |line| line.teaser = /RoutingError/ line.regexp = /No route matches \[([A-Z]+)\] "([^"]+)"/ line.capture(:missing_resource_method).as(:string) line.capture(:missing_resource).as(:string) end # ActionView::Template::Error (undefined local variable or method `field' for #) on line #3 of /Users/willem/Code/warehouse/app/views/queries/execute.csv.erb: line_definition :failure do |line| line.footer = true line.regexp = /((?:[A-Z]\w*[a-z]\w+\:\:)*[A-Z]\w*[a-z]\w+) \((.*)\)(?: on line #(\d+) of (.+))?\:\s*$/ line.capture(:error) line.capture(:message) line.capture(:line).as(:integer) line.capture(:file) end # Rendered queries/index.html.erb (0.6ms) line_definition :rendered do |line| line.compound = [:partial_duration] line.teaser = /\bRendered / line.regexp = /\bRendered ([a-zA-Z0-9_\-\/.]+(?:\/[a-zA-Z0-9_\-.]+)+)(?:\ within\ .*?)? \((\d+(?:\.\d+)?)ms\)/ line.capture(:rendered_file) line.capture(:partial_duration).as(:duration, unit: :msec) end # # Not parsed at the moment: # SQL (0.2ms) SET SQL_AUTO_IS_NULL=0 # Query Load (0.4ms) SELECT `queries`.* FROM `queries` # Rendered collection (0.0ms) REQUEST_CATEGORIZER = lambda { |request| "#{request[:controller]}##{request[:action]}.#{request[:format]}" } report do |analyze| analyze.timespan analyze.hourly_spread analyze.frequency category: REQUEST_CATEGORIZER, title: 'Most requested' analyze.frequency :method, title: 'HTTP methods' analyze.frequency :status, title: 'HTTP statuses returned' analyze.duration :duration, category: REQUEST_CATEGORIZER, title: 'Request duration', line_type: :completed analyze.duration :partial_duration, category: :rendered_file, title: 'Partials rendering time', line_type: :rendered analyze.duration :view, category: REQUEST_CATEGORIZER, title: 'View rendering time', line_type: :completed analyze.duration :db, category: REQUEST_CATEGORIZER, title: 'Database time', line_type: :completed analyze.frequency category: REQUEST_CATEGORIZER, title: 'Process blockers (> 1 sec duration)', if: lambda { |request| request[:duration] && request[:duration] > 1.0 } analyze.frequency category: lambda { |x| "[#{x[:missing_resource_method]}] #{x[:missing_resource]}" }, title: 'Routing Errors', if: lambda { |request| !request[:missing_resource].nil? } end class Request < RequestLogAnalyzer::Request # Used to handle conversion of abbrev. month name to a digit MONTHS = %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec) def convert_timestamp(value, _definition) # the time value can be in 2 formats: # - 2010-10-26 02:27:15 +0000 (ruby 1.9.2) # - Thu Oct 25 16:15:18 -0800 2010 if value =~ /^#{CommonRegularExpressions::TIMESTAMP_PARTS['Y']}/ value.gsub!(/\W/, '') value[0..13].to_i else value.gsub!(/\W/, '') time_as_str = value[-4..-1] # year # convert the month to a 2-digit representation month = MONTHS.index(value[3..5]) + 1 month < 10 ? time_as_str << "0#{month}" : time_as_str << month.to_s time_as_str << value[6..13] # day of month + time time_as_str.to_i end end end end end