request-log-analyzer 1.9.10 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. data/.gitignore +4 -1
  2. data/.infinity_test +8 -0
  3. data/Gemfile +2 -0
  4. data/bin/request-log-analyzer +0 -1
  5. data/lib/request_log_analyzer/aggregator/database_inserter.rb +2 -1
  6. data/lib/request_log_analyzer/aggregator.rb +5 -5
  7. data/lib/request_log_analyzer/controller.rb +0 -3
  8. data/lib/request_log_analyzer/database.rb +6 -7
  9. data/lib/request_log_analyzer/file_format/apache.rb +19 -28
  10. data/lib/request_log_analyzer/file_format/delayed_job2.rb +2 -2
  11. data/lib/request_log_analyzer/file_format/delayed_job21.rb +2 -2
  12. data/lib/request_log_analyzer/file_format/haproxy.rb +232 -0
  13. data/lib/request_log_analyzer/file_format/mysql.rb +5 -5
  14. data/lib/request_log_analyzer/file_format/rails3.rb +9 -2
  15. data/lib/request_log_analyzer/file_format/w3c.rb +56 -0
  16. data/lib/request_log_analyzer/file_format.rb +46 -15
  17. data/lib/request_log_analyzer/filter.rb +4 -5
  18. data/lib/request_log_analyzer/line_definition.rb +6 -4
  19. data/lib/request_log_analyzer/output.rb +3 -5
  20. data/lib/request_log_analyzer/request.rb +7 -0
  21. data/lib/request_log_analyzer/source/log_parser.rb +56 -4
  22. data/lib/request_log_analyzer/source.rb +3 -4
  23. data/lib/request_log_analyzer/tracker.rb +8 -8
  24. data/lib/request_log_analyzer.rb +15 -29
  25. data/request-log-analyzer.gemspec +4 -4
  26. data/spec/fixtures/mysql_slow_query.log +0 -1
  27. data/spec/integration/command_line_usage_spec.rb +0 -5
  28. data/spec/lib/helpers.rb +2 -2
  29. data/spec/lib/matchers.rb +38 -7
  30. data/spec/lib/mocks.rb +1 -5
  31. data/spec/spec_helper.rb +3 -2
  32. data/spec/unit/database/base_class_spec.rb +1 -0
  33. data/spec/unit/file_format/amazon_s3_format_spec.rb +58 -55
  34. data/spec/unit/file_format/apache_format_spec.rb +90 -161
  35. data/spec/unit/file_format/common_regular_expressions_spec.rb +51 -26
  36. data/spec/unit/file_format/delayed_job21_format_spec.rb +22 -31
  37. data/spec/unit/file_format/delayed_job2_format_spec.rb +27 -32
  38. data/spec/unit/file_format/delayed_job_format_spec.rb +44 -63
  39. data/spec/unit/file_format/haproxy_format_spec.rb +82 -0
  40. data/spec/unit/file_format/line_definition_spec.rb +26 -33
  41. data/spec/unit/file_format/merb_format_spec.rb +22 -37
  42. data/spec/unit/file_format/mysql_format_spec.rb +80 -123
  43. data/spec/unit/file_format/oink_format_spec.rb +29 -61
  44. data/spec/unit/file_format/postgresql_format_spec.rb +10 -21
  45. data/spec/unit/file_format/rack_format_spec.rb +49 -44
  46. data/spec/unit/file_format/rails3_format_spec.rb +46 -38
  47. data/spec/unit/file_format/rails_format_spec.rb +52 -68
  48. data/spec/unit/file_format/w3c_format_spec.rb +47 -0
  49. data/spec/unit/source/log_parser_spec.rb +1 -1
  50. metadata +12 -7
  51. data/lib/mixins/gets_memory_protection.rb +0 -80
  52. data/lib/request_log_analyzer/output/fancy_html.rb +0 -44
  53. data/lib/request_log_analyzer/source/database_loader.rb +0 -87
data/.gitignore CHANGED
@@ -8,4 +8,7 @@ requests.db
8
8
  /tmp
9
9
  /classes
10
10
  /files
11
- /coverage
11
+ /coverage
12
+ Gemfile.lock
13
+ /.bundle/
14
+ *.rbc
data/.infinity_test ADDED
@@ -0,0 +1,8 @@
1
+ infinity_test do
2
+
3
+ use :rubies => %w(1.8.7 1.9.2 ree), :test_framework => :rspec # jruby rbx
4
+
5
+ before(:each_ruby) do |environment|
6
+ environment.system('bundle install')
7
+ end
8
+ end
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
@@ -58,7 +58,6 @@ begin
58
58
 
