time_bandits 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -9,13 +9,13 @@ Time Bandits is a plugin which enhances Rails' controller/view/db benchmark logg
9
9
  Without configuration, the standard Rails 'Completed line' will change
10
10
  from its default format
11
11
 
12
- Completed in 56ms (View: 28, DB: 5) | 200 OK [http://127.0.0.1/jobs/info]
12
+ Completed 200 OK in 56ms (Views: 28.5ms, ActiveRecord: 5.1ms)
13
13
 
14
14
  to:
15
15
 
16
- Completed in 56.278ms (View: 28.488, DB: 5.111(2,0)) | 200 OK [http://127.0.0.1/jobs/info]
16
+ Completed 200 OK in 56.278ms (Views: 28.488ms, ActiveRecord: 5.111ms(2q,0h))
17
17
 
18
- Here "DB: 5.111(2,0)" means that 2 DB queries were executed and there were 0 SQL query cache hits.
18
+ "ActiveRecord: 5.111ms(2q,0h)" means that 2 SQL queries were executed and there were 0 SQL query cache hits.
19
19
 
20
20
  However, non-trivial applications also rather often use external services, which consume time that adds
21
21
  to your total response time, and sometimes these external services are not under your control. In these
@@ -30,50 +30,58 @@ Example:
30
30
  TimeBandits.add TimeBandits::TimeConsumers::Memcached
31
31
  TimeBandits.add TimeBandits::TimeConsumers::GarbageCollection.instance if GC.respond_to? :enable_stats
32
32
 
33
- Here we've added two additional consumers, which are already provided with the plugin. (Note that GC
34
- information requires a patched ruby, (e.g. http://github.com/skaes/matzruby, branch ruby187pl202patched
35
- or Ruby Enterprise Edition).
33
+ Here we've added two additional consumers, which are already provided with the
34
+ plugin. (Note that GC information requires a patched ruby, see prerequistes below.)
35
+
36
+ Note: if you run a multithreaded program, the numbers reported for garbage collections and
37
+ heap usage are partially misleading, because the Ruby interpreter collects stats in global
38
+ variables shared by all threads.
36
39
 
37
40
  With these two new time consumers, the log line changes to
38
41
 
39
- Completed in 680.378ms (View: 28.488, DB: 5.111(2,0), MC: 5.382(6r,0m), GC: 120.100(1), HP: 0(2000000,546468,18682541,934967)) | 200 OK [http://127.0.0.1/jobs/info]
42
+ Completed 200 OK in 680.378ms (Views: 28.488ms, ActiveRecord: 5.111ms(2q,0h), MC: 5.382(6r,0m), GC: 120.100(1), HP: 0(2000000,546468,18682541,934967))
40
43
 
41
44
  "MC: 5.382(6r,0m)" means that 6 memcache reads were performed and all keys were found in the cache (0 misses).
42
45
 
43
46
  "GC: 120.100(1)" tells us that 1 garbage collection was triggered during the request, taking 120.100 milliseconds.
44
47
 
45
- "HP: 0(2000000,546468,18682541,934967)" shows statistics on heap usage. The format is g(s,a,m,l), where
48
+ "HP: 0(2000000,546468,18682541,934967)" shows statistics on heap usage. The format is g(s,a,b,l), where
46
49
 
47
50
  g: heap growth during the request (#slots)
48
51
  s: size of the heap after request processing was completed (#slots)
49
52
  a: number of object allocations during the request (#slots)
50
- m: number of bytes allocated by the ruby x_malloc call (#bytes)
53
+ b: number of bytes allocated by the ruby x_malloc call (#bytes)
51
54
  l: live data set size after last GC (#slots)
52
55
 
56
+ Sidenote for Germans: you can use the word "Gesabbel" (eng: drivel) as a mnemonic here ;-)
57
+
53
58
  It's pretty easy to write additional time consumers; please refer to the source code.
54
59
 
55
60
 
56
61
  == Prerequisites
57
62
 
58
- Rails 2.3.2, 2.3.3 or 2.3.4 The plugin will raise an error if you try
59
- to use it with a different version.
63
+ Rails 3.x is required. The plugin will raise an error if you try to use it with a
64
+ different version.
65
+
66
+ You'll need a ruby with the railsexpress GC patches applied, if you want to include GC and
67
+ heap size information in the completed line. This is very useful, especially if you want
68
+ to analyze your rails logs using logjam (see http://github.com/alpinegizmo/logjam/).
60
69
 
61
- A ruby with the railsbench GC patches applied, if you want to include
62
- GC and heap size information in the completed line. This is very
63
- useful, especially if you want to analyze your rails logs using logjam
64
- (see http://github.com/alpinegizmo/logjam/).
70
+ Ruby Enterprise Edition contains a subset of the railsexpress patches. To get the full
71
+ monty, you can use for example rvm and the railsexpress rvm patchsets (see
72
+ https://github.com/skaes/rvm-patchsets).
65
73
 
66
74
 
67
75
  == History
68
76
 
69
- This plugin started from the code of the 'custom_benchmark' plugin
70
- written by tylerkovacs. However, we changed so much of the code that
71
- is is practically a full rewrite, hence we changed the name.
77
+ This plugin started from the code of the 'custom_benchmark' plugin written by
78
+ tylerkovacs. However, we changed so much of the code that is is practically a full
79
+ rewrite, hence we changed the name.
72
80
 
73
81
 
74
82
  == License
75
83
 
76
- Copyright (c) 2009 Stefan Kaes <skaes@railsexpress.de>
84
+ Copyright (c) 2009,2011 Stefan Kaes <skaes@railsexpress.de>
77
85
 
78
86
  Some portions Copyright (c) 2008 tylerkovacs
79
87
 
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ * make memcache consumers thread safe
2
+ * test memcache consumers against a rails 3 app
3
+ * maybe use ActiveSupport::Notifications everywhere?
@@ -58,6 +58,14 @@ module ActionController #:nodoc:
58
58
  consumed_during_rendering = TimeBandits.consumed - consumed_before_rendering
59
59
  runtime - consumed_during_rendering
60
60
  end
61
+
62
+ module ClassMethods
63
+ def log_process_action(payload) #:nodoc:
64
+ messages, view_runtime = [], payload[:view_runtime]
65
+ messages << ("Views: %.3fms" % view_runtime.to_f) if view_runtime
66
+ messages
67
+ end
68
+ end
61
69
  end
62
70
 
63
71
  class LogSubscriber
@@ -0,0 +1,76 @@
1
+ # this file monkey patches class ActiveRecord::LogSubscriber
2
+ # to count the number of sql statements being executed
3
+ # and the number of query cache hits
4
+ # it needs to be adapted to each new rails version
5
+
6
+ raise "AR log subscriber monkey patch for custom benchmarking is not compatible with your rails version" unless
7
+ (Rails::VERSION::STRING > "3.0" && Rails::VERSION::STRING < "3.1")
8
+
9
+ require "active_record/log_subscriber"
10
+
11
+ module ActiveRecord
12
+ class LogSubscriber
13
+
14
+ def self.call_count=(value)
15
+ Thread.current["active_record_sql_call_count"] = value
16
+ end
17
+
18
+ def self.call_count
19
+ Thread.current["active_record_sql_call_count"] ||= 0
20
+ end
21
+
22
+ def self.query_cache_hits=(value)
23
+ Thread.current["active_record_sql_query_cache_hits"] = value
24
+ end
25
+
26
+ def self.query_cache_hits
27
+ Thread.current["active_record_sql_query_cache_hits"] ||= 0
28
+ end
29
+
30
+ def self.reset_call_count
31
+ calls = call_count
32
+ self.call_count = 0
33
+ calls
34
+ end
35
+
36
+ def self.reset_query_cache_hits
37
+ hits = query_cache_hits
38
+ self.query_cache_hits = 0
39
+ hits
40
+ end
41
+
42
+ def sql(event)
43
+ self.class.runtime += event.duration
44
+ self.class.call_count += 1
45
+ self.class.query_cache_hits += 1 if event.payload[:name] == "CACHE"
46
+
47
+ return unless logger.debug?
48
+
49
+ name = '%s (%.1fms)' % [event.payload[:name], event.duration]
50
+ sql = event.payload[:sql].squeeze(' ')
51
+
52
+ if odd?
53
+ name = color(name, CYAN, true)
54
+ sql = color(sql, nil, true)
55
+ else
56
+ name = color(name, MAGENTA, true)
57
+ end
58
+
59
+ debug " #{name} #{sql}"
60
+ end
61
+ end
62
+
63
+ module Railties
64
+ module ControllerRuntime
65
+ def cleanup_view_runtime
66
+ super
67
+ end
68
+ module ClassMethods
69
+ def log_process_action(payload)
70
+ super
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+
@@ -9,8 +9,8 @@ module TimeBandits
9
9
  end
10
10
 
11
11
  ActiveSupport.on_load(:active_record) do
12
- require 'time_bandits/monkey_patches/activerecord_adapter'
13
- TimeBandits.add TimeBandits::TimeConsumers::Database.instance
12
+ require 'time_bandits/monkey_patches/active_record'
13
+ TimeBandits.add TimeBandits::TimeConsumers::Database
14
14
  end
15
15
  end
16
16
 
@@ -1,54 +1,49 @@
1
1
  # this consumer gets installed automatically by the plugin
2
2
  # if this were not so
3
3
  #
4
- # time_bandit TimeBandits::TimeConsumers::Database.new
4
+ # TimeBandit.add TimeBandits::TimeConsumers::Database
5
5
  #
6
6
  # would do the job
7
7
 
8
8
  module TimeBandits
9
9
  module TimeConsumers
10
- # provide a time consumer interface to ActiveRecord for perform_action_with_benchmark and render_with_benchmark
11
- class Database
12
- def initialize
13
- @consumed = 0.0
14
- @call_count = 0
15
- @query_cache_hits = 0
10
+ # provide a time consumer interface to ActiveRecord
11
+ module Database
12
+ extend self
13
+
14
+ def info
15
+ Thread.current[:time_bandits_database_info] ||= [0.0, 0, 0]
16
16
  end
17
- private :initialize
18
17
 
19
- def self.instance
20
- @instance ||= new
18
+ def info=(info)
19
+ Thread.current[:time_bandits_database_info] = info
21
20
  end
22
21
 
23
22
  def reset
24
23
  reset_stats
25
- @call_count = 0
26
- @consumed = 0.0
27
- @query_cache_hits = 0
24
+ self.info = [0.0, 0, 0]
28
25
  end
29
26
 
30
27
  def consumed
31
- hits, calls, time = reset_stats
32
- @query_cache_hits += hits
33
- @call_count += calls
34
- @consumed += time
28
+ time, calls, hits = reset_stats
29
+ i = self.info
30
+ i[2] += hits
31
+ i[1] += calls
32
+ i[0] += time
35
33
  end
36
34
 
37
35
  def runtime
38
- sprintf "DB: %.3f(%d,%d)", @consumed * 1000, @call_count, @query_cache_hits
36
+ sprintf "ActiveRecord: %.3fms(%dq,%dh)", *info
39
37
  end
40
38
 
41
39
  private
42
- def all_connections
43
- ActiveRecord::Base.connection_handler.connection_pools.values.map{|pool| pool.connections}.flatten
44
- end
45
40
 
46
41
  def reset_stats
47
- connections = all_connections
48
- hits = connections.map{|c| c.reset_query_cache_hits}.sum
49
- calls = connections.map{|c| c.reset_call_count}.sum
50
- time = connections.map{|c| c.reset_runtime}.sum
51
- [hits, calls, time]
42
+ s = ActiveRecord::LogSubscriber
43
+ hits = s.reset_query_cache_hits
44
+ calls = s.reset_call_count
45
+ time = s.reset_runtime
46
+ [time, calls, hits]
52
47
  end
53
48
  end
54
49
  end
@@ -1,3 +1,3 @@
1
1
  module TimeBandits
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: time_bandits
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 4
10
- version: 0.0.4
9
+ - 5
10
+ version: 0.0.5
11
11
  platform: ruby
12
12
  authors:
13
13
  - Stefan Kaes
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-31 00:00:00 +02:00
18
+ date: 2011-04-03 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -47,9 +47,10 @@ files:
47
47
  - Gemfile
48
48
  - README.rdoc
49
49
  - Rakefile
50
+ - TODO
50
51
  - lib/time_bandits.rb
51
52
  - lib/time_bandits/monkey_patches/action_controller.rb
52
- - lib/time_bandits/monkey_patches/activerecord_adapter.rb
53
+ - lib/time_bandits/monkey_patches/active_record.rb
53
54
  - lib/time_bandits/monkey_patches/memcache-client.rb
54
55
  - lib/time_bandits/monkey_patches/memcached.rb
55
56
  - lib/time_bandits/monkey_patches/rails_rack_logger.rb
@@ -91,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
92
  requirements: []
92
93
 
93
94
  rubyforge_project: time_bandits
94
- rubygems_version: 1.6.2
95
+ rubygems_version: 1.5.2
95
96
  signing_key:
96
97
  specification_version: 3
97
98
  summary: Custom performance logging for Rails
@@ -1,85 +0,0 @@
1
- # this file monkey patches class ActiveRecord::ConnectionAdapters::AbstractAdapter
2
- # and the module module ActiveRecord::ConnectionAdapters::QueryCache
3
- # to count the number of sql statements being executed.
4
- # it needs to be adapted to each new rails version
5
-
6
- raise "AR abstract adapter monkey patch for custom benchmarking is not compatible with your rails version" unless %w(2.3.2 2.3.3 2.3.4 2.3.8 2.3.9 2.3.10).include?(Rails::VERSION::STRING)
7
-
8
- module ActiveRecord
9
- module ConnectionAdapters
10
- class ConnectionPool
11
- attr_reader :connections
12
- end
13
-
14
- class AbstractAdapter
15
- attr_accessor :call_count, :query_cache_hits
16
-
17
- def initialize(connection, logger = nil) #:nodoc:
18
- @connection, @logger = connection, logger
19
- @runtime = 0
20
- @call_count = 0
21
- @last_verification = 0
22
- @query_cache_enabled = false
23
- @query_cache_hits = 0
24
- end
25
-
26
- def reset_call_count
27
- calls = @call_count
28
- @call_count = 0
29
- calls
30
- end
31
-
32
- def reset_query_cache_hits
33
- hits = @query_cache_hits
34
- @query_cache_hits = 0
35
- hits
36
- end
37
-
38
- protected
39
- def log(sql, name)
40
- if block_given?
41
- result = nil
42
- seconds = Benchmark.realtime { result = yield }
43
- @runtime += seconds
44
- @call_count += 1
45
- log_info(sql, name, seconds * 1000)
46
- result
47
- else
48
- log_info(sql, name, 0)
49
- nil
50
- end
51
- rescue Exception => e
52
- # Log message and raise exception.
53
- # Set last_verification to 0, so that connection gets verified
54
- # upon reentering the request loop
55
- @last_verification = 0
56
- message = "#{e.class.name}: #{e.message}: #{sql}"
57
- log_info(message, name, 0)
58
- raise ActiveRecord::StatementInvalid, message
59
- end
60
- end
61
-
62
- module QueryCache
63
- private
64
- def cache_sql(sql)
65
- result =
66
- if @query_cache.has_key?(sql)
67
- @query_cache_hits += 1
68
- log_info(sql, "CACHE", 0.0)
69
- @query_cache[sql]
70
- else
71
- @query_cache[sql] = yield
72
- end
73
-
74
- if Array === result
75
- result.collect { |row| row.dup }
76
- else
77
- result.duplicable? ? result.dup : result
78
- end
79
- rescue TypeError
80
- result
81
- end
82
- end
83
- end
84
- end
85
-