time_bandits 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +16 -4
  3. data/Appraisals +2 -16
  4. data/README.md +11 -6
  5. data/Rakefile +1 -0
  6. data/docker-compose.yml +4 -4
  7. data/gemfiles/{activesupport_5.0.4.gemfile → activesupport_5.2.4.3.gemfile} +2 -2
  8. data/gemfiles/{activesupport_5.2.3.gemfile.lock → activesupport_5.2.4.3.gemfile.lock} +29 -31
  9. data/gemfiles/{activesupport_4.2.9.gemfile → activesupport_6.0.3.2.gemfile} +2 -2
  10. data/gemfiles/{activesupport_5.1.7.gemfile.lock → activesupport_6.0.3.2.gemfile.lock} +31 -33
  11. data/lib/time_bandits.rb +2 -6
  12. data/lib/time_bandits/monkey_patches/action_controller.rb +1 -66
  13. data/lib/time_bandits/monkey_patches/active_record.rb +18 -116
  14. data/lib/time_bandits/rack/logger.rb +46 -19
  15. data/lib/time_bandits/railtie.rb +2 -6
  16. data/lib/time_bandits/time_consumers/dalli.rb +0 -12
  17. data/lib/time_bandits/time_consumers/garbage_collection.rb +5 -29
  18. data/lib/time_bandits/version.rb +1 -1
  19. data/test/test_helper.rb +2 -10
  20. data/test/unit/gc_consumer_test.rb +2 -3
  21. data/test/unit/sequel_test.rb +5 -1
  22. data/time_bandits.gemspec +6 -3
  23. metadata +20 -46
  24. data/gemfiles/activesupport_4.1.16.gemfile +0 -8
  25. data/gemfiles/activesupport_4.1.16.gemfile.lock +0 -91
  26. data/gemfiles/activesupport_4.2.8.gemfile +0 -8
  27. data/gemfiles/activesupport_4.2.8.gemfile.lock +0 -85
  28. data/gemfiles/activesupport_4.2.9.gemfile.lock +0 -85
  29. data/gemfiles/activesupport_5.0.3.gemfile +0 -8
  30. data/gemfiles/activesupport_5.0.3.gemfile.lock +0 -83
  31. data/gemfiles/activesupport_5.0.4.gemfile.lock +0 -83
  32. data/gemfiles/activesupport_5.0.7.2.gemfile +0 -8
  33. data/gemfiles/activesupport_5.0.7.2.gemfile.lock +0 -83
  34. data/gemfiles/activesupport_5.0.7.gemfile +0 -8
  35. data/gemfiles/activesupport_5.0.7.gemfile.lock +0 -83
  36. data/gemfiles/activesupport_5.1.1.gemfile +0 -8
  37. data/gemfiles/activesupport_5.1.1.gemfile.lock +0 -83
  38. data/gemfiles/activesupport_5.1.2.gemfile +0 -8
  39. data/gemfiles/activesupport_5.1.2.gemfile.lock +0 -83
  40. data/gemfiles/activesupport_5.1.5.gemfile +0 -8
  41. data/gemfiles/activesupport_5.1.5.gemfile.lock +0 -83
  42. data/gemfiles/activesupport_5.1.7.gemfile +0 -8
  43. data/gemfiles/activesupport_5.2.0.gemfile +0 -8
  44. data/gemfiles/activesupport_5.2.0.gemfile.lock +0 -83
  45. data/gemfiles/activesupport_5.2.3.gemfile +0 -8
  46. data/gemfiles/activesupport_6.0.0.gemfile +0 -8
  47. data/gemfiles/activesupport_6.0.0.gemfile.lock +0 -83
  48. data/lib/time_bandits/monkey_patches/active_support_cache_store.rb +0 -18
  49. data/lib/time_bandits/rack/logger40.rb +0 -94
  50. data/rails/init.rb +0 -1
@@ -3,9 +3,6 @@
3
3
  # and the number of query cache hits
4
4
  # it needs to be adapted to each new rails version
5
5
 