59
59
  command_line.switch(:debug)
60
60
  command_line.switch(:no_progress)
61
- command_line.switch(:gets_memory_protection)
62
61
  command_line.switch(:silent)
63
62
 
64
63
  command_line.minimum_parameters = 1
@@ -1,4 +1,3 @@
1
-
2
1
  module RequestLogAnalyzer::Aggregator
3
2
 
4
3
  # The database aggregator will create an SQLite3 database with all parsed request information.
@@ -19,6 +18,8 @@ module RequestLogAnalyzer::Aggregator
19
18
  # Establishes a connection to the database and creates the necessary database schema for the
20
19
  # current file format
21
20
  def prepare
21
+ require 'request_log_analyzer/database'
22
+
22
23
  @sources = {}
23
24
  @database = RequestLogAnalyzer::Database.new(options[:database])
24
25
  @database.file_format = source.file_format
@@ -1,9 +1,5 @@
1
1
  module RequestLogAnalyzer::Aggregator
2
2
 
3
- autoload :Echo, 'request_log_analyzer/aggregator/echo'
4
- autoload :Summarizer, 'request_log_analyzer/aggregator/summarizer'
5
- autoload :DatabaseInserter, 'request_log_analyzer/aggregator/database_inserter'
6
-
7
3
  # The base class of an aggregator. This class provides the interface to which
8
4
  # every aggregator should comply (by simply subclassing this class).
9
5
  class Base
@@ -46,4 +42,8 @@ module RequestLogAnalyzer::Aggregator
46
42
  end
47
43
 
48
44
  end
49
- end
45
+ end
46
+
47
+ require 'request_log_analyzer/aggregator/echo'
48
+ require 'request_log_analyzer/aggregator/summarizer'
49
+ require 'request_log_analyzer/aggregator/database_inserter'
@@ -21,8 +21,6 @@ module RequestLogAnalyzer
21
21
  # <tt>arguments<tt> A CommandLine::Arguments hash containing parsed commandline parameters.
22
22
  def self.build_from_arguments(arguments)
23
23
 
24
- require 'mixins/gets_memory_protection' if arguments[:gets_memory_protection]
25
-
26
24
  options = {}
27
25
 
28
26
  # Copy fields
@@ -58,7 +56,6 @@ module RequestLogAnalyzer
58
56
 
59
57
  # Handle output format casing
60
58
  if options[:output].class == String
61
- options[:output] = 'FancyHTML' if options[:output] =~ /^fancy_?html$/i
62
59
  options[:output] = 'HTML' if options[:output] =~ /^html$/i
63
60
  options[:output] = 'FixedWidth' if options[:output] =~ /^fixed_?width$/i
64
61
  end
@@ -3,12 +3,7 @@ require 'active_record'
3
3
 
4
4
  class RequestLogAnalyzer::Database
5
5
 
6
- autoload :Connection, 'request_log_analyzer/database/connection'
7
- autoload :Base, 'request_log_analyzer/database/base'
8
- autoload :Request, 'request_log_analyzer/database/request'
9
- autoload :Source, 'request_log_analyzer/database/source'
10
- autoload :Warning, 'request_log_analyzer/database/warning'
11
-
6
+ require 'request_log_analyzer/database/connection'
12
7
  include RequestLogAnalyzer::Database::Connection
13
8
 
14
9
  attr_accessor :file_format
@@ -100,5 +95,9 @@ class RequestLogAnalyzer::Database
100
95
  end
101
96
  end
102
97
  end
103
-
104
98
  end
99
+
100
+ require 'request_log_analyzer/database/base'
101
+ require 'request_log_analyzer/database/request'
102
+ require 'request_log_analyzer/database/source'
103
+ require 'request_log_analyzer/database/warning'
@@ -30,20 +30,23 @@ module RequestLogAnalyzer::FileFormat
30
30
 
31
31
  # A hash that defines how the log format directives should be parsed.
