Class: RequestLogAnalyzer::FileFormat::Rails

Inherits:
Base
  • Object
show all
Extended by:
CommonRegularExpressions
Defined in:
lib/request_log_analyzer/file_format/rails.rb

Overview

Default FileFormat class for Rails logs.

Instances will be created dynamically based on the lines you want it to parse. You can specify what lines should be included in the parser by providing a list to the create method as first argument.

Direct Known Subclasses

Oink, RailsDevelopment

Defined Under Namespace

Classes: Request

Constant Summary

RAILS_21_COMPLETED =

Rails < 2.1 completed line example Completed in 0.21665 (4 reqs/sec) | Rendering: 0.00926 (4%) | DB: 0.00000 (0%) | 200 OK [demo.nu/employees]

/Completed in (\d+\.\d{5}) \(\d+ reqs\/sec\) (?:\| Rendering: (\d+\.\d{5}) \(\d+\%\) )?(?:\| DB: (\d+\.\d{5}) \(\d+\%\) )?\| (\d\d\d).+\[(http.+)\]/
RAILS_22_COMPLETED =

Rails > 2.1 completed line example Completed in 614ms (View: 120, DB: 31) | 200 OK [floorplanner.local/demo]

/Completed in (\d+)ms \((?:View: (\d+))?,?(?:.?DB: (\d+))?\)? \| (\d{3}).+\[(http.+)\]/
LINE_DEFINITIONS =

A hash of definitions for all common lines in Rails logs.

