ngauthier-slow-actions 0.2.6 → 0.3.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.
- data/VERSION.yml +2 -2
- data/bin/slow-actions +10 -1
- data/lib/slow_actions.rb +37 -3
- data/lib/slow_actions_action.rb +9 -0
- data/lib/slow_actions_computation_module.rb +26 -3
- data/lib/slow_actions_controller.rb +8 -0
- data/lib/slow_actions_log_entry.rb +16 -0
- data/lib/slow_actions_parser.rb +18 -2
- data/lib/slow_actions_session.rb +6 -0
- metadata +1 -1
data/VERSION.yml
CHANGED
data/bin/slow-actions
CHANGED
@@ -1,7 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require File.join(File.dirname(__FILE__), '..', 'lib', 'slow_actions')
|
3
3
|
|
4
|
-
|
4
|
+
opts = {}
|
5
|
+
start_date = ARGV.detect{|arg| arg =~/--start-date=(\S+)/}
|
6
|
+
opts[:start_date] = $1 if start_date
|
7
|
+
end_date = ARGV.detect{|arg| arg =~/--end-date=(\S+)/}
|
8
|
+
opts[:end_date] = $1 if start_date
|
9
|
+
|
10
|
+
|
11
|
+
@sa = SlowActions.new(opts)
|
5
12
|
ARGV.select{|arg| arg[0,2] != "--"}.each do |file|
|
6
13
|
@sa.parse_file(file)
|
7
14
|
end
|
@@ -34,6 +41,8 @@ if output.size == 0
|
|
34
41
|
puts "\t--min-cost=FLOAT"
|
35
42
|
puts "\t--min-avg=FLOAT"
|
36
43
|
puts "\t--min-max=FLOAT"
|
44
|
+
puts "\t--start-date=YYYY-MM-DD"
|
45
|
+
puts "\t--end-date=YYYY-MM-DD"
|
37
46
|
else
|
38
47
|
puts output.join("\n\n")
|
39
48
|
end
|
data/lib/slow_actions.rb
CHANGED
@@ -1,23 +1,44 @@
|
|
1
|
+
# Main class for the slow actions plugin library
|
1
2
|
require File.join(File.dirname(__FILE__), 'slow_actions_parser')
|
2
3
|
require File.join(File.dirname(__FILE__), 'slow_actions_controller')
|
3
4
|
require File.join(File.dirname(__FILE__), 'slow_actions_action')
|
4
5
|
require File.join(File.dirname(__FILE__), 'slow_actions_session')
|
6
|
+
require 'date'
|
5
7
|
|
8
|
+
# SlowActions class that is the master controller for processing slow actions
|
6
9
|
class SlowActions
|
7
|
-
|
10
|
+
# Takes an options hash where you can specify :start_date and :end_date as "YYYY-MM-DD" strings
|
11
|
+
def initialize(opts = {})
|
8
12
|
@log_entries = []
|
13
|
+
if opts[:start_date]
|
14
|
+
@start_date = Date.strptime(opts[:start_date])
|
15
|
+
else
|
16
|
+
@start_date = Date.strptime
|
17
|
+
end
|
18
|
+
if opts[:end_date]
|
19
|
+
@end_date = Date.strptime(opts[:end_date])
|
20
|
+
else
|
21
|
+
@end_date = Date.today
|
22
|
+
end
|
9
23
|
end
|
10
24
|
|
25
|
+
# Parse the file found at "file_path" and add the log entries to its collection of entries.
|
11
26
|
def parse_file(file_path)
|
12
|
-
parser = Parser.new(file_path)
|
27
|
+
parser = Parser.new(file_path, @start_date, @end_date)
|
13
28
|
@log_entries += parser.parse
|
14
29
|
process
|
15
30
|
end
|
16
31
|
|
32
|
+
# All the #LogEntry objects
|
17
33
|
def log_entries
|
18
34
|
return @log_entries
|
19
35
|
end
|
20
36
|
|
37
|
+
# Print out all the actions and their statistics
|
38
|
+
#
|
39
|
+
# :mincost Lower bound on the cost of this action. See Computable.
|
40
|
+
# :minavg Lower bound on the average amount of time this action ever took
|
41
|
+
# :minmax Lower bound on the maximum amount of time this action ever took
|
21
42
|
def print_actions(opts = {})
|
22
43
|
str = ""
|
23
44
|
str += " Cost Average Max\n"
|
@@ -34,6 +55,7 @@ class SlowActions
|
|
34
55
|
return str
|
35
56
|
end
|
36
57
|
|
58
|
+
# Print out all the controllers and their statistics, nesting actions as a tree. See #print_actions for options.
|
37
59
|
def print_controller_tree(opts = {})
|
38
60
|
str = ""
|
39
61
|
str += " Cost Average Max\n"
|
@@ -59,6 +81,7 @@ class SlowActions
|
|
59
81
|
return str
|
60
82
|
end
|
61
83
|
|
84
|
+
# Print out all the session_ids and their statistics. See #print_actions for options.
|
62
85
|
def print_sessions(opts = {})
|
63
86
|
str = ""
|
64
87
|
str += " Cost Average Max\n"
|
@@ -75,30 +98,40 @@ class SlowActions
|
|
75
98
|
return str
|
76
99
|
end
|
77
100
|
|
101
|
+
# Print out the stats as html. Not implemented.
|
78
102
|
def to_html
|
79
103
|
raise "Not Implemented"
|
80
104
|
end
|
81
105
|
|
106
|
+
# All the #Controller objects.
|
82
107
|
def controllers
|
83
108
|
@controllers.values
|
84
109
|
end
|
85
110
|
|
111
|
+
# All the #Action objects
|
86
112
|
def actions
|
87
113
|
@actions.values
|
88
114
|
end
|
89
115
|
|
116
|
+
# All the #Session objects
|
90
117
|
def sessions
|
91
118
|
@sessions.values
|
92
119
|
end
|
93
120
|
|
94
121
|
private
|
95
122
|
|
123
|
+
# The Date to start parsing from
|
124
|
+
attr_accessor :start_date
|
125
|
+
# The Date to stop parsing at
|
126
|
+
attr_accessor :end_date
|
127
|
+
|
128
|
+
# Process statistics for all #Actions #Controllers and #Sessions
|
96
129
|
def process
|
97
130
|
@controllers ||= {}
|
98
131
|
@actions ||= {}
|
99
132
|
@sessions ||= {}
|
100
133
|
@log_entries.each do |la|
|
101
|
-
next if la.processed
|
134
|
+
next if la.nil? or la.processed
|
102
135
|
c = @controllers[la.controller]
|
103
136
|
if c.nil?
|
104
137
|
c = Controller.new(la.controller)
|
@@ -129,6 +162,7 @@ class SlowActions
|
|
129
162
|
@sessions.values.each{|s| s.compute_times}
|
130
163
|
end
|
131
164
|
|
165
|
+
# Convert a float to 7 places padded with zeros then one space
|
132
166
|
def ftos(float)
|
133
167
|
str = ((float*1000).to_i.to_f/1000).to_s
|
134
168
|
while str.size < 7
|
data/lib/slow_actions_action.rb
CHANGED
@@ -1,20 +1,29 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'slow_actions_computation_module')
|
2
2
|
class SlowActions
|
3
3
|
private
|
4
|
+
# Class to hold all #LogEntry objects that are associated with this individual #Action on a #Controller
|
4
5
|
class Action
|
5
6
|
include Computable
|
7
|
+
# Create a new #Action object.
|
8
|
+
# name: the #name of the #Action. i.e. "new"
|
9
|
+
# controller: the #Controller this #Action is a part of
|
6
10
|
def initialize(name, controller)
|
7
11
|
@name = name
|
8
12
|
@controller = controller
|
9
13
|
@log_entries = []
|
10
14
|
end
|
15
|
+
|
16
|
+
# Name of this #Action
|
11
17
|
attr_reader :name
|
18
|
+
# #Controller of this #Action
|
12
19
|
attr_reader :controller
|
13
20
|
|
21
|
+
# Add a log entry to this #Action
|
14
22
|
def add_entry(la)
|
15
23
|
@log_entries << la
|
16
24
|
la.action = self
|
17
25
|
end
|
26
|
+
# All the #LogEntry objects this #Action holds
|
18
27
|
attr_reader :log_entries
|
19
28
|
|
20
29
|
end
|
@@ -1,9 +1,32 @@
|
|
1
|
+
# Computable module provides the attributes and methods to compute statistics on log entries
|
2
|
+
#
|
3
|
+
# Cost is computed as
|
4
|
+
# avg * Math.log(total + 0.1)
|
5
|
+
#
|
6
|
+
# So that it can take into account the frequency for which an action is called
|
1
7
|
module Computable
|
2
|
-
|
3
|
-
attr_reader :
|
4
|
-
|
8
|
+
# the average time for rendering
|
9
|
+
attr_reader :render_avg
|
10
|
+
# the average time for the database
|
11
|
+
attr_reader :db_avg
|
12
|
+
# the average time for the entire object
|
13
|
+
attr_reader :total_avg
|
14
|
+
# the maximum time an object ever took to render
|
15
|
+
attr_reader :render_max
|
16
|
+
# the maximum time an object ever took to query the database
|
17
|
+
attr_reader :db_max
|
18
|
+
# the maximum time an object ever took to complete the entire action
|
19
|
+
attr_reader :total_max
|
20
|
+
# cost for this action to render
|
21
|
+
attr_reader :render_cost
|
22
|
+
# cost for this action to query the db
|
23
|
+
attr_reader :db_cost
|
24
|
+
# cost for this action to complete
|
25
|
+
attr_reader :total_cost
|
26
|
+
# average error rate for this action
|
5
27
|
attr_reader :error_avg
|
6
28
|
|
29
|
+
# Perform all the computations in one loop
|
7
30
|
def compute_times
|
8
31
|
@render_avg = 0.0
|
9
32
|
@db_avg = 0.0
|
@@ -1,24 +1,32 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'slow_actions_computation_module')
|
2
2
|
class SlowActions
|
3
3
|
private
|
4
|
+
# Class to hold all #LogEntry objects that are associated with this #Controller.
|
4
5
|
class Controller
|
5
6
|
include Computable
|
7
|
+
# Create a new #Controller
|
8
|
+
# name: The name of the #Controller
|
6
9
|
def initialize(name)
|
7
10
|
@name = name
|
8
11
|
@log_entries = []
|
9
12
|
@actions = []
|
10
13
|
end
|
14
|
+
# Name of the #Controller
|
11
15
|
attr_reader :name
|
12
16
|
|
17
|
+
# Add a #LogEntry to this #Controller
|
13
18
|
def add_entry(la)
|
14
19
|
@log_entries << la
|
15
20
|
la.controller = self
|
16
21
|
end
|
22
|
+
# All the #LogEntry objects associated with this #Controller
|
17
23
|
attr_reader :log_entries
|
18
24
|
|
25
|
+
# Add an #Action as a child of this #Controller
|
19
26
|
def add_action(a)
|
20
27
|
@actions << a
|
21
28
|
end
|
29
|
+
# All the #Actions under this #Controller
|
22
30
|
attr_reader :actions
|
23
31
|
|
24
32
|
end
|
@@ -1,23 +1,39 @@
|
|
1
1
|
class SlowActions
|
2
2
|
private
|
3
|
+
# Object representing a single entry in a rails application's log file
|
3
4
|
class LogEntry
|
5
|
+
# Create a new #LogEntry
|
4
6
|
def initialize
|
5
7
|
self.processed = false
|
6
8
|
end
|
9
|
+
# The #Controller
|
7
10
|
attr_accessor :controller
|
11
|
+
# The #Action
|
8
12
|
attr_accessor :action
|
13
|
+
# IP address of user
|
9
14
|
attr_accessor :ip
|
15
|
+
# date
|
10
16
|
attr_accessor :date
|
17
|
+
# time
|
11
18
|
attr_accessor :time
|
19
|
+
# HTTP Method
|
12
20
|
attr_accessor :method
|
21
|
+
# Session ID
|
13
22
|
attr_accessor :session
|
23
|
+
# Parameters to the action
|
14
24
|
attr_accessor :parameters
|
25
|
+
# Total duration to complete
|
15
26
|
attr_accessor :duration
|
27
|
+
# time spent rendering
|
16
28
|
attr_accessor :rendering
|
29
|
+
# time spent in the database
|
17
30
|
attr_accessor :db
|
31
|
+
# error text (if any)
|
18
32
|
attr_accessor :error_text
|
33
|
+
# whether or not is has already been processed by #SlowActions
|
19
34
|
attr_accessor :processed
|
20
35
|
|
36
|
+
# Whether or not this #LogEntry was an error
|
21
37
|
def error?
|
22
38
|
return false if error_text.nil?
|
23
39
|
return false if error_text.empty?
|
data/lib/slow_actions_parser.rb
CHANGED
@@ -1,14 +1,23 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'slow_actions_log_entry')
|
2
2
|
class SlowActions
|
3
3
|
private
|
4
|
+
# Text parser for #SlowActions
|
4
5
|
class Parser
|
5
6
|
|
6
|
-
|
7
|
+
# Create a new #Parser
|
8
|
+
# file_path: path to log file to parse
|
9
|
+
# start_date: Date object. Entries before this date are skipped
|
10
|
+
# end_date: Date object. Entries after this date are skipped
|
11
|
+
# Dates are inclusive
|
12
|
+
def initialize(file_path, start_date, end_date)
|
7
13
|
@file_path = file_path
|
8
14
|
raise "File not found: #{file_path}" unless File.exists? file_path
|
9
15
|
@file = File.new(file_path, 'r')
|
16
|
+
@start_date = start_date
|
17
|
+
@end_date = end_date
|
10
18
|
end
|
11
|
-
|
19
|
+
|
20
|
+
# Initiate the parsing
|
12
21
|
def parse
|
13
22
|
@log_entries = []
|
14
23
|
begin
|
@@ -26,6 +35,8 @@ class SlowActions
|
|
26
35
|
|
27
36
|
private
|
28
37
|
|
38
|
+
# Parse an individual entry
|
39
|
+
# line: one line of text
|
29
40
|
def parse_log_entry(line)
|
30
41
|
la = LogEntry.new
|
31
42
|
if line =~ /^Processing (\S+)#(\S+) \(for (\S+) at (\S+) (\S+)\) \[(\S+)\]$/
|
@@ -35,6 +46,10 @@ class SlowActions
|
|
35
46
|
la.date = $4
|
36
47
|
la.time = $5
|
37
48
|
la.method = $6
|
49
|
+
parsed_date = Date.strptime(la.date)
|
50
|
+
if parsed_date < @start_date or parsed_date > @end_date
|
51
|
+
return nil
|
52
|
+
end
|
38
53
|
end
|
39
54
|
line = @file.readline
|
40
55
|
if line =~ /^\s+Session ID: (\S+)$/
|
@@ -69,6 +84,7 @@ class SlowActions
|
|
69
84
|
return la
|
70
85
|
end
|
71
86
|
|
87
|
+
# parse an error
|
72
88
|
def parse_error
|
73
89
|
line = "\n"
|
74
90
|
while line == "\n"
|
data/lib/slow_actions_session.rb
CHANGED
@@ -1,18 +1,24 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'slow_actions_computation_module')
|
2
2
|
class SlowActions
|
3
3
|
private
|
4
|
+
# Class to hold and #LogEntry objects that are associated with this individual Session ID
|
4
5
|
class Session
|
5
6
|
include Computable
|
7
|
+
# Create a new session
|
8
|
+
# name: the session_id
|
6
9
|
def initialize(name)
|
7
10
|
@name = name
|
8
11
|
@log_entries = []
|
9
12
|
end
|
13
|
+
# The session_id
|
10
14
|
attr_reader :name
|
11
15
|
|
16
|
+
# Add a #LogEntry to this #Session
|
12
17
|
def add_entry(la)
|
13
18
|
@log_entries << la
|
14
19
|
la.session = self
|
15
20
|
end
|
21
|
+
# All the #LogEntry objects this #Session holds
|
16
22
|
attr_reader :log_entries
|
17
23
|
|
18
24
|
end
|