request-log-analyzer 1.5.4 → 1.6.4
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.
- data/README.rdoc +2 -3
- data/lib/request_log_analyzer/database/base.rb +4 -5
- data/lib/request_log_analyzer/file_format/amazon_s3.rb +19 -18
- data/lib/request_log_analyzer/file_format/delayed_job.rb +14 -10
- data/lib/request_log_analyzer/file_format/mysql.rb +15 -12
- data/lib/request_log_analyzer/file_format/postgresql.rb +68 -0
- data/lib/request_log_analyzer/file_format/rails.rb +2 -2
- data/lib/request_log_analyzer/file_format/rails3.rb +86 -0
- data/lib/request_log_analyzer/file_format.rb +2 -2
- data/lib/request_log_analyzer/line_definition.rb +28 -3
- data/lib/request_log_analyzer/output/fancy_html.rb +19 -1
- data/lib/request_log_analyzer/output/fixed_width.rb +1 -1
- data/lib/request_log_analyzer/source/log_parser.rb +2 -2
- data/lib/request_log_analyzer/tracker/numeric_value.rb +1 -1
- data/lib/request_log_analyzer/tracker/traffic.rb +1 -1
- data/lib/request_log_analyzer.rb +1 -1
- data/request-log-analyzer.gemspec +4 -4
- data/spec/fixtures/postgresql.log +2980 -0
- data/spec/unit/file_format/amazon_s3_format_spec.rb +18 -0
- data/spec/unit/file_format/postgresql_format_spec.rb +65 -0
- data/spec/unit/file_format/rails3_format_spec.rb +120 -0
- data/spec/unit/file_format/rails_format_spec.rb +8 -2
- data/spec/unit/source/log_parser_spec.rb +1 -1
- data/spec/unit/tracker/tracker_api_spec.rb +4 -4
- data/tasks/github-gem.rake +22 -17
- metadata +41 -19
data/README.rdoc
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This is a simple command line tool to analyze request log files in various formats to produce a performance report. Its purpose is to find what actions are best candidates for optimization.
|
|
4
4
|
|
|
5
|
-
* Analyzes
|
|
5
|
+
* Analyzes log files. Currently supports: Amazon S3, Apache, Delayed::Job, Merb, Mysql, PostgreSQL, Rack, Rails and more.
|
|
6
6
|
* Combines multiple files and decompresses compressed files, which comes in handy if you are using logrotate.
|
|
7
7
|
* Uses several metrics, including cumulative request time, mean request time, process blockers, database and rendering time, HTTP methods and statuses, Rails action cache statistics, etc.) (Sample output: http://wiki.github.com/wvanbergen/request-log-analyzer/sample-output)
|
|
8
8
|
* Low memory footprint and reasonably fast, so it is safe to run on a production server.
|
|
@@ -35,5 +35,4 @@ Bergen (willem@railsdoctors.com) or Bart ten Brinke (bart@railsdoctors.com).
|
|
|
35
35
|
|
|
36
36
|
* Project wiki at GitHub: http://wiki.github.com/wvanbergen/request-log-analyzer
|
|
37
37
|
* railsdoctors homepage: http://railsdoctors.com
|
|
38
|
-
* wvanbergen's blog posts: http://techblog.floorplanner.com/tag/request-log-analyzer
|
|
39
|
-
* barttenbrinke's blog posts: http://movesonrails.com/articles/tag/analyzer
|
|
38
|
+
* wvanbergen's blog posts: http://techblog.floorplanner.com/tag/request-log-analyzer
|
|
@@ -82,14 +82,13 @@ class RequestLogAnalyzer::Database::Base < ActiveRecord::Base
|
|
|
82
82
|
capture[:provides].each { |field, field_type| t.column(field, column_type(field_type)) } if capture[:provides].kind_of?(Hash)
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
|
+
|
|
86
|
+
# Add indices to table for more speedy querying
|
|
87
|
+
database.connection.add_index(self.table_name.to_sym, [:request_id]) # rescue
|
|
88
|
+
database.connection.add_index(self.table_name.to_sym, [:source_id]) # rescue
|
|
85
89
|
end
|
|
86
|
-
|
|
87
|
-
# Add indices to table for more speedy querying
|
|
88
|
-
database.connection.add_index(self.table_name.to_sym, [:request_id]) # rescue
|
|
89
|
-
database.connection.add_index(self.table_name.to_sym, [:source_id]) # rescue
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
-
|
|
93
92
|
# Function to determine the column type for a field
|
|
94
93
|
# TODO: make more robust / include in file-format definition
|
|
95
94
|
def self.column_type(type_indicator)
|
|
@@ -11,24 +11,25 @@ module RequestLogAnalyzer::FileFormat
|
|
|
11
11
|
line_definition :access do |line|
|
|
12
12
|
line.header = true
|
|
13
13
|
line.footer = true
|
|
14
|
-
line.regexp = /^([^\ ]+) ([^\ ]+) \[(#{timestamp('%d/%b/%Y:%H:%M:%S %z')})?\] (#{ip_address}) ([^\ ]+) ([^\ ]+) (\w+(?:\.\w+)*) ([^\ ]+) "([^"]+)" (\d+) ([^\ ]+) (\d+) (\d+) (\d+) (\d+) "([^"]
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
14
|
+
line.regexp = /^([^\ ]+) ([^\ ]+) \[(#{timestamp('%d/%b/%Y:%H:%M:%S %z')})?\] (#{ip_address}) ([^\ ]+) ([^\ ]+) (\w+(?:\.\w+)*) ([^\ ]+) "([^"]+)" (\d+) ([^\ ]+) (\d+) (\d+) (\d+) (\d+) "([^"]*)" "([^"]*)"/
|
|
15
|
+
|
|
16
|
+
line.capture(:bucket_owner)
|
|
17
|
+
line.capture(:bucket)
|
|
18
|
+
line.capture(:timestamp).as(:timestamp)
|
|
19
|
+
line.capture(:remote_ip)
|
|
20
|
+
line.capture(:requester)
|
|
21
|
+
line.capture(:request_id)
|
|
22
|
+
line.capture(:operation)
|
|
23
|
+
line.capture(:key).as(:nillable_string)
|
|
24
|
+
line.capture(:request_uri)
|
|
25
|
+
line.capture(:http_status).as(:integer)
|
|
26
|
+
line.capture(:error_code).as(:nillable_string)
|
|
27
|
+
line.capture(:bytes_sent).as(:traffic, :unit => :byte)
|
|
28
|
+
line.capture(:object_size).as(:traffic, :unit => :byte)
|
|
29
|
+
line.capture(:total_time).as(:duration, :unit => :msec)
|
|
30
|
+
line.capture(:turnaround_time).as(:duration, :unit => :msec)
|
|
31
|
+
line.capture(:referer).as(:referer)
|
|
32
|
+
line.capture(:user_agent).as(:user_agent)
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
report do |analyze|
|
|
@@ -7,38 +7,42 @@ module RequestLogAnalyzer::FileFormat
|
|
|
7
7
|
line_definition :job_lock do |line|
|
|
8
8
|
line.header = true
|
|
9
9
|
line.regexp = /\* \[JOB\] acquiring lock on (\S+)/
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
line.capture(:job)
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
line_definition :job_completed do |line|
|
|
14
15
|
line.footer = true
|
|
15
16
|
line.regexp = /\* \[JOB\] (\S+) completed after (\d+\.\d+)/
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
line.capture(:completed_job)
|
|
19
|
+
line.capture(:duration).as(:duration, :unit => :sec)
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
line_definition :job_failed do |line|
|
|
21
23
|
line.footer = true
|
|
22
24
|
line.regexp = /\* \[JOB\] (\S+) failed with (\S+)\: .* - (\d+) failed attempts/
|
|
23
|
-
line.captures << { :name => :failed_job, :type => :string } <<
|
|
24
|
-
{ :name => :exception, :type => :string } <<
|
|
25
|
-
{ :name => :attempts, :type => :integer }
|
|
26
25
|
|
|
26
|
+
line.capture(:failed_job)
|
|
27
|
+
line.capture(:exception)
|
|
28
|
+
line.capture(:attempts).as(:integer)
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
line_definition :job_lock_failed do |line|
|
|
30
32
|
line.footer = true
|
|
31
33
|
line.regexp = /\* \[JOB\] failed to acquire exclusive lock for (\S+)/
|
|
32
|
-
|
|
34
|
+
|
|
35
|
+
line.capture(:locked_job)
|
|
33
36
|
end
|
|
34
37
|
|
|
35
38
|
# line_definition :batch_completed do |line|
|
|
36
39
|
# line.header = true
|
|
37
40
|
# line.footer = true
|
|
38
41
|
# line.regexp = /(\d+) jobs processed at (\d+\.\d+) j\/s, (\d+) failed .../
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
+
#
|
|
43
|
+
# line.capture(:total_amount).as(:integer)
|
|
44
|
+
# line.capture(:mean_duration).as(:duration, :unit => :sec)
|
|
45
|
+
# line.capture(:failed_amount).as(:integer)
|
|
42
46
|
# end
|
|
43
47
|
|
|
44
48
|
report do |analyze|
|
|
@@ -8,42 +8,45 @@ module RequestLogAnalyzer::FileFormat
|
|
|
8
8
|
line.header = :alternative
|
|
9
9
|
line.teaser = /\# Time: /
|
|
10
10
|
line.regexp = /\# Time: (#{timestamp('%y%m%d %k:%M:%S')})/
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
line.capture(:timestamp).as(:timestamp)
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
line_definition :user_host do |line|
|
|
15
16
|
line.header = :alternative
|
|
16
17
|
line.teaser = /\# User\@Host\: /
|
|
17
18
|
line.regexp = /\# User\@Host\: ([\w-]+)\[[\w-]+\] \@ ([\w\.-]*) \[(#{ip_address(true)})\]/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
|
|
20
|
+
line.capture(:user)
|
|
21
|
+
line.capture(:host)
|
|
22
|
+
line.capture(:ip)
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
line_definition :query_statistics do |line|
|
|
24
26
|
line.header = :alternative
|
|
25
27
|
line.teaser = /\# Query_time: /
|
|
26
28
|
line.regexp = /\# Query_time: (\d+(?:\.\d+)?)\s+Lock_time: (\d+(?:\.\d+)?)\s+Rows_sent: (\d+)\s+Rows_examined: (\d+)/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
|
|
30
|
+
line.capture(:query_time).as(:duration, :unit => :sec)
|
|
31
|
+
line.capture(:lock_time).as(:duration, :unit => :sec)
|
|
32
|
+
line.capture(:rows_sent).as(:integer)
|
|
33
|
+
line.capture(:rows_examined).as(:integer)
|
|
31
34
|
end
|
|
32
35
|
|
|
33
36
|
line_definition :use_database do |line|
|
|
34
37
|
line.regexp = /^use (\w+);\s*$/
|
|
35
|
-
line.
|
|
38
|
+
line.capture(:database)
|
|
36
39
|
end
|
|
37
40
|
|
|
38
41
|
line_definition :query_part do |line|
|
|
39
42
|
line.regexp = /^(?!(?:use |\# |SET ))(.*[^;\s])\s*$/
|
|
40
|
-
line.
|
|
43
|
+
line.capture(:query_fragment)
|
|
41
44
|
end
|
|
42
45
|
|
|
43
46
|
line_definition :query do |line|
|
|
44
|
-
line.regexp = /^(?!(?:use |\# |SET ))(.*);\s*$/
|
|
45
|
-
line.captures << { :name => :query, :type => :sql }
|
|
46
47
|
line.footer = true
|
|
48
|
+
line.regexp = /^(?!(?:use |\# |SET ))(.*);\s*$/
|
|
49
|
+
line.capture(:query).as(:sql)
|
|
47
50
|
end
|
|
48
51
|
|
|
49
52
|
PER_USER = :user
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module RequestLogAnalyzer::FileFormat
|
|
2
|
+
|
|
3
|
+
class Postgresql < Base
|
|
4
|
+
|
|
5
|
+
extend CommonRegularExpressions
|
|
6
|
+
|
|
7
|
+
line_definition :query do |line|
|
|
8
|
+
line.header = true
|
|
9
|
+
line.teaser = /LOG\: query\:/
|
|
10
|
+
line.regexp = /(#{timestamp('%y-%m-%d %k:%M:%S')})\ LOG: query:\s+(.*)/
|
|
11
|
+
|
|
12
|
+
line.capture(:timestamp).as(:timestamp)
|
|
13
|
+
line.capture(:query_fragment)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
line_definition :duration do |line|
|
|
17
|
+
line.footer = true
|
|
18
|
+
line.teaser = /duration:/
|
|
19
|
+
line.regexp = /#{timestamp('%y-%m-%d %k:%M:%S')}\ LOG\: duration\: (.*)(\ )sec/
|
|
20
|
+
|
|
21
|
+
line.capture(:query_time).as(:duration, :unit => :sec)
|
|
22
|
+
line.capture(:query).as(:sql) # Hack to gather up fragments
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
line_definition :query_fragment do |line|
|
|
26
|
+
line.regexp = /^(?!.*LOG)\s*(.*)\s*/
|
|
27
|
+
line.capture(:query_fragment)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
report do |analyze|
|
|
31
|
+
analyze.timespan
|
|
32
|
+
analyze.hourly_spread
|
|
33
|
+
analyze.duration :query_time, :category => :query, :title => 'Query time'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class Request < RequestLogAnalyzer::Request
|
|
37
|
+
|
|
38
|
+
def convert_sql(value, definition)
|
|
39
|
+
|
|
40
|
+
# Recreate the full SQL query by joining all the previous parts and this last line
|
|
41
|
+
sql = every(:query_fragment).join("\n") + value
|
|
42
|
+
|
|
43
|
+
# Sanitize an SQL query so that it can be used as a category field.
|
|
44
|
+
# sql.gsub!(/\/\*.*\*\//, '') # remove comments
|
|
45
|
+
sql.gsub!(/\s+/, ' ') # remove excessive whitespace
|
|
46
|
+
sql.gsub!(/"([^"]+)"/, '\1') # remove quotes from field names
|
|
47
|
+
sql.gsub!(/'\d{4}-\d{2}-\d{2}'/, ':date') # replace dates
|
|
48
|
+
sql.gsub!(/'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'/, ':datetime') # replace timestamps
|
|
49
|
+
sql.gsub!(/'[^']*'/, ':string') # replace strings
|
|
50
|
+
sql.gsub!(/\b\d+\b/, ':int') # replace integers
|
|
51
|
+
sql.gsub!(/(:int,)+:int/, ':ints') # replace multiple ints by a list
|
|
52
|
+
sql.gsub!(/(:string,)+:string/, ':strings') # replace multiple strings by a list
|
|
53
|
+
|
|
54
|
+
return sql.lstrip.rstrip
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def host
|
|
58
|
+
self[:host] == '' || self[:host].nil? ? self[:ip] : self[:host]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Convert the timestamp to an integer
|
|
62
|
+
def convert_timestamp(value, definition)
|
|
63
|
+
all,y,m,d,h,i,s = value.split(/(\d\d)-(\d\d)-(\d\d)\s+(\d?\d):(\d\d):(\d\d)/)
|
|
64
|
+
('20%s%s%s%s%s%s' % [y,m,d,h.rjust(2, '0'),i,s]).to_i
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -119,13 +119,13 @@ module RequestLogAnalyzer::FileFormat
|
|
|
119
119
|
{ :name => :file, :type => :string }]),
|
|
120
120
|
|
|
121
121
|
:cache_hit => RequestLogAnalyzer::LineDefinition.new(:cache_hit,
|
|
122
|
-
:regexp => /Filter chain halted as \[\#<ActionController::Caching::Actions::ActionCacheFilter/),
|
|
122
|
+
:regexp => /Filter chain halted as \[\#<ActionController::Filters::AroundFilter.*\@method=.*(?:Caching::Actions::ActionCacheFilter|action_controller\/caching\/actions\.rb).*\] did_not_yield/),
|
|
123
123
|
|
|
124
124
|
:parameters => RequestLogAnalyzer::LineDefinition.new(:parameters,
|
|
125
125
|
:teaser => / Parameters:/,
|
|
126
126
|
:regexp => / Parameters:\s+(\{.*\})/,
|
|
127
127
|
:captures => [{ :name => :params, :type => :eval }]),
|
|
128
|
-
|
|
128
|
+
|
|
129
129
|
:rendered => RequestLogAnalyzer::LineDefinition.new(:rendered,
|
|
130
130
|
:teaser => /Rendered /,
|
|
131
131
|
:regexp => /Rendered (\w+(?:\/\w+)+) \((\d+\.\d+)ms\)/,
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module RequestLogAnalyzer::FileFormat
|
|
2
|
+
|
|
3
|
+
# Default FileFormat class for Rails 3 logs.
|
|
4
|
+
#
|
|
5
|
+
# For now, this is just a basic implementation. It will probaby change after
|
|
6
|
+
# Rails 3 final has been released.
|
|
7
|
+
class Rails3 < Base
|
|
8
|
+
|
|
9
|
+
extend CommonRegularExpressions
|
|
10
|
+
|
|
11
|
+
# Started GET "/queries" for 127.0.0.1 at 2010-02-25 16:15:18
|
|
12
|
+
line_definition :started do |line|
|
|
13
|
+
line.header = true
|
|
14
|
+
line.teaser = /Started /
|
|
15
|
+
line.regexp = /Started ([A-Z]+) "([^"]+)" for (#{ip_address}) at (#{timestamp('%Y-%m-%d %H:%M:%S')})/
|
|
16
|
+
|
|
17
|
+
line.capture(:method)
|
|
18
|
+
line.capture(:path)
|
|
19
|
+
line.capture(:ip)
|
|
20
|
+
line.capture(:timestamp).as(:timestamp)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Processing by QueriesController#index as HTML
|
|
24
|
+
line_definition :processing do |line|
|
|
25
|
+
line.teaser = /Processing by /
|
|
26
|
+
line.regexp = /Processing by (\w+)\#(\w+) as (\w+)/
|
|
27
|
+
|
|
28
|
+
line.capture(:controller)
|
|
29
|
+
line.capture(:action)
|
|
30
|
+
line.capture(:format)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Completed in 9ms (Views: 4.9ms | ActiveRecord: 0.5ms) with 200
|
|
34
|
+
line_definition :completed do |line|
|
|
35
|
+
line.footer = true
|
|
36
|
+
line.teaser = /Completed /
|
|
37
|
+
line.regexp = /Completed (\d+) .* in (\d+)ms \([^\)]*\)/
|
|
38
|
+
|
|
39
|
+
line.capture(:status).as(:integer)
|
|
40
|
+
line.capture(:duration).as(:duration, :unit => :msec)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# 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:
|
|
44
|
+
line_definition :failure do |line|
|
|
45
|
+
line.footer = true
|
|
46
|
+
line.regexp = /((?:[A-Z]\w*[a-z]\w+\:\:)*[A-Z]\w*[a-z]\w+) \((.*)\)(?: on line #(\d+) of (.+))?\:\s*$/
|
|
47
|
+
|
|
48
|
+
line.capture(:error)
|
|
49
|
+
line.capture(:message)
|
|
50
|
+
line.capture(:line).as(:integer)
|
|
51
|
+
line.capture(:file)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# # Not parsed at the moment:
|
|
55
|
+
# SQL (0.2ms) SET SQL_AUTO_IS_NULL=0
|
|
56
|
+
# Query Load (0.4ms) SELECT `queries`.* FROM `queries`
|
|
57
|
+
# Rendered collection (0.0ms)
|
|
58
|
+
# Rendered queries/index.html.erb (0.6ms)
|
|
59
|
+
|
|
60
|
+
REQUEST_CATEGORIZER = lambda { |request| "#{request[:controller]}##{request[:action]}.#{request[:format]}" }
|
|
61
|
+
|
|
62
|
+
report do |analyze|
|
|
63
|
+
|
|
64
|
+
analyze.timespan
|
|
65
|
+
analyze.hourly_spread
|
|
66
|
+
|
|
67
|
+
analyze.frequency :category => REQUEST_CATEGORIZER, :title => 'Most requested'
|
|
68
|
+
analyze.frequency :method, :title => 'HTTP methods'
|
|
69
|
+
analyze.frequency :status, :title => 'HTTP statuses returned'
|
|
70
|
+
|
|
71
|
+
analyze.duration :duration, :category => REQUEST_CATEGORIZER, :title => "Request duration", :line_type => :completed
|
|
72
|
+
# analyze.duration :view, :category => REQUEST_CATEGORIZER, :title => "View rendering time", :line_type => :completed
|
|
73
|
+
# analyze.duration :db, :category => REQUEST_CATEGORIZER, :title => "Database time", :line_type => :completed
|
|
74
|
+
|
|
75
|
+
analyze.frequency :category => REQUEST_CATEGORIZER, :title => 'Process blockers (> 1 sec duration)',
|
|
76
|
+
:if => lambda { |request| request[:duration] && request[:duration] > 1.0 }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class Request < RequestLogAnalyzer::Request
|
|
80
|
+
def convert_timestamp(value, defintion)
|
|
81
|
+
value.gsub(/[^0-9]/, '')[0...14].to_i
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -63,7 +63,7 @@ module RequestLogAnalyzer::FileFormat
|
|
|
63
63
|
|
|
64
64
|
parsers = all_formats.map { |f| RequestLogAnalyzer::Source::LogParser.new(f, :parse_strategy => 'cautious') }
|
|
65
65
|
|
|
66
|
-
File.open(file, '
|
|
66
|
+
File.open(file, 'rb') do |io|
|
|
67
67
|
while io.lineno < line_count && (line = io.gets)
|
|
68
68
|
parsers.each { |parser| parser.parse_line(line) }
|
|
69
69
|
end
|
|
@@ -201,7 +201,7 @@ module RequestLogAnalyzer::FileFormat
|
|
|
201
201
|
|
|
202
202
|
# Specifies a single line defintions.
|
|
203
203
|
def self.line_definition(name, &block)
|
|
204
|
-
@line_definer.
|
|
204
|
+
@line_definer.define_line(name, &block)
|
|
205
205
|
end
|
|
206
206
|
|
|
207
207
|
# Specifies multiple line definitions at once using a block
|
|
@@ -16,14 +16,31 @@ module RequestLogAnalyzer
|
|
|
16
16
|
def initialize_copy(other)
|
|
17
17
|
@line_definitions = other.line_definitions.dup
|
|
18
18
|
end
|
|
19
|
-
|
|
20
|
-
def
|
|
19
|
+
|
|
20
|
+
def define_line(name, arg = {}, &block)
|
|
21
21
|
if block_given?
|
|
22
22
|
@line_definitions[name] = RequestLogAnalyzer::LineDefinition.define(name, &block)
|
|
23
23
|
else
|
|
24
|
-
@line_definitions[name] = RequestLogAnalyzer::LineDefinition.new(name,
|
|
24
|
+
@line_definitions[name] = RequestLogAnalyzer::LineDefinition.new(name, arg)
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
|
+
|
|
28
|
+
def method_missing(name, *args, &block)
|
|
29
|
+
define_line(name, args[0], &block)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class CaptureDefiner
|
|
34
|
+
attr_accessor :capture_hash
|
|
35
|
+
|
|
36
|
+
def initialize(hash)
|
|
37
|
+
@capture_hash = hash
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def as(type, type_options = {})
|
|
41
|
+
@capture_hash.merge!(type_options.merge(:type => type))
|
|
42
|
+
return self
|
|
43
|
+
end
|
|
27
44
|
end
|
|
28
45
|
|
|
29
46
|
attr_reader :name
|
|
@@ -47,6 +64,12 @@ module RequestLogAnalyzer
|
|
|
47
64
|
yield(definition) if block_given?
|
|
48
65
|
return definition
|
|
49
66
|
end
|
|
67
|
+
|
|
68
|
+
def capture(name)
|
|
69
|
+
new_capture_hash = { :name => name, :type => :string}
|
|
70
|
+
captures << new_capture_hash
|
|
71
|
+
CaptureDefiner.new(new_capture_hash)
|
|
72
|
+
end
|
|
50
73
|
|
|
51
74
|
# Checks whether a given line matches this definition.
|
|
52
75
|
# It will return false if a line does not match. If the line matches, a hash is returned
|
|
@@ -66,6 +89,8 @@ module RequestLogAnalyzer
|
|
|
66
89
|
else
|
|
67
90
|
return false
|
|
68
91
|
end
|
|
92
|
+
rescue
|
|
93
|
+
return false
|
|
69
94
|
end
|
|
70
95
|
|
|
71
96
|
alias :=~ :matches
|
|
@@ -25,7 +25,25 @@ module RequestLogAnalyzer::Output
|
|
|
25
25
|
def report_hourly_spread(tracker)
|
|
26
26
|
title tracker.title
|
|
27
27
|
puts tag(:img, nil, :width => '700', :height => '120', :src =>
|
|
28
|
-
Gchart.sparkline(:data => tracker.hour_frequencies, :size => '700x120', :line_colors => '0077CC'
|
|
28
|
+
Gchart.sparkline(:data => tracker.hour_frequencies, :size => '700x120', :line_colors => '0077CC',
|
|
29
|
+
:axis_with_labels => 'x,y', :axis_labels => [x_axis_labels(tracker),y_axis_labels(tracker)]))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def x_axis_labels(tracker)
|
|
33
|
+
frmt = '%H:%M'
|
|
34
|
+
x_axis_labels = []
|
|
35
|
+
num_labels = 5
|
|
36
|
+
start = tracker.first_timestamp
|
|
37
|
+
step = tracker.timespan / (num_labels - 1)
|
|
38
|
+
for i in 0...num_labels do
|
|
39
|
+
x_axis_labels << (start + step * i).strftime(frmt)
|
|
40
|
+
end
|
|
41
|
+
x_axis_labels
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def y_axis_labels(tracker)
|
|
45
|
+
sorted_frequencies = tracker.hour_frequencies.sort
|
|
46
|
+
[sorted_frequencies.first, sorted_frequencies.last]
|
|
29
47
|
end
|
|
30
48
|
end
|
|
31
49
|
end
|
|
@@ -38,7 +38,7 @@ module RequestLogAnalyzer::Output
|
|
|
38
38
|
foreground_color = "3#{COLORS[option]}" if COLORS.include?(option)
|
|
39
39
|
font_style = "#{STYLES[option]};" if STYLES.include?(option)
|
|
40
40
|
elsif option.kind_of?(Hash)
|
|
41
|
-
|
|
41
|
+
option.each do |key, value|
|
|
42
42
|
case key
|
|
43
43
|
when :color; foreground_color = "3#{COLORS[value]}" if COLORS.include?(value)
|
|
44
44
|
when :background; background_color = "4#{COLORS[value]};" if COLORS.include?(value)
|
|
@@ -109,12 +109,12 @@ module RequestLogAnalyzer::Source
|
|
|
109
109
|
@progress_handler = @dormant_progress_handler
|
|
110
110
|
@progress_handler.call(:started, file) if @progress_handler
|
|
111
111
|
|
|
112
|
-
File.open(file, '
|
|
112
|
+
File.open(file, 'rb') { |f| parse_io(f, options, &block) }
|
|
113
113
|
|
|
114
114
|
@progress_handler.call(:finished, file) if @progress_handler
|
|
115
115
|
@progress_handler = nil
|
|
116
116
|
else
|
|
117
|
-
IO.popen(decompress_file?(file), '
|
|
117
|
+
IO.popen(decompress_file?(file), 'rb') { |f| parse_io(f, options, &block) }
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
@source_changes_handler.call(:finished, @current_source) if @source_changes_handler
|
|
@@ -71,7 +71,7 @@ module RequestLogAnalyzer::Tracker
|
|
|
71
71
|
return "- " if value.nil?
|
|
72
72
|
return "0 " if value.zero?
|
|
73
73
|
|
|
74
|
-
case Math.log10(value).floor
|
|
74
|
+
case [Math.log10(value.abs).floor, 0].max
|
|
75
75
|
when 0...4 then '%d ' % value
|
|
76
76
|
when 4...7 then '%dk' % (value / 1000)
|
|
77
77
|
when 7...10 then '%dM' % (value / 1000_000)
|
|
@@ -23,7 +23,7 @@ module RequestLogAnalyzer::Tracker
|
|
|
23
23
|
return "-" if bytes.nil?
|
|
24
24
|
return "0 B" if bytes.zero?
|
|
25
25
|
|
|
26
|
-
case Math.log10(bytes).floor
|
|
26
|
+
case [Math.log10(bytes.abs).floor, 0].max
|
|
27
27
|
when 0...4 then '%d B' % bytes
|
|
28
28
|
when 4...7 then '%d kB' % (bytes / 1000)
|
|
29
29
|
when 7...10 then '%d MB' % (bytes / 1000_000)
|
data/lib/request_log_analyzer.rb
CHANGED
|
@@ -11,7 +11,7 @@ module RequestLogAnalyzer
|
|
|
11
11
|
|
|
12
12
|
# The current version of request-log-analyzer.
|
|
13
13
|
# Do not change the value by hand; it will be updated automatically by the gem release script.
|
|
14
|
-
VERSION = "1.
|
|
14
|
+
VERSION = "1.6.4"
|
|
15
15
|
|
|
16
16
|
# Loads constants in the RequestLogAnalyzer namespace using self.load_default_class_file(base, const)
|
|
17
17
|
# <tt>const</tt>:: The constant that is not yet loaded in the RequestLogAnalyzer namespace. This should be passed as a string or symbol.
|
|
@@ -2,8 +2,8 @@ Gem::Specification.new do |s|
|
|
|
2
2
|
s.name = "request-log-analyzer"
|
|
3
3
|
|
|
4
4
|
# Do not set the version and date field manually, this is done by the release script
|
|
5
|
-
s.version = "1.
|
|
6
|
-
s.date = "
|
|
5
|
+
s.version = "1.6.4"
|
|
6
|
+
s.date = "2010-03-22"
|
|
7
7
|
|
|
8
8
|
s.rubyforge_project = 'r-l-a'
|
|
9
9
|
|
|
@@ -36,6 +36,6 @@ Gem::Specification.new do |s|
|
|
|
36
36
|
|
|
37
37
|
# The files and test_files directives are set automatically by the release script.
|
|
38
38
|
# Do not change them by hand, but make sure to add the files to the git repository.
|
|
39
|
-
s.files = %w(spec/unit/tracker/hourly_spread_spec.rb lib/request_log_analyzer/output/html.rb DESIGN.rdoc spec/fixtures/rails_unordered.log spec/fixtures/multiple_files_1.log lib/request_log_analyzer/database/source.rb README.rdoc spec/unit/file_format/amazon_s3_format_spec.rb lib/request_log_analyzer/file_format/rails_development.rb spec/fixtures/rails_22.log spec/fixtures/test_order.log spec/unit/database/base_class_spec.rb lib/request_log_analyzer/database/connection.rb lib/request_log_analyzer/aggregator.rb spec/fixtures/decompression.log lib/request_log_analyzer/file_format/apache.rb spec/unit/file_format/rack_format_spec.rb spec/fixtures/decompression.log.gz lib/request_log_analyzer/tracker/frequency.rb lib/request_log_analyzer/tracker/duration.rb tasks/github-gem.rake
|
|
40
|
-
s.test_files = %w(spec/unit/tracker/hourly_spread_spec.rb spec/unit/file_format/amazon_s3_format_spec.rb spec/unit/database/base_class_spec.rb spec/unit/file_format/rack_format_spec.rb spec/unit/filter/filter_spec.rb spec/unit/database/connection_spec.rb spec/integration/scout_spec.rb spec/unit/
|
|
39
|
+
s.files = %w(spec/unit/tracker/hourly_spread_spec.rb lib/request_log_analyzer/output/html.rb DESIGN.rdoc spec/fixtures/rails_unordered.log spec/fixtures/multiple_files_1.log lib/request_log_analyzer/database/source.rb README.rdoc spec/unit/file_format/amazon_s3_format_spec.rb lib/request_log_analyzer/file_format/rails_development.rb spec/fixtures/rails_22.log spec/fixtures/test_order.log spec/unit/database/base_class_spec.rb lib/request_log_analyzer/database/connection.rb lib/request_log_analyzer/aggregator.rb spec/fixtures/decompression.log lib/request_log_analyzer/file_format/apache.rb spec/unit/file_format/rack_format_spec.rb spec/fixtures/decompression.log.gz lib/request_log_analyzer/tracker/frequency.rb lib/request_log_analyzer/tracker/duration.rb tasks/github-gem.rake spec/unit/filter/filter_spec.rb .gitignore spec/lib/macros.rb spec/spec_helper.rb spec/fixtures/decompression.log.bz2 spec/unit/database/connection_spec.rb lib/request_log_analyzer/filter.rb spec/integration/scout_spec.rb spec/fixtures/test_file_format.log spec/unit/filter/timespan_filter_spec.rb lib/request_log_analyzer/tracker/traffic.rb spec/unit/file_format/merb_format_spec.rb spec/unit/file_format/format_autodetection_spec.rb spec/unit/file_format/file_format_api_spec.rb lib/request_log_analyzer/filter/field.rb lib/request_log_analyzer/file_format/merb.rb lib/request_log_analyzer/filter/anonymize.rb lib/request_log_analyzer/file_format/amazon_s3.rb lib/cli/database_console.rb spec/integration/mailer_spec.rb lib/request_log_analyzer/line_definition.rb spec/unit/tracker/frequency_tracker_spec.rb spec/fixtures/postgresql.log spec/fixtures/multiple_files_2.log spec/lib/mocks.rb spec/unit/file_format/apache_format_spec.rb spec/fixtures/syslog_1x.log spec/unit/file_format/rails3_format_spec.rb spec/integration/munin_plugins_rails_spec.rb lib/request_log_analyzer/file_format/rails3.rb spec/lib/helpers.rb spec/unit/file_format/delayed_job_format_spec.rb spec/fixtures/rails.db spec/unit/source/log_parser_spec.rb lib/request_log_analyzer/aggregator/database_inserter.rb spec/fixtures/header_and_footer.log spec/lib/testing_format.rb lib/request_log_analyzer/database.rb spec/unit/file_format/rails_format_spec.rb lib/request_log_analyzer/request.rb lib/request_log_analyzer/output/fixed_width.rb spec/integration/command_line_usage_spec.rb lib/request_log_analyzer/file_format/rack.rb lib/request_log_analyzer/tracker/numeric_value.rb spec/fixtures/test_language_combined.log spec/unit/request_spec.rb lib/cli/command_line_arguments.rb lib/request_log_analyzer.rb spec/database.yml lib/request_log_analyzer/database/base.rb spec/unit/aggregator/database_inserter_spec.rb lib/request_log_analyzer/database/request.rb lib/request_log_analyzer/output/fancy_html.rb lib/cli/database_console_init.rb lib/cli/progressbar.rb lib/request_log_analyzer/database/warning.rb spec/unit/filter/anonymize_filter_spec.rb spec/fixtures/apache_combined.log spec/unit/tracker/duration_tracker_spec.rb spec/fixtures/rails_22_cached.log lib/request_log_analyzer/aggregator/summarizer.rb spec/unit/file_format/postgresql_format_spec.rb spec/unit/file_format/common_regular_expressions_spec.rb spec/fixtures/rails_1x.log lib/request_log_analyzer/file_format/delayed_job.rb lib/request_log_analyzer/output.rb spec/unit/file_format/line_definition_spec.rb lib/request_log_analyzer/file_format/mysql.rb spec/unit/file_format/mysql_format_spec.rb request-log-analyzer.gemspec spec/fixtures/apache_common.log spec/unit/mailer_spec.rb lib/request_log_analyzer/file_format/rails.rb Rakefile lib/request_log_analyzer/tracker/hourly_spread.rb spec/unit/aggregator/summarizer_spec.rb spec/unit/tracker/traffic_tracker_spec.rb spec/unit/tracker/numeric_value_tracker_spec.rb lib/request_log_analyzer/file_format/postgresql.rb lib/request_log_analyzer/tracker/timespan.rb spec/unit/controller/log_processor_spec.rb spec/unit/filter/field_filter_spec.rb spec/fixtures/sinatra.log spec/fixtures/merb_prefixed.log lib/request_log_analyzer/log_processor.rb lib/request_log_analyzer/filter/timespan.rb spec/unit/controller/controller_spec.rb lib/request_log_analyzer/mailer.rb lib/cli/tools.rb spec/fixtures/decompression.tar.gz spec/unit/tracker/timespan_tracker_spec.rb spec/lib/matchers.rb lib/request_log_analyzer/controller.rb spec/unit/tracker/tracker_api_spec.rb lib/request_log_analyzer/source/database_loader.rb spec/fixtures/merb.log LICENSE lib/request_log_analyzer/file_format.rb tasks/request_log_analyzer.rake spec/unit/database/database_spec.rb spec/fixtures/decompression.log.zip spec/fixtures/decompression.tgz bin/request-log-analyzer lib/request_log_analyzer/tracker.rb lib/request_log_analyzer/source.rb spec/fixtures/mysql_slow_query.log lib/request_log_analyzer/source/log_parser.rb lib/request_log_analyzer/aggregator/echo.rb)
|
|
40
|
+
s.test_files = %w(spec/unit/tracker/hourly_spread_spec.rb spec/unit/file_format/amazon_s3_format_spec.rb spec/unit/database/base_class_spec.rb spec/unit/file_format/rack_format_spec.rb spec/unit/filter/filter_spec.rb spec/unit/database/connection_spec.rb spec/integration/scout_spec.rb spec/unit/filter/timespan_filter_spec.rb spec/unit/file_format/merb_format_spec.rb spec/unit/file_format/format_autodetection_spec.rb spec/unit/file_format/file_format_api_spec.rb spec/integration/mailer_spec.rb spec/unit/tracker/frequency_tracker_spec.rb spec/unit/file_format/apache_format_spec.rb spec/unit/file_format/rails3_format_spec.rb spec/integration/munin_plugins_rails_spec.rb spec/unit/file_format/delayed_job_format_spec.rb spec/unit/source/log_parser_spec.rb spec/unit/file_format/rails_format_spec.rb spec/integration/command_line_usage_spec.rb spec/unit/request_spec.rb spec/unit/aggregator/database_inserter_spec.rb spec/unit/filter/anonymize_filter_spec.rb spec/unit/tracker/duration_tracker_spec.rb spec/unit/file_format/postgresql_format_spec.rb spec/unit/file_format/common_regular_expressions_spec.rb spec/unit/file_format/line_definition_spec.rb spec/unit/file_format/mysql_format_spec.rb spec/unit/mailer_spec.rb spec/unit/aggregator/summarizer_spec.rb spec/unit/tracker/traffic_tracker_spec.rb spec/unit/tracker/numeric_value_tracker_spec.rb spec/unit/controller/log_processor_spec.rb spec/unit/filter/field_filter_spec.rb spec/unit/controller/controller_spec.rb spec/unit/tracker/timespan_tracker_spec.rb spec/unit/tracker/tracker_api_spec.rb spec/unit/database/database_spec.rb)
|
|
41
41
|
end
|