time_bandits 0.11.0 → 0.12.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.
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