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 CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :minor: 2
3
- :patch: 6
2
+ :minor: 3
3
+ :patch: 0
4
4
  :major: 0
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
- @sa = SlowActions.new
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
- def initialize
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
@@ -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
- attr_reader :render_avg, :db_avg, :total_avg
3
- attr_reader :render_max, :db_max, :total_max
4
- attr_reader :render_cost, :db_cost, :total_cost
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?
@@ -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
- def initialize(file_path)
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"
@@ -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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ngauthier-slow-actions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Gauthier