{
  :processing => RequestLogAnalyzer::LineDefinition.new(:processing, :header => true,
        :teaser   => /Processing /,
        :regexp   => /Processing ((?:\w+::)*\w+)#(\w+)(?: to (\w+))? \(for (#{ip_address}) at (#{timestamp('%Y-%m-%d %H:%M:%S')})\) \[([A-Z]+)\]/,
        :captures => [{ :name => :controller, :type  => :string },
                      { :name => :action,     :type  => :string },
                      { :name => :format,     :type  => :string, :default => 'html' },
                      { :name => :ip,         :type  => :string },
                      { :name => :timestamp,  :type  => :timestamp },
                      { :name => :method,     :type  => :string }]),

  :completed => RequestLogAnalyzer::LineDefinition.new(:completed, :footer => true,
        :teaser   => /Completed in /,
        :regexp   => Regexp.union(RAILS_21_COMPLETED, RAILS_22_COMPLETED),
        :captures => [{ :name => :duration, :type => :duration, :unit => :sec },   # First old variant capture
                      { :name => :view,     :type => :duration, :unit => :sec },
                      { :name => :db,       :type => :duration, :unit => :sec },
                      { :name => :status,   :type => :integer },
                      { :name => :url,      :type => :string },                    # Last old variant capture
                      { :name => :duration, :type => :duration, :unit => :msec },  # First new variant capture
                      { :name => :view,     :type => :duration, :unit => :msec },
                      { :name => :db,       :type => :duration, :unit => :msec },
                      { :name => :status,   :type => :integer },
                      { :name => :url,      :type => :string }]),                  # Last new variant capture

  :failure => RequestLogAnalyzer::LineDefinition.new(:failure, :footer => true,
      :teaser   => /((?:[A-Z]\w*[a-z]\w+\:\:)*[A-Z]\w*[a-z]\w+) \((.*)\)(?: on line #(\d+) of (.+))?\:/,
      :regexp   => /((?:[A-Z]\w*[a-z]\w+\:\:)*[A-Z]\w*[a-z]\w+) \((.*)\)(?: on line #(\d+) of (.+))?\:\s*$/,
      :captures => [{ :name => :error,       :type => :string },
                    { :name => :message,     :type => :string },
                    { :name => :line,        :type => :integer },
                    { :name => :file,        :type => :string }]),

  :cache_hit => RequestLogAnalyzer::LineDefinition.new(:cache_hit,
      :regexp => /Filter chain halted as \[\#<ActionController::Filters::AroundFilter.*\@method=.*(?:Caching::Actions::ActionCacheFilter|action_controller\/caching\/actions\.rb).*\] did_not_yield/),

  :parameters => RequestLogAnalyzer::LineDefinition.new(:parameters,
      :teaser   => /  Parameters:/,
      :regexp   => /  Parameters:\s+(\{.*\})/,
      :captures => [{ :name => :params, :type => :eval }]),

  :rendered => RequestLogAnalyzer::LineDefinition.new(:rendered,
      :teaser   => /Rendered /,
      :regexp   => /Rendered (\w+(?:\/\w+)+) \((\d+\.\d+)ms\)/,
      :captures => [{ :name => :render_file,     :type  => :string },
                    { :name => :render_duration, :type  => :duration, :unit => :msec }]),

  :query_executed => RequestLogAnalyzer::LineDefinition.new(:query_executed,
      :regexp   => /\s+(?:\e\[4;36;1m)?((?:\w+::)*\w+) Load \((\d+\.\d+)ms\)(?:\e\[0m)?\s+(?:\e\[0;1m)?([^\e]+) ?(?:\e\[0m)?/,
      :captures => [{ :name => :query_class,    :type  => :string },
                    { :name => :query_duration, :type  => :duration, :unit => :msec },
                    { :name => :query_sql,      :type  => :sql }]),

  :query_cached => RequestLogAnalyzer::LineDefinition.new(:query_cached,
      :regexp   => /\s+(?:\e\[4;35;1m)?CACHE \((\d+\.\d+)ms\)(?:\e\[0m)?\s+(?:\e\[0m)?([^\e]+) ?(?:\e\[0m)?/,
      :captures => [{ :name => :cached_duration, :type  => :duration, :unit => :msec },
                    { :name => :cached_sql,      :type  => :sql }])
}
LINE_COLLECTIONS =

Definitions of common combinations of lines that can be parsed

{
  :minimal     => [:processing, :completed],
  :production  => [:processing, :completed, :failure, :cache_hit],
  :development => [:processing, :completed, :failure, :rendered, :query_executed, :query_cached],
  :all         => LINE_DEFINITIONS.keys
}
REQUEST_CATEGORIZER =

Simple function to categorize Rails requests using controller/actions/format and method.

Proc.new do |request|
  "#{request[:controller]}##{request[:action]}.#{request[:format]} [#{request[:method]}]"
end

Constants included from CommonRegularExpressions

CommonRegularExpressions::TIMESTAMP_PARTS

Constants inherited from Base

Base::Request

Instance Attribute Summary

Attributes inherited from Base

#line_definitions, #report_trackers

Class Method Summary (collapse)

Methods included from CommonRegularExpressions

anchored, hostname, hostname_or_ip_address, ip_address, timestamp

Methods inherited from Base

#captures?, format_definition, #initialize, line_definer, line_definition, #line_divider, #max_line_length, #parse_line, report, report_definer, #request, #request_class, #setup_environment, #valid_line_definitions?, #valid_request_class?, #well_formed?

Constructor Details

This class inherits a constructor from RequestLogAnalyzer::FileFormat::Base

Class Method Details

+ (Object) create(lines = 'production')

Creates a Rails FileFormat instance.

The lines that will be parsed can be defined by the argument to this function, which should be an array of line names, or a list of line names as comma separated string. The resulting report depends on the lines that will be parsed. You can also provide s string that describes a common set of lines, like “production”, “development” or “production”.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/request_log_analyzer/file_format/rails.rb', line 19

def self.create(lines = 'production')
  definitions_hash = line_definer.line_definitions.clone
  
  lines = lines.to_s.split(',') if lines.kind_of?(String)
  lines = [lines.to_s]          if lines.kind_of?(Symbol)
  
  lines.each do |line|
    line = line.to_sym
    if LINE_COLLECTIONS.has_key?(line)
      LINE_COLLECTIONS[line].each { |l| definitions_hash[l] ||= LINE_DEFINITIONS[l] }
    elsif LINE_DEFINITIONS.has_key?(line)
      definitions_hash[line] ||= LINE_DEFINITIONS[line]
    else
      raise "Unrecognized Rails log line name: #{line.inspect}!"
    end
  end

  return self.new(definitions_hash, report_trackers(definitions_hash))
end

+ (Object) report_trackers(lines)

Creates trackers based on the specified line definitions.

The more lines that will be parsed, the more information will appear in the report.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/request_log_analyzer/file_format/rails.rb', line 42

def self.report_trackers(lines)
  analyze = RequestLogAnalyzer::Aggregator::Summarizer::Definer.new
  
  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'
  
  if lines.has_key?(:cache_hit)
    analyze.frequency(:category => lambda { |request| request =~ :cache_hit ? 'Cache hit' : 'No hit' }, 
          :title => 'Rails action cache hits')
  end
  
  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 }
  
  if lines.has_key?(:failure)
    analyze.frequency :error, :title => 'Failed requests', :line_type => :failure
  end

  if lines.has_key?(:rendered)
    analyze.duration :render_duration, :category => :render_file, :multiple => true, :title => 'Partial rendering duration'
  end

  if lines.has_key?(:query_executed)
    analyze.duration :query_duration, :category => :query_sql, :multiple => true, :title => 'Query duration'
  end
  
  return analyze.trackers + report_definer.trackers
end