6
- raise "time_bandits ActiveRecord monkey patch is not compatible with your rails version" unless
7
- Rails::VERSION::STRING =~ /^(3\.[012]|4\.[012])|5\.[012]|6\.0/
8
-
9
6
  require "active_record/log_subscriber"
10
7
 
11
8
  module ActiveRecord
@@ -42,31 +39,7 @@ module ActiveRecord
42
39
  hits
43
40
  end
44
41
 
45
- # Rails 4.1 uses method_added to automatically subscribe newly
46
- # added methods. Since :render_bind and :sql are already defined,
47
- # the net effect is that sql gets called twice. Therefore, we
48
- # temporarily switch to protected mode and change it back later to
49
- # public.
50
-
51
- # Note that render_bind was added for Rails 4.0, and the implementation
52
- # has changed since then, so we are careful to only redefine it if necessary.
53
- unless instance_methods.include?(:render_bind)
54
- protected
55
- def render_bind(column, value)
56
- if column
57
- if column.type == :binary
58
- value = "<#{value.bytesize} bytes of binary data>"
59
- end
60
- [column.name, value]
61
- else
62
- [nil, value]
63
- end
64
- end
65
- public :render_bind
66
- public
67
- end
68
-
69
- protected
42
+ remove_method :sql
70
43
  def sql(event)
71
44
  payload = event.payload
72
45
 
@@ -80,99 +53,25 @@ module ActiveRecord
80
53
 
81
54
  log_sql_statement(payload, event)
82
55
  end
83
- public :sql
84
- public
85
56
 
86
57
  private
87
- if Rails::VERSION::STRING >= "5.1.5"
88
- def log_sql_statement(payload, event)
89
- name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
90
- name = "CACHE #{name}" if payload[:cached]
91
- sql = payload[:sql]
92
- binds = nil
93
-
94
- unless (payload[:binds] || []).empty?
95
- casted_params = type_casted_binds(payload[:type_casted_binds])
96
- binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
97
- render_bind(attr, value)
98
- }.inspect
99
- end
100
-
101
- name = colorize_payload_name(name, payload[:name])
102
- sql = color(sql, sql_color(sql), true)
103
-
104
- debug " #{name} #{sql}#{binds}"
58
+ def log_sql_statement(payload, event)
59
+ name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
60
+ name = "CACHE #{name}" if payload[:cached]
61
+ sql = payload[:sql]
62
+ binds = nil
63
+
64
+ unless (payload[:binds] || []).empty?
65
+ casted_params = type_casted_binds(payload[:type_casted_binds])
66
+ binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
67
+ render_bind(attr, value)
68
+ }.inspect
105
69
  end
106
- elsif Rails::VERSION::STRING >= "5.0.7" && Rails::VERSION::STRING < "5.1.0"
107
- def log_sql_statement(payload, event)
108
- name = '%s (%.1fms)' % [payload[:name], event.duration]
109
- sql = payload[:sql].squeeze(' ')
110
- binds = nil
111
-
112
- unless (payload[:binds] || []).empty?
113
- casted_params = type_casted_binds(payload[:type_casted_binds])
114
- binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
115
- render_bind(attr, value)
116
- }.inspect
117
- end
118
70
 
119
- name = colorize_payload_name(name, payload[:name])
120
- sql = color(sql, sql_color(sql), true)
121
-
122
- debug " #{name} #{sql}#{binds}"
123
- end
124
- elsif Rails::VERSION::STRING >= "5.0.3"
125
- def log_sql_statement(payload, event)
126
- name = '%s (%.1fms)' % [payload[:name], event.duration]
127
- sql = payload[:sql].squeeze(' ')
128
- binds = nil
129
-
130
- unless (payload[:binds] || []).empty?
131
- casted_params = type_casted_binds(payload[:binds], payload[:type_casted_binds])
132
- binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
133
- render_bind(attr, value)
134
- }.inspect
135
- end
71
+ name = colorize_payload_name(name, payload[:name])
72
+ sql = color(sql, sql_color(sql), true)
136
73
 