32
32
  LOG_DIRECTIVES = {
33
- '%' => { :regexp => '%', :captures => [] },
34
- 'h' => { :regexp => '([A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+)', :captures => [{:name => :remote_host, :type => :string}] },
35
- 'a' => { :regexp => "(#{ip_address})", :captures => [{:name => :remote_ip, :type => :string}] },
36
- 'b' => { :regexp => '(\d+|-)', :captures => [{:name => :bytes_sent, :type => :traffic}] },
37
- 'c' => { :regexp => '(\+|\-|\X)', :captures => [{:name => :connection_status, :type => :integer}] },
38
- 'D' => { :regexp => '(\d+|-)', :captures => [{:name => :duration, :type => :duration, :unit => :musec}] },
39
- 'l' => { :regexp => '([\w-]+)', :captures => [{:name => :remote_logname, :type => :nillable_string}] },
40
- 'T' => { :regexp => '((?:\d+(?:\.\d+))|-)', :captures => [{:name => :duration, :type => :duration, :unit => :sec}] },
41
- 't' => { :regexp => "\\[(#{APACHE_TIMESTAMP})?\\]", :captures => [{:name => :timestamp, :type => :timestamp}] },
42
- 's' => { :regexp => '(\d{3})', :captures => [{:name => :http_status, :type => :integer}] },
43
- 'u' => { :regexp => '(\w+|-)', :captures => [{:name => :user, :type => :nillable_string}] },
44
- 'U' => { :regexp => '(\/\S*)', :captures => [{:name => :path, :type => :string}] },
45
- 'r' => { :regexp => '([A-Z]+) (\S+) HTTP\/(\d+(?:\.\d+)*)', :captures => [{:name => :http_method, :type => :string},
46
- {:name => :path, :type => :path}, {:name => :http_version, :type => :string}]},
33
+ '%' => { nil => { :regexp => '%', :captures => [] } },
34
+ 'h' => { nil => { :regexp => "(#{hostname_or_ip_address})", :captures => [{:name => :remote_host, :type => :string}] } },
35
+ 'a' => { nil => { :regexp => "(#{ip_address})", :captures => [{:name => :remote_ip, :type => :string}] } },
36
+ 'b' => { nil => { :regexp => '(\d+|-)', :captures => [{:name => :bytes_sent, :type => :traffic}] } },
37
+ 'c' => { nil => { :regexp => '(\+|\-|\X)', :captures => [{:name => :connection_status, :type => :integer}] } },
38
+ 'D' => { nil => { :regexp => '(\d+|-)', :captures => [ {:name => :duration, :type => :duration, :unit => :musec }] },
39
+ 'micro' => { :regexp => '(\d+|-)', :captures => [ {:name => :duration, :type => :duration, :unit => :musec }] },
40
+ 'milli' => { :regexp => '(\d+|-)', :captures => [ {:name => :duration, :type => :duration, :unit => :msec }] }
41
+ },
42
+ 'l' => { nil => { :regexp => '([\w-]+)', :captures => [{:name => :remote_logname, :type => :nillable_string}] } },
43
+ 'T' => { nil => { :regexp => '((?:\d+(?:\.\d+))|-)', :captures => [{:name => :duration, :type => :duration, :unit => :sec}] } },
44
+ 't' => { nil => { :regexp => "\\[(#{APACHE_TIMESTAMP})?\\]", :captures => [{:name => :timestamp, :type => :timestamp}] } },
45
+ 's' => { nil => { :regexp => '(\d{3})', :captures => [{:name => :http_status, :type => :integer}] } },
46
+ 'u' => { nil => { :regexp => '(\w+|-)', :captures => [{:name => :user, :type => :nillable_string}] } },
47
+ 'U' => { nil => { :regexp => '(\/\S*)', :captures => [{:name => :path, :type => :string}] } },
48
+ 'r' => { nil => { :regexp => '([A-Z]+) (\S+) HTTP\/(\d+(?:\.\d+)*)', :captures => [{:name => :http_method, :type => :string},
49
+ {:name => :path, :type => :path}, {:name => :http_version, :type => :string}]} },
47
50
  'i' => { 'Referer' => { :regexp => '(\S+)', :captures => [{:name => :referer, :type => :nillable_string}] },
48
51
  'User-agent' => { :regexp => '(.*)', :captures => [{:name => :user_agent, :type => :user_agent}] }
49
52
  }
@@ -72,14 +75,13 @@ module RequestLogAnalyzer::FileFormat
72
75
 
73
76
  if variable
74
77
  # Check if we recognize the log directive
75
- directive = LOG_DIRECTIVES[variable]
76
- directive = directive[arg] if directive && arg
78
+ directive = LOG_DIRECTIVES[variable][arg] rescue nil
77
79
 
78
80
  if directive
79
81
  line_regexp << directive[:regexp] # Parse the value of the directive
80
82
  captures += directive[:captures] # Add the directive's information to the captures
81
83
  else
82
- puts "%#{directive} log directiven not yet supported, field is ignored."
84
+ puts "Apache log directive %#{arg}#{variable} is not yet supported by RLA, the field will be ignored."
83
85
  line_regexp << '.*' # Just accept any input for this literal
84
86
  end
85
87
  end
@@ -126,22 +128,11 @@ module RequestLogAnalyzer::FileFormat
126
128
  "#{value[7,4]}#{MONTHS[value[3,3]]}#{value[0,2]}#{value[12,2]}#{value[15,2]}#{value[18,2]}".to_i
127
129
  end
128
130
 
129
- # This function can be overridden to rewrite the path for better categorization in the
130
- # reports.
131
- def convert_path(value, definition)
132
- value
133
- end
134
-
135
131
  # This function can be overridden to simplify the user agent string for better
136
132
  # categorization in the reports
137
133
  def convert_user_agent(value, definition)
138
134
  value # TODO
139
135
  end
140
-
141
- # Make sure that the string '-' is parsed as a nil value.
142
- def convert_nillable_string(value, definition)
143
- value == '-' ? nil : value
144
- end
145
136
  end
146
137
  end
147
138
  end
@@ -8,7 +8,7 @@ module RequestLogAnalyzer::FileFormat
8
8
 
9
9
  line_definition :job_lock do |line|
10
10
  line.header = true
11
- line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \* \[Worker\(\S+ host:(\S+) pid:(\d+)\)\] acquired lock on (\S+)/
11
+ line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \* \[Worker\(\S+ host:(#{hostname_or_ip_address}) pid:(\d+)\)\] acquired lock on (\S+)/
12
12
 
13
13
  line.capture(:timestamp).as(:timestamp)
14
14
  line.capture(:host)
@@ -18,7 +18,7 @@ module RequestLogAnalyzer::FileFormat
18
18
 
19
19
  line_definition :job_completed do |line|
20
20
  line.footer = true
21
- line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \* \[JOB\] \S+ host:(\S+) pid:(\d+) completed after (\d+\.\d+)/
21
+ line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \* \[JOB\] \S+ host:(#{hostname_or_ip_address}) pid:(\d+) completed after (\d+\.\d+)/
22
22
  line.capture(:timestamp).as(:timestamp)
23
23
  line.capture(:host)
24
24
  line.capture(:pid).as(:integer)
@@ -8,7 +8,7 @@ module RequestLogAnalyzer::FileFormat
8
8
 
9
9
  line_definition :job_lock do |line|
10
10
  line.header = true
11
- line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \[Worker\(\S+ host:(\S+) pid:(\d+)\)\] acquired lock on (\S+)/
11
+ line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \[Worker\(\S+ host:(#{hostname_or_ip_address}) pid:(\d+)\)\] acquired lock on (\S+)/
12
12
 
13
13
  line.capture(:timestamp).as(:timestamp)
14
14
  line.capture(:host)
@@ -18,7 +18,7 @@ module RequestLogAnalyzer::FileFormat
18
18
 
19
19
  line_definition :job_completed do |line|
20
20
  line.footer = true
21
- line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \[Worker\(\S+ host:(\S+) pid:(\d+)\)\] (\S+) completed after (\d+\.\d+)/
21
+ line.regexp = /(#{timestamp('%Y-%m-%dT%H:%M:%S%z')}): \[Worker\(\S+ host:(#{hostname_or_ip_address}) pid:(\d+)\)\] (\S+) completed after (\d+\.\d+)/
22
22
  line.capture(:timestamp).as(:timestamp)
23
23
  line.capture(:host)
24
24
  line.capture(:pid).as(:integer)
@@ -0,0 +1,232 @@
1
+ module RequestLogAnalyzer::FileFormat
2
+
3
+ class Haproxy < RequestLogAnalyzer::FileFormat::Base
4
+
5
+ extend CommonRegularExpressions
6
+
7
+ # substitute version specific parts of the haproxy entry regexp.
8
+ def self.compose_regexp(millisecs, backends, counters, connections, queues)
9
+ %r{
10
+ (#{ip_address}):\d+\s # client_ip ':' client_port
11
+ \[(#{timestamp('%d/%b/%Y:%H:%M:%S')})#{millisecs}\]\s # '[' accept_date ']'
12
+ (\S+)\s # frontend_name
13
+ #{backends}
14
+ #{counters}
15
+ (\d+)\s # status_code
16
+ \+?(\d+)\s # bytes_read
17
+ (\S+)\s # captured_request_cookie
18
+ (\S+)\s # captured_response_cookie
19
+ (\w|-)(\w|-)(\w|-)(\w|-)\s # termination_state
20
+ #{connections}
21
+ #{queues}
22
+ (\S*)\s? # captured_request_headers
23
+ (\S*)\s? # captured_response_headers
24
+ "([^"]*)" # '"' http_request '"'
25
+ }x
26
+ end
27
+
28
+ # Define line types
29
+
30
+ # line definition for haproxy 1.3 and higher
31
+ line_definition :haproxy13 do |line|
32
+ line.header = true
33
+ line.footer = true
34
+ line.teaser = /\.\d{3}\] \S+ \S+\/\S+ / # .millisecs] frontend_name backend_name/server_name
35
+
36
+ line.regexp = compose_regexp(
37
+ '\.\d{3}', # millisecs
38
+ '(\S+)\/(\S+)\s', # backend_name '/' server_name
39
+ '(\d+|-1)\/(\d+|-1)\/(\d+|-1)\/(\d+|-1)\/\+?(\d+)\s', # Tq '/' Tw '/' Tc '/' Tr '/' Tt
40
+ '(\d+)\/(\d+)\/(\d+)\/(\d+)\/\+?(\d+)\s', # actconn '/' feconn '/' beconn '/' srv_conn '/' retries
41
+ '(\d+)\/(\d+)\s' # srv_queue '/' backend_queue
42
+ )
43
+
44
+ line.capture(:client_ip).as(:string)
45
+ line.capture(:timestamp).as(:timestamp)
46
+ line.capture(:frontend_name).as(:string)
47
+ line.capture(:backend_name).as(:string)
48
+ line.capture(:server_name).as(:string)
49
+ line.capture(:tq).as(:nillable_duration, :unit => :msec)
50
+ line.capture(:tw).as(:nillable_duration, :unit => :msec)
51
+ line.capture(:tc).as(:nillable_duration, :unit => :msec)
52
+ line.capture(:tr).as(:nillable_duration, :unit => :msec)
53
+ line.capture(:tt).as(:duration, :unit => :msec)
54
+ line.capture(:status_code).as(:integer)
55
+ line.capture(:bytes_read).as(:traffic, :unit => :byte)
56
+ line.capture(:captured_request_cookie).as(:nillable_string)
57
+ line.capture(:captured_response_cookie).as(:nillable_string)
58
+ line.capture(:termination_event_code).as(:nillable_string)
59
+ line.capture(:terminated_session_state).as(:nillable_string)
60
+ line.capture(:clientside_persistence_cookie).as(:nillable_string)
61
+ line.capture(:serverside_persistence_cookie).as(:nillable_string)
62
+ line.capture(:actconn).as(:integer)
63
+ line.capture(:feconn).as(:integer)
64
+ line.capture(:beconn).as(:integer)
65
+ line.capture(:srv_conn).as(:integer)
66
+ line.capture(:retries).as(:integer)
67
+ line.capture(:srv_queue).as(:integer)
68
+ line.capture(:backend_queue).as(:integer)
69
+ line.capture(:captured_request_headers).as(:nillable_string)
70
+ line.capture(:captured_response_headers).as(:nillable_string)
71
+ line.capture(:http_request).as(:nillable_string)
72
+ end
73
+
74
+ # haproxy 1.2 has a few fields less than 1.3+
75
+ line_definition :haproxy12 do |line|
76
+ line.header = true
77
+ line.footer = true
78
+ line.teaser = /\.\d{3}\] \S+ \S+ / # .millisecs] frontend_name server_name
79
+
80
+ line.regexp = compose_regexp(
81
+ '\.\d{3}', # millisecs
82
+ '(\S+)\s', # server_name
83
+ '(\d+|-1)\/(\d+|-1)\/(\d+|-1)\/(\d+|-1)\/\+?(\d+)\s', # Tq '/' Tw '/' Tc '/' Tr '/' Tt
84
+ '(\d+)\/(\d+)\/(\d+)\s', # srv_conn '/' listener_conn '/' process_conn
85
+ '(\d+)\/(\d+)\s' # srv_queue '/' backend_queue
86
+ )
87
+
88
+ line.capture(:client_ip).as(:string)
89
+ line.capture(:timestamp).as(:timestamp)
90
+ line.capture(:frontend_name).as(:string)
91
+ line.capture(:server_name).as(:string)
92
+ line.capture(:tq).as(:nillable_duration, :unit => :msec)
93
+ line.capture(:tw).as(:nillable_duration, :unit => :msec)
94
+ line.capture(:tc).as(:nillable_duration, :unit => :msec)
95
+ line.capture(:tr).as(:nillable_duration, :unit => :msec)
96
+ line.capture(:tt).as(:duration, :unit => :msec)
97
+ line.capture(:status_code).as(:integer)
98
+ line.capture(:bytes_read).as(:traffic, :unit => :byte)
99
+ line.capture(:captured_request_cookie).as(:nillable_string)
100
+ line.capture(:captured_response_cookie).as(:nillable_string)
101
+ line.capture(:termination_event_code).as(:nillable_string)
102
+ line.capture(:terminated_session_state).as(:nillable_string)
103
+ line.capture(:clientside_persistence_cookie).as(:nillable_string)
104
+ line.capture(:serverside_persistence_cookie).as(:nillable_string)
105
+ line.capture(:srv_conn).as(:integer)
106
+ line.capture(:listener_conn).as(:integer)
107
+ line.capture(:process_conn).as(:integer)
108
+ line.capture(:srv_queue).as(:integer)
109
+ line.capture(:backend_queue).as(:integer)
110
+ line.capture(:captured_request_headers).as(:nillable_string)
111
+ line.capture(:captured_response_headers).as(:nillable_string)
112
+ line.capture(:http_request).as(:nillable_string)
113
+ end
114
+
115
+ # and haproxy 1.1 has even less fields
116
+ line_definition :haproxy11 do |line|
117
+ line.header = true
118
+ line.footer = true
119
+ line.teaser = /:\d{2}\] \S+ \S+ / # :secs] frontend_name server_name
120
+
121
+ line.regexp = compose_regexp(
122
+ '', # no millisec precision in this version of haproxy
123
+ '(\S+)\s', # server_name
124
+ '(\d+|-1)\/(\d+|-1)\/(\d+|-1)\/\+?(\d+)\s', # Tq '/' Tc '/' Tr '/' Tt
125
+ '(\d+)\/(\d+)\s', # listener_conn '/' process_conn
126
+ '' # no queues in this version of haproxy
127
+ )
128
+
129
+ line.capture(:client_ip).as(:string)
130
+ line.capture(:timestamp).as(:timestamp)
131
+ line.capture(:frontend_name).as(:string)
132
+ line.capture(:server_name).as(:string)
133
+ line.capture(:tq).as(:nillable_duration, :unit => :msec)
134
+ line.capture(:tc).as(:nillable_duration, :unit => :msec)
135
+ line.capture(:tr).as(:nillable_duration, :unit => :msec)
136
+ line.capture(:tt).as(:duration, :unit => :msec)
137
+ line.capture(:status_code).as(:integer)
138
+ line.capture(:bytes_read).as(:traffic, :unit => :byte)
139
+ line.capture(:captured_request_cookie).as(:nillable_string)
140
+ line.capture(:captured_response_cookie).as(:nillable_string)
141
+ line.capture(:termination_event_code).as(:nillable_string)
142
+ line.capture(:terminated_session_state).as(:nillable_string)
143
+ line.capture(:clientside_persistence_cookie).as(:nillable_string)
144
+ line.capture(:serverside_persistence_cookie).as(:nillable_string)
145
+ line.capture(:listener_conn).as(:integer)
146
+ line.capture(:process_conn).as(:integer)
147
+ line.capture(:srv_queue).as(:integer)
148
+ line.capture(:backend_queue).as(:integer)
149
+ line.capture(:captured_request_headers).as(:nillable_string)
150
+ line.capture(:captured_response_headers).as(:nillable_string)
151
+ line.capture(:http_request).as(:nillable_string)
152
+ end
153
+
154
+ # Define the summary report
155
+ report do |analyze|
156
+ analyze.hourly_spread :field => :timestamp
157
+
158
+ analyze.frequency :client_ip,
159
+ :title => "Hits per IP"
160
+
161
+ analyze.frequency :frontend_name,
162
+ :title => "Hits per frontend service"
163
+
164
+ analyze.frequency :backend_name,
165
+ :title => "Hits per backend service"
166
+
167
+ analyze.frequency :server_name,
168
+ :title => "Hits per backend server"
169
+
170
+ analyze.frequency :status_code,
171
+ :title => "HTTP response code frequency"
172
+
173
+ analyze.frequency :http_request,
174
+ :title => "Most popular requests"
175
+
176
+ analyze.frequency :http_request,
177
+ :title => "Most frequent HTTP 40x errors",
178
+ :category => lambda { |r| "#{r[:http_request]}"},
179
+ :if => lambda { |r| r[:status_code] >= 400 and r[:status_code] <= 417 }
180
+
181
+ analyze.frequency :http_request,
182
+ :title => "Most frequent HTTP 50x errors",
183
+ :category => lambda { |r| "#{r[:http_request]}"},
184
+ :if => lambda { |r| r[:status_code] >= 500 and r[:status_code] <= 505 }
185
+
186
+ analyze.traffic :bytes_read,
187
+ :title => "Traffic per frontend service",
188
+ :category => lambda { |r| "#{r[:frontend_name]}"}
189
+
190
+ analyze.traffic :bytes_read,
191
+ :title => "Traffic per backend service",
192
+ :category => lambda { |r| "#{r[:backend_name]}"}
193
+
194
+ analyze.traffic :bytes_read,
195
+ :title => "Traffic per backend server",
196
+ :category => lambda { |r| "#{r[:server_name]}"}
197
+
198
+ analyze.duration :tr,
199
+ :title => "Time waiting for backend response",
200
+ :category => lambda { |r| "#{r[:http_request]}"}
201
+
202
+ analyze.duration :tt,
203
+ :title => "Total time spent on request",
204
+ :category => lambda { |r| "#{r[:http_request]}"}
205
+ end
206
+
207
+ # Define a custom Request class for the HAProxy file format to speed up
208
+ # timestamp handling. Shamelessly copied from apache.rb
209
+ class Request < RequestLogAnalyzer::Request
210
+
211
+ MONTHS = {'Jan' => '01', 'Feb' => '02', 'Mar' => '03', 'Apr' => '04', 'May' => '05', 'Jun' => '06',
212
+ 'Jul' => '07', 'Aug' => '08', 'Sep' => '09', 'Oct' => '10', 'Nov' => '11', 'Dec' => '12' }
213
+
214
+ # Do not use DateTime.parse, but parse the timestamp ourselves to return
215
+ # a integer to speed up parsing.
216
+ def convert_timestamp(value, definition)
217
+ "#{value[7,4]}#{MONTHS[value[3,3]]}#{value[0,2]}#{value[12,2]}#{value[15,2]}#{value[18,2]}".to_i
218
+ end
219
+
220
+ # Make sure that the strings '-' or '{}' or '' are parsed as a nil value.
221
+ def convert_nillable_string(value, definition)
222
+ value =~ /-|\{\}|^$/ ? nil : value
223
+ end
224
+
225
+ # Make sure that -1 is parsed as a nil value.
226
+ def convert_nillable_duration(value, definition)
227
+ value == '-1' ? nil : convert_duration(value, definition)
228
+ end
229
+
230
+ end
231
+ end
232
+ end
@@ -15,7 +15,7 @@ module RequestLogAnalyzer::FileFormat
15
15
  line_definition :user_host do |line|
16
16
  line.header = :alternative
17
17
  line.teaser = /\# User\@Host\: /
18
- line.regexp = /\# User\@Host\: ([\w-]+)\[[\w-]+\] \@ ([\w\.-]*) \[(#{ip_address(true)})\]/
18
+ line.regexp = /\# User\@Host\: ([\w-]+)\[[\w-]+\] \@ (#{hostname(true)}) \[(#{ip_address(true)})\]/
19
19
 
20
20
  line.capture(:user)
21
21
  line.capture(:host)
@@ -34,18 +34,18 @@ module RequestLogAnalyzer::FileFormat
34
34
  end
35
35
 
36
36
  line_definition :use_database do |line|
37
- line.regexp = /^use (\w+);\s*$/
37
+ line.regexp = /^\s*use (\w+);\s*$/
38
38
  line.capture(:database)
39
39
  end
40
40
 
41
41
  line_definition :query_part do |line|
42
- line.regexp = /^(?!(?:use |\# |SET ))(.*[^;\s])\s*$/
42
+ line.regexp = /^\s*(?!(?:use |\# |SET ))(.*[^;\s])\s*$/
43
43
  line.capture(:query_fragment)
44
44
  end
45
45
 
46
46
  line_definition :query do |line|
47
47
  line.footer = true
48
- line.regexp = /^(?!(?:use |\# |SET ))(.*);\s*$/
48
+ line.regexp = /^(?!\s*(?:use |\# |SET ))(.*);\s*$/
49
49
  line.capture(:query).as(:sql)
50
50
  end
51
51
 
@@ -84,7 +84,7 @@ module RequestLogAnalyzer::FileFormat
84
84
  sql.gsub!(/(:int,)+:int/, ':ints') # replace multiple ints by a list
85
85
  sql.gsub!(/(:string,)+:string/, ':strings') # replace multiple strings by a list
86
86
 
87
- return sql.rstrip
87
+ return sql.strip
88
88
  end
89
89
 
90
90
  def host
@@ -23,12 +23,19 @@ module RequestLogAnalyzer::FileFormat
23
23
  # Processing by QueriesController#index as HTML
24
24
  line_definition :processing do |line|
25
25
  line.teaser = /Processing by /
26
- line.regexp = /Processing by ([A-Za-z0-9\-:]+)\#(\w+) as ([\w\/\*]+)/
26
+ line.regexp = /Processing by ([A-Za-z0-9\-:]+)\#(\w+) as ([\w\/\*]*)/
27
27
 
28
28
  line.capture(:controller)
29
29
  line.capture(:action)
30
30
  line.capture(:format)
31
31
  end
32
+
33
+ # Parameters: {"action"=>"cached", "controller"=>"cached"}
34
+ line_definition :parameters do |line|
35
+ line.teaser = / Parameters:/
36
+ line.regexp = / Parameters:\s+(\{.*\})/
37
+ line.capture(:params).as(:eval)
38
+ end
32
39
 
33
40
  # Completed 200 OK in 224ms (Views: 200.2ms | ActiveRecord: 3.4ms)
34
41
  # Completed 302 Found in 23ms
@@ -36,7 +43,7 @@ module RequestLogAnalyzer::FileFormat
36
43
  line_definition :completed do |line|
37
44
  line.footer = true
38
45
  line.teaser = /Completed /
39
- line.regexp = /Completed (\d+)? .*in (\d+(?:\.\d+)?)ms(?:[^\(]*\(Views: (\d+(?:\.\d+)?)ms .* ActiveRecord: (\d+(?:\.\d+)?)ms\))?/
46
+ line.regexp = /Completed (\d+)? .*in (\d+(?:\.\d+)?)ms(?:[^\(]*\(Views: (\d+(?:\.\d+)?)ms .* ActiveRecord: (\d+(?:\.\d+)?)ms.*\))?/
40
47
 
41
48
  line.capture(:status).as(:integer)
42
49
  line.capture(:duration).as(:duration, :unit => :msec)
@@ -0,0 +1,56 @@
1
+ module RequestLogAnalyzer::FileFormat
2
+
3
+ # FileFormat for W3C access logs.
4
+ class W3c < Base
5
+
6
+ extend CommonRegularExpressions
7
+
8
+ line_definition :access do |line|
9
+ line.header = true
10
+ line.footer = true
11
+ line.regexp = /^(#{timestamp('%Y-%m-%d %H:%M:%S')}) (#{ip_address}) (.*) (#{ip_address}) (\d+) (\w+) ([\w|\/|.]+) \- (\d+) (\d+) (\d+) (\d+) (.*) (\S+)/
12
+
13
+ line.capture(:timestamp).as(:timestamp)
14
+ line.capture(:remote_ip)
15
+ line.capture(:username).as(:nillable_string)
16
+ line.capture(:local_ip)
17
+ line.capture(:port).as(:integer)
18
+ line.capture(:method)
19
+ line.capture(:path).as(:path)
20
+ line.capture(:http_status).as(:integer)
21
+ line.capture(:bytes_sent).as(:traffic, :unit => :byte)
22
+ line.capture(:bytes_received).as(:traffic, :unit => :byte)
23
+ line.capture(:duration).as(:duration, :unit => :msec)
24
+ line.capture(:user_agent)
25
+ line.capture(:referer)
26
+ end
27
+
28
+ report do |analyze|
29
+ analyze.timespan
30
+ analyze.hourly_spread
31
+
32
+ analyze.frequency :category => :http_method, :title => "HTTP methods"
33
+ analyze.frequency :category => :http_status, :title => "HTTP statuses"
34
+
35
+ analyze.frequency :category => :path, :title => "Most popular URIs"
36
+
37
+ analyze.frequency :category => :user_agent, :title => "User agents"
38
+ analyze.frequency :category => :referer, :title => "Referers"
39
+
40
+ analyze.duration :duration => :duration, :category => :path, :title => 'Request duration'
41
+ analyze.traffic :traffic => :bytes_sent, :category => :path, :title => 'Traffic out'
42
+ analyze.traffic :traffic => :bytes_received, :category => :path, :title => 'Traffic in'
43
+ end
44
+
45
+ class Request < RequestLogAnalyzer::Request
46
+ # Do not use DateTime.parse, but parse the timestamp ourselves to return a integer
47
+ # to speed up parsing.
48
+ def convert_timestamp(value, definition)
49
+ "#{value[0,4]}#{value[5,2]}#{value[8,2]}#{value[11,2]}#{value[14,2]}#{value[17,2]}".to_i
50
+ end
51
+ end
52
+
53
+
54
+ end
55
+
56
+ end