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')})/
      
      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 (\w+)\#(\w+) as (\w+)/
      
      line.capture(:controller)
      line.capture(:action)
      line.capture(:format)
    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+)ms(?:[^(]*\(Views: ((?:\d|\.)+)ms .* ActiveRecord: ((?:\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
    
    # ActionView::Template::Error (undefined local variable or method `field' for #<Class>) 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
    
    # # 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)
    # Rendered queries/index.html.erb (0.6ms)
    
    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 :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 }
    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)
        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
        
        time_as_str << value[6..13] # day of month + time
        time_as_str.to_i
      end
    end

  end
end