137
- name = colorize_payload_name(name, payload[:name])
138
- sql = color(sql, sql_color(sql), true)
139
-
140
- debug " #{name} #{sql}#{binds}"
141
- end
142
- elsif Rails::VERSION::STRING >= "5.0"
143
- def log_sql_statement(payload, event)
144
- name = '%s (%.1fms)' % [payload[:name], event.duration]
145
- sql = payload[:sql].squeeze(' ')
146
- binds = nil
147
-
148
- unless (payload[:binds] || []).empty?
149
- binds = " " + payload[:binds].map { |attr| render_bind(attr) }.inspect
150
- end
151
-
152
- name = colorize_payload_name(name, payload[:name])
153
- sql = color(sql, sql_color(sql), true)
154
-
155
- debug " #{name} #{sql}#{binds}"
156
- end
157
- else
158
- def log_sql_statement(payload, event)
159
- name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
160
- sql = payload[:sql]
161
- binds = nil
162
-
163
- unless (payload[:binds] || []).empty?
164
- binds = " " + payload[:binds].map { |col,v| render_bind(col, v) }.inspect
165
- end
166
-
167
- if odd?
168
- name = color(name, CYAN, true)
169
- sql = color(sql, nil, true)
170
- else
171
- name = color(name, MAGENTA, true)
172
- end
173
-
174
- debug " #{name} #{sql}#{binds}"
175
- end
74
+ debug " #{name} #{sql}#{binds}"
176
75
  end
177
76
  end
178
77
 
@@ -180,11 +79,13 @@ module ActiveRecord
180
79
 
181
80
  module Railties
182
81
  module ControllerRuntime
82
+ remove_method :cleanup_view_runtime
183
83
  def cleanup_view_runtime
184
84
  # this method has been redefined to do nothing for activerecord on purpose
185
85
  super
186
86
  end
187
87
 
88
+ remove_method :append_info_to_payload
188
89
  def append_info_to_payload(payload)
189
90
  super
190
91
  if ActiveRecord::Base.connected?
@@ -194,6 +95,7 @@ module ActiveRecord
194
95
 
195
96
  module ClassMethods
196
97
  # this method has been redefined to do nothing for activerecord on purpose
98
+ remove_method :log_process_action
197
99
  def log_process_action(payload)
198
100
  super
199
101
  end
@@ -1,9 +1,17 @@
1
+ require 'active_support/core_ext/time/conversions'
2
+ require 'active_support/core_ext/object/blank'
3
+ require 'active_support/log_subscriber'
4
+ require 'action_dispatch/http/request'
5
+ require 'rack/body_proxy'
6
+
1
7
  module TimeBandits
2
8
  module Rack
9
+ # Sets log tags, logs the request, calls the app, and flushes the logs.
3
10
  class Logger < ActiveSupport::LogSubscriber
4
- def initialize(app, taggers=nil)
5
- @app = app
6
- @taggers = taggers || Rails.application.config.log_tags || []
11
+ def initialize(app, taggers = nil)
12
+ @app = app
13
+ @taggers = taggers || Rails.application.config.log_tags || []
14
+ @instrumenter = ActiveSupport::Notifications.instrumenter
7
15
  end
8
16
 
9
17
  def call(env)
@@ -16,15 +24,29 @@ module TimeBandits
16
24
  end
17
25
  end
18
26
 
19
- protected
27
+ protected
20
28
 
21
29
  def call_app(request, env)
22
30
  start_time = Time.now
23
- before_dispatch(request, env, start_time)
24
- result = @app.call(env)
31
+ start(request, start_time)
32
+ resp = @app.call(env)
33
+ resp[2] = ::Rack::BodyProxy.new(resp[2]) { finish(request) }
34
+ resp
35
+ rescue
36
+ finish(request)
37
+ raise
25
38
  ensure
26
- run_time = Time.now - start_time
27
- after_dispatch(env, result, run_time)
39
+ completed(request, (Time.now - start_time) * 1000, resp)
40
+ ActiveSupport::LogSubscriber.flush_all!
41
+ end
42
+
43
+ # Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700
44
+ def started_request_message(request, start_time=Time.now)
45
+ 'Started %s "%s" for %s at %s' % [
46
+ request.request_method,
47
+ request.filtered_path,
48
+ request.ip,
49
+ start_time.to_default_s ]
28
50
  end
29
51
 
30
52
  def compute_tags(request)
@@ -40,28 +62,33 @@ module TimeBandits
40
62
  end
41
63
  end
42
64
 
43
- def before_dispatch(request, env, start_time)
65
+ private
66
+
67
+ def start(request, start_time)
44
68
  TimeBandits.reset
45
69
  Thread.current.thread_variable_set(:time_bandits_completed_info, nil)
70
+ @instrumenter.start 'action_dispatch.request', request: request
46
71
 
47
- path = request.filtered_path
48
-
49
- debug ""
50
- info "Started #{request.request_method} \"#{path}\" for #{request.ip} at #{start_time.to_default_s}"
72
+ logger.debug ""
73
+ logger.info started_request_message(request, start_time)
51
74
  end
52
75
 
53
- def after_dispatch(env, result, run_time)
54
- status = result ? result.first.to_i : 500
76
+ def completed(request, run_time, resp)
77
+ status = resp ? resp.first.to_i : 500
55
78
  completed_info = Thread.current.thread_variable_get(:time_bandits_completed_info)
56
79
  additions = completed_info[1] if completed_info
57
-
58
80
  message = "Completed #{status} #{::Rack::Utils::HTTP_STATUS_CODES[status]} in %.1fms" % run_time
59
81
  message << " (#{additions.join(' | ')})" unless additions.blank?
60
- info message
61
- ensure
62
- ActiveSupport::LogSubscriber.flush_all!
82
+ logger.info message
63
83
  end
64
84
 
85
+ def finish(request)
86
+ @instrumenter.finish 'action_dispatch.request', request: request
87
+ end
88
+
89
+ def logger
90
+ @logger ||= Rails.logger
91
+ end
65
92
  end
66
93
  end
67
94
  end
@@ -1,11 +1,7 @@
1
1
  module TimeBandits
2
2
 
3
3
  module Rack
4
- if Rails::VERSION::STRING >= "4.0"
5
- autoload :Logger, 'time_bandits/rack/logger40'
6
- else
7
- autoload :Logger, 'time_bandits/rack/logger'
8
- end
4
+ autoload :Logger, 'time_bandits/rack/logger'
9
5
  end
10
6
 
11
7
  class Railtie < Rails::Railtie
@@ -19,7 +15,7 @@ module TimeBandits
19
15
  # Rails 5 may trigger the on_load event several times.
20
16
  next if included_modules.include?(ActionController::TimeBanditry)
21
17
  # For some magic reason, the test above is always false, but I'll leave it in
22
- # here, should rails every decide to change this behavior.
18
+ # here, should the Rails team ever decide to change this behavior.
23
19
 
24
20
  include ActionController::TimeBanditry
25
21
 
@@ -1,21 +1,9 @@
1
- if Rails::VERSION::STRING =~ /\A4.[0123]/
2
- require "time_bandits/monkey_patches/active_support_cache_store"
3
- end
4
-
5
1
  module TimeBandits::TimeConsumers
6
2
  class Dalli < BaseConsumer
7
3
  prefix :memcache
8
4
  fields :time, :calls, :misses, :reads, :writes
9
5
  format "Dalli: %.3f(%dr,%dm,%dw,%dc)", :time, :reads, :misses, :writes, :calls
10
6
 
11
- if Rails::VERSION::STRING >= "4.0" && Rails::VERSION::STRING < "4.2" && Rails.cache.class.respond_to?(:instrument=)
12
- # Rails 4 mem_cache_store (which uses dalli internally), unlike dalli_store, is not instrumented by default
13
- def reset
14
- Rails.cache.class.instrument = true
15
- super
16
- end
17
- end
18
-
19
7
  class Subscriber < ActiveSupport::LogSubscriber
20
8
  # cache events are: read write fetch_hit generate delete read_multi increment decrement clear
21
9
  def cache_read(event)
@@ -33,27 +33,11 @@ module TimeBandits
33
33
  def _get_gc_time; 0; end
34
34
  end
35
35
 
36
- if GC.respond_to?(:collections)
37
- def _get_collections; GC.collections; end
38
- elsif GC.respond_to?(:count)
39
- def _get_collections; GC.count; end
40
- else
41
- def _get_collections; 0; end
42
- end
36
+ def _get_collections; GC.count; end
43
37
 
44
- if ObjectSpace.respond_to?(:allocated_objects)
45
- def _get_allocated_objects; ObjectSpace.allocated_objects; end
46
- elsif GC.respond_to?(:stat) && RUBY_VERSION >= "2.2.0"
47
- def _get_allocated_objects; GC.stat(:total_allocated_objects); end
48
- elsif GC.respond_to?(:stat) && RUBY_VERSION >= "2.1.0"
49
- def _get_allocated_objects; GC.stat(:total_allocated_object); end
50
- else
51
- def _get_allocated_objects; 0; end
52
- end
38
+ def _get_allocated_objects; GC.stat(:total_allocated_objects); end
53
39
 
54
- if GC.respond_to?(:allocated_size)
55
- def _get_allocated_size; GC.allocated_size; end
56
- elsif GC.respond_to?(:total_malloced_bytes)
40
+ if GC.respond_to?(:total_malloced_bytes)
57
41
  def _get_allocated_size; GC.total_malloced_bytes; end
58
42
  else
59
43
  def _get_allocated_size; 0; end
@@ -61,22 +45,14 @@ module TimeBandits
61
45
 
62
46
  if GC.respond_to?(:heap_slots)
63
47
  def _get_heap_slots; GC.heap_slots; end
64
- elsif GC.respond_to?(:stat) && RUBY_VERSION >= "2.2.0"
65
- def _get_heap_slots; GC.stat(:heap_live_slots) + GC.stat(:heap_free_slots) + GC.stat(:heap_final_slots); end
66
- elsif GC.respond_to?(:stat) && RUBY_VERSION >= "2.1.0"
67
- def _get_heap_slots; GC.stat(:heap_live_slot) + GC.stat(:heap_free_slot) + GC.stat(:heap_final_slot); end
68
48
  else
69
- def _get_heap_slots; 0; end
49
+ def _get_heap_slots; GC.stat(:heap_live_slots) + GC.stat(:heap_free_slots) + GC.stat(:heap_final_slots); end
70
50
  end
71
51
 
72
52
  if GC.respond_to?(:heap_slots_live_after_last_gc)
73
53
  def live_data_set_size; GC.heap_slots_live_after_last_gc; end
74
- elsif GC.respond_to?(:stat) && RUBY_VERSION >= "2.2.0"
75
- def live_data_set_size; GC.stat(:heap_live_slots); end
76
- elsif GC.respond_to?(:stat) && RUBY_VERSION >= "2.1.0"
77
- def live_data_set_size; GC.stat(:heap_live_slot); end
78
54
  else
79
- def live_data_set_size; 0; end
55
+ def live_data_set_size; GC.stat(:heap_live_slots); end
80
56
  end
81
57
 
82
58
  def reset
@@ -1,3 +1,3 @@
1
1
  module TimeBandits
2
- VERSION = "0.11.0"
2
+ VERSION = "0.12.0"
3
3
  end
@@ -1,5 +1,5 @@
1
1
  require 'minitest'
2
- require 'mocha/setup'
2
+ require 'mocha/minitest'
3
3
  require 'minitest/pride'
4
4
  require 'minitest/autorun'
5
5
 
@@ -18,19 +18,11 @@ end
18
18
  require_relative '../lib/time_bandits'
19
19
  require "byebug"
20
20
 
21
- ActiveSupport::LogSubscriber.class_eval do
22
- # need a logger, otherwise no data will be collected
23
- def logger
24
- @logger ||= ::Logger.new("/dev/null")
25
- end
26
- end
21
+ ActiveSupport::LogSubscriber.logger =::Logger.new("/dev/null")
27
22
 
28
23
  # fake Rails
29
24
  module Rails
30
25
  extend self
31
- module VERSION
32
- STRING = ActiveSupport::VERSION::STRING
33
- end
34
26
  def cache
35
27
  @cache ||= ActiveSupport::Cache.lookup_store(:mem_cache_store)
36
28
  end
@@ -49,11 +49,10 @@ class GCConsumerTest < Test::Unit::TestCase
49
49
  def check_work
50
50
  GC.start
51
51
  m = TimeBandits.metrics
52
- number_class = RUBY_VERSION >= "2.4.0" ? Integer : Fixnum
53
52
  if GC.respond_to?(:time)
54
53
  assert_operator 0, :<, m[:gc_calls]
55
54
  assert_operator 0, :<, m[:gc_time]
56
- assert_instance_of number_class, m[:heap_growth]
55
+ assert_instance_of Integer, m[:heap_growth]
57
56
  assert_operator 0, :<, m[:heap_size]
58
57
  assert_operator 0, :<, m[:allocated_objects]
59
58
  assert_operator 0, :<, m[:allocated_bytes]
@@ -61,7 +60,7 @@ class GCConsumerTest < Test::Unit::TestCase
61
60
  else
62
61
  assert_operator 0, :<, m[:gc_calls]
63
62
  assert_operator 0, :<=, m[:gc_time]
64
- assert_instance_of number_class, m[:heap_growth]
63
+ assert_instance_of Integer, m[:heap_growth]
65
64
  assert_operator 0, :<, m[:heap_size]
66
65
  assert_operator 0, :<, m[:allocated_objects]
67
66
  assert_operator 0, :<=, m[:allocated_bytes]
@@ -31,8 +31,12 @@ class SequelTest < Test::Unit::TestCase
31
31
  assert_equal metrics[:db_time], TimeBandits.consumed
32
32
  end
33
33
 
34
+ def mysql_port
35
+ ENV['TRAVIS'] == "true" ? 3306 : 3601
36
+ end
37
+
34
38
  def sequel
35
- @sequel ||= Sequel.connect('mysql2://localhost:3601')
39
+ @sequel ||= Sequel.mysql2(host: "localhost", port: mysql_port, user: "root")
36
40
  end
37
41
 
38
42
  def metrics
@@ -12,6 +12,9 @@ Gem::Specification.new do |s|
12
12
  s.summary = "Custom performance logging for Rails"
13
13
  s.description = "Rails Completed Line on Steroids"
14
14
  s.license = 'MIT'
15
+ s.metadata = {
16
+ "changelog_uri" => "https://github.com/skaes/time_bandits/blob/master/README.md#release-notes"
17
+ }
15
18
 
16
19
  s.files = `git ls-files`.split("\n")
17
20
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -19,7 +22,7 @@ Gem::Specification.new do |s|
19
22
  s.require_paths = ["lib"]
20
23
 
21
24
  s.add_runtime_dependency("thread_variables")
22
- s.add_runtime_dependency("activesupport", [">= 2.3.2"])
25
+ s.add_runtime_dependency("activesupport", [">= 5.2.0"])
23
26
 
24
27
  s.add_development_dependency("ansi")
25
28
  s.add_development_dependency("appraisal")
@@ -29,10 +32,10 @@ Gem::Specification.new do |s|
29
32
  s.add_development_dependency("minitest", "~> 5.5")
30
33
  s.add_development_dependency("mocha")
31
34
  s.add_development_dependency("mysql2")
32
- s.add_development_dependency("rake", "~> 10.5.0")
35
+ s.add_development_dependency("rake")
33
36
  s.add_development_dependency("redis")
34
37
  s.add_development_dependency("sequel")
35
38
  s.add_development_dependency("activerecord")
36
- s.add_development_dependency("beetle", ">= 0.4.6")
39
+ s.add_development_dependency("beetle", ">= 3.4.1")
37
40
  end
38
41