time_bandits 0.13.1 → 0.14.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e14e3f3974575197ad97649241f327dbf69824c354044bde8bd55ff421d9c21a
4
- data.tar.gz: 4115bb4f9820a3227a62d6b4ea66c5f045c59cacb019fd89be71ff9da6b2f790
3
+ metadata.gz: 3597bdf1515a72122e85b16fffdcedc74a22c479012fa0fb2f7c42f64e0434de
4
+ data.tar.gz: a6acc865643fc97ce247b33fd55da8399323488435234d15d75766b26fc853c5
5
5
  SHA512:
6
- metadata.gz: abc74cb6ae11bdf9388f8d113a3df89d61e6403cd614ebdd8d73aaa5be84c8a2d9d50d746b57a2eb4079140e1a34c9e1e3c172428e1065d15893ad94e99330b6
7
- data.tar.gz: bd2347a6f49613abd7359524c1a08cb609acf501180b1deeb5b093318b664ff330c7a0bd8b2a577b1dee303d62ea8481162485b704d4f9041000cc58b3dd77a2
6
+ metadata.gz: c1451d6f063d1fd7feb18dd66c0650f10bf8398f7407fd4f3f7a63bef57a4bf52846267e8bf82c4e7295e41f1d999b70a16aadd1a08d1fc44fc9f174e18e74a4
7
+ data.tar.gz: cb1ff1654db60be898992d20a9b7b4825483033637147e94b8fc0192c2566ef096ce49f14b71f7ffd61a6524fa883030c78a69feb10eb90d012bca20321618a6
@@ -13,7 +13,7 @@ jobs:
13
13
 
14
14
  strategy:
15
15
  matrix:
16
- ruby-version: [3.2.0, 3.1.3, 3.0.5, 2.7.7]
16
+ ruby-version: [3.2.2, 3.1.4, 3.0.6, 2.7.8]
17
17
 
18
18
  steps:
19
19
  - uses: actions/checkout@v3
data/Appraisals CHANGED
@@ -1,10 +1,11 @@
1
1
  appraisals = [
2
- "6.0.6",
3
- "6.1.6",
2
+ "6.0.6.1",
3
+ "6.1.7.6",
4
+ "7.0.8",
5
+ "7.1.0"
4
6
  ]
5
7
 
6
8
  appraisals.insert(0, "5.2.8.1") if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.0.0")
7
- appraisals << "7.0.4" if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
8
9
 
9
10
  appraisals.each do |rails_version|
10
11
  %w(4.0 5.0).each do |redis_version|
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.14.1
4
+
5
+ * Fixed that ActiveReccord metrics where only collected at log level DEBUG.
6
+
7
+ ## 0.14.0
8
+
9
+ * Support Rails 7.1.0.
10
+
3
11
  ## 0.13.1
4
12
  * Fixed last place that tried to log a monotonic clock timestamp
5
13
  instead of a Time instance.
data/Gemfile CHANGED
@@ -4,6 +4,3 @@ source "https://rubygems.org"
4
4
  gemspec
5
5
 
6
6
  gem "hiredis-client"
7
-
8
- # Use patched appraisal gem until it is fixed upstream.
9
- gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git", ref: "0c855ae0da89fec74b4d1a01801c55b0e72496d4"
data/docker-compose.yml CHANGED
@@ -3,7 +3,7 @@ version: '2'
3
3
  services:
4
4
  mysql:
5
5
  container_name: mysql
6
- image: mysql:5.7
6
+ image: mysql:8
7
7
  ports:
8
8
  - "3601:3306"
9
9
  environment:
@@ -0,0 +1,77 @@
1
+ # This file monkey patches class ActiveRecord::LogSubscriber to count
2
+ # the number of sql statements being executed and the number of query
3
+ # cache hits, but is only used for Rails versions before 7.1.0.
4
+
5
+ require "active_record/log_subscriber"
6
+
7
+ module ActiveRecord
8
+ class LogSubscriber
9
+ IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"] unless defined?(IGNORE_PAYLOAD_NAMES)
10
+
11
+ def self.call_count=(value)
12
+ Thread.current.thread_variable_set(:active_record_sql_call_count, value)
13
+ end
14
+
15
+ def self.call_count
16
+ Thread.current.thread_variable_get(:active_record_sql_call_count) ||
17
+ Thread.current.thread_variable_set(:active_record_sql_call_count, 0)
18
+ end
19
+
20
+ def self.query_cache_hits=(value)
21
+ Thread.current.thread_variable_set(:active_record_sql_query_cache_hits, value)
22
+ end
23
+
24
+ def self.query_cache_hits
25
+ Thread.current.thread_variable_get(:active_record_sql_query_cache_hits) ||
26
+ Thread.current.thread_variable_set(:active_record_sql_query_cache_hits, 0)
27
+ end
28
+
29
+ def self.reset_call_count
30
+ calls = call_count
31
+ self.call_count = 0
32
+ calls
33
+ end
34
+
35
+ def self.reset_query_cache_hits
36
+ hits = query_cache_hits
37
+ self.query_cache_hits = 0
38
+ hits
39
+ end
40
+
41
+ remove_method :sql
42
+ def sql(event)
43
+ payload = event.payload
44
+
45
+ self.class.runtime += event.duration
46
+ self.class.call_count += 1
47
+ self.class.query_cache_hits += 1 if payload[:cached] || payload[:name] == "CACHE"
48
+
49
+ return unless logger.debug?
50
+
51
+ return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
52
+
53
+ log_sql_statement(payload, event)
54
+ end
55
+
56
+ private
57
+ def log_sql_statement(payload, event)
58
+ name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
59
+ name = "CACHE #{name}" if payload[:cached]
60
+ sql = payload[:sql]
61
+ binds = nil
62
+
63
+ unless (payload[:binds] || []).empty?
64
+ casted_params = type_casted_binds(payload[:type_casted_binds])
65
+ binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
66
+ render_bind(attr, value)
67
+ }.inspect
68
+ end
69
+
70
+ name = colorize_payload_name(name, payload[:name])
71
+ sql = color(sql, sql_color(sql), true)
72
+
73
+ debug " #{name} #{sql}#{binds}"
74
+ end
75
+ end
76
+
77
+ end
@@ -0,0 +1,29 @@
1
+ require "active_record/railties/controller_runtime"
2
+
3
+ module ActiveRecord
4
+ module Railties
5
+ module ControllerRuntime
6
+ remove_method :cleanup_view_runtime
7
+ def cleanup_view_runtime
8
+ # this method has been redefined to do nothing for activerecord on purpose
9
+ super
10
+ end
11
+
12
+ remove_method :append_info_to_payload
13
+ def append_info_to_payload(payload)
14
+ super
15
+ if ActiveRecord::Base.connected?
16
+ payload[:db_runtime] = TimeBandits::TimeConsumers::Database.instance.consumed
17
+ end
18
+ end
19
+
20
+ module ClassMethods
21
+ # this method has been redefined to do nothing for activerecord on purpose
22
+ remove_method :log_process_action
23
+ def log_process_action(payload)
24
+ super
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,50 @@
1
+ # This file monkey patches class ActiveRecord::RuntimeRegistry to
2
+ # additionally store call counts and cache hits and subscribes an
3
+ # event listener to manage those counters.
4
+
5
+ require "active_record/runtime_registry"
6
+
7
+ module ActiveRecord
8
+ module RuntimeRegistry
9
+
10
+ def self.call_count
11
+ ActiveSupport::IsolatedExecutionState[:active_record_sql_call_count] ||= 0
12
+ end
13
+
14
+ def self.call_count=(value)
15
+ ActiveSupport::IsolatedExecutionState[:active_record_sql_call_count] = value
16
+ end
17
+
18
+ def self.query_cache_hits
19
+ ActiveSupport::IsolatedExecutionState[:active_record_sql_query_cache_hits] ||= 0
20
+ end
21
+
22
+ def self.query_cache_hits=(value)
23
+ ActiveSupport::IsolatedExecutionState[:active_record_sql_query_cache_hits] = value
24
+ end
25
+
26
+ alias_method :reset_runtime, :reset
27
+ alias_method :runtime, :sql_runtime
28
+ alias_method :runtime=, :sql_runtime=
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
+ end
43
+ end
44
+
45
+ require "active_support/notifications"
46
+
47
+ ActiveSupport::Notifications.subscribe("sql.active_record") do |event|
48
+ ActiveRecord::RuntimeRegistry.call_count += 1
49
+ ActiveRecord::RuntimeRegistry.query_cache_hits += 1 if event.payload[:cached]
50
+ end
@@ -1,105 +1,6 @@
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
- require "active_record/log_subscriber"
7
-
8
- module ActiveRecord
9
- class LogSubscriber
10
- IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"] unless defined?(IGNORE_PAYLOAD_NAMES)
11
-
12
- def self.call_count=(value)
13
- Thread.current.thread_variable_set(:active_record_sql_call_count, value)
14
- end
15
-
16
- def self.call_count
17
- Thread.current.thread_variable_get(:active_record_sql_call_count) ||
18
- Thread.current.thread_variable_set(:active_record_sql_call_count, 0)
19
- end
20
-
21
- def self.query_cache_hits=(value)
22
- Thread.current.thread_variable_set(:active_record_sql_query_cache_hits, value)
23
- end
24
-
25
- def self.query_cache_hits
26
- Thread.current.thread_variable_get(:active_record_sql_query_cache_hits) ||
27
- Thread.current.thread_variable_set(: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
- remove_method :sql
43
- def sql(event)
44
- payload = event.payload
45
-
46
- self.class.runtime += event.duration
47
- self.class.call_count += 1
48
- self.class.query_cache_hits += 1 if payload[:cached] || payload[:name] == "CACHE"
49
-
50
- return unless logger.debug?
51
-
52
- return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
53
-
54
- log_sql_statement(payload, event)
55
- end
56
-
57
- private
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
69
- end
70
-
71
- name = colorize_payload_name(name, payload[:name])
72
- sql = color(sql, sql_color(sql), true)
73
-
74
- debug " #{name} #{sql}#{binds}"
75
- end
76
- end
77
-
78
- require "active_record/railties/controller_runtime"
79
-
80
- module Railties
81
- module ControllerRuntime
82
- remove_method :cleanup_view_runtime
83
- def cleanup_view_runtime
84
- # this method has been redefined to do nothing for activerecord on purpose
85
- super
86
- end
87
-
88
- remove_method :append_info_to_payload
89
- def append_info_to_payload(payload)
90
- super
91
- if ActiveRecord::Base.connected?
92
- payload[:db_runtime] = TimeBandits::TimeConsumers::Database.instance.consumed
93
- end
94
- end
95
-
96
- module ClassMethods
97
- # this method has been redefined to do nothing for activerecord on purpose
98
- remove_method :log_process_action
99
- def log_process_action(payload)
100
- super
101
- end
102
- end
103
- end
104
- end
1
+ if Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("7.1.0")
2
+ require_relative "active_record/log_subscriber"
3
+ else
4
+ require_relative "active_record/runtime_registry"
105
5
  end
6
+ require_relative "active_record/railties/controller_runtime"
@@ -13,6 +13,18 @@ module TimeBandits
13
13
  fields :time, :calls, :sql_query_cache_hits
14
14
  format "ActiveRecord: %.3fms(%dq,%dh)", :time, :calls, :sql_query_cache_hits
15
15
 
16
+ class << self
17
+ if Gem::Version.new(ActiveRecord::VERSION::STRING) >= Gem::Version.new("7.1.0")
18
+ def metrics_store
19
+ ActiveRecord::RuntimeRegistry
20
+ end
21
+ else
22
+ def metrics_store
23
+ ActiveRecord::LogSubscriber
24
+ end
25
+ end
26
+ end
27
+
16
28
  def reset
17
29
  reset_stats
18
30
  super
@@ -27,13 +39,13 @@ module TimeBandits
27
39
  end
28
40
 
29
41
  def current_runtime
30
- Database.instance.time + ActiveRecord::LogSubscriber.runtime
42
+ Database.instance.time + self.class.metrics_store.runtime
31
43
  end
32
44
 
33
45
  private
34
46
 
35
47
  def reset_stats
36
- s = ActiveRecord::LogSubscriber
48
+ s = self.class.metrics_store
37
49
  hits = s.reset_query_cache_hits
38
50
  calls = s.reset_call_count
39
51
  time = s.reset_runtime
@@ -1,3 +1,3 @@
1
1
  module TimeBandits
2
- VERSION = "0.13.1"
2
+ VERSION = "0.14.1"
3
3
  end
data/test/test_helper.rb CHANGED
@@ -23,6 +23,7 @@ ActiveSupport::LogSubscriber.logger =::Logger.new("/dev/null")
23
23
  # fake Rails
24
24
  module Rails
25
25
  extend self
26
+ ActiveSupport::Cache.format_version = 7.1 if Gem::Version.new(ActiveSupport::VERSION::STRING) >= Gem::Version.new("7.1.0")
26
27
  def cache
27
28
  @cache ||= ActiveSupport::Cache.lookup_store(:mem_cache_store)
28
29
  end
@@ -10,6 +10,15 @@ class DatabaseTest < Test::Unit::TestCase
10
10
  TimeBandits.reset
11
11
  @old_logger = ActiveRecord::Base.logger
12
12
  ActiveRecord::Base.logger = Logger.new($stdout)
13
+ ActiveRecord::Base.logger.level = Logger::DEBUG
14
+
15
+ ActiveRecord::Base.establish_connection(
16
+ adapter: "mysql2",
17
+ username: "root",
18
+ encoding: "utf8",
19
+ host: ENV['MYSQL_HOST'] || "127.0.0.1",
20
+ port: (ENV['MYSQL_PORT'] || 3601).to_i
21
+ )
13
22
  end
14
23
 
15
24
  def teardown
@@ -28,18 +37,18 @@ class DatabaseTest < Test::Unit::TestCase
28
37
  end
29
38
 
30
39
  test "formatting" do
31
- log_subscriber.runtime += 1.234
32
- log_subscriber.call_count += 3
33
- log_subscriber.query_cache_hits += 1
40
+ metrics_store.runtime += 1.234
41
+ metrics_store.call_count += 3
42
+ metrics_store.query_cache_hits += 1
34
43
  TimeBandits.consumed
35
44
  assert_equal "ActiveRecord: 1.234ms(3q,1h)", TimeBandits.runtime
36
45
  end
37
46
 
38
47
  test "accessing current runtime" do
39
- log_subscriber.runtime += 1.234
48
+ metrics_store.runtime += 1.234
40
49
  assert_equal 1.234, TimeBandits.consumed
41
- assert_equal 0, log_subscriber.runtime
42
- log_subscriber.runtime += 4.0
50
+ assert_equal 0, metrics_store.runtime
51
+ metrics_store.runtime += 4.0
43
52
  assert_equal 5.234, bandit.current_runtime
44
53
  assert_equal "ActiveRecord: 1.234ms(0q,0h)", TimeBandits.runtime
45
54
  end
@@ -52,12 +61,38 @@ class DatabaseTest < Test::Unit::TestCase
52
61
  assert_nil log_subscriber.new.sql(event)
53
62
  end
54
63
 
64
+ test "instrumentation records runtimes at log level debug" do
65
+ ActiveRecord::Base.logger.stubs(:debug)
66
+ ActiveRecord::Base.connection.execute "SELECT 1"
67
+ bandit.consumed
68
+ assert(bandit.current_runtime > 0)
69
+ # 2 calls, because one configures the connection
70
+ assert_equal 2, bandit.calls
71
+ assert_equal 0, bandit.sql_query_cache_hits
72
+ end
73
+
74
+ test "instrumentation records runtimes at log level error" do
75
+ skip if Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("7.1.0")
76
+ ActiveRecord::Base.logger.level = Logger::ERROR
77
+ ActiveRecord::LogSubscriber.expects(:sql).never
78
+ ActiveRecord::Base.connection.execute "SELECT 1"
79
+ bandit.consumed
80
+ assert(bandit.current_runtime > 0)
81
+ # 2 calls, because one configures the connection
82
+ assert_equal 2, bandit.calls
83
+ assert_equal 0, bandit.sql_query_cache_hits
84
+ end
85
+
55
86
  private
56
87
 
57
88
  def bandit
58
89
  TimeBandits::TimeConsumers::Database.instance
59
90
  end
60
91
 
92
+ def metrics_store
93
+ TimeBandits::TimeConsumers::Database.metrics_store
94
+ end
95
+
61
96
  def log_subscriber
62
97
  ActiveRecord::LogSubscriber
63
98
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: time_bandits
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.1
4
+ version: 0.14.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Kaes
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-28 00:00:00.000000000 Z
11
+ date: 2023-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thread_variables
@@ -240,6 +240,9 @@ files:
240
240
  - lib/time_bandits.rb
241
241
  - lib/time_bandits/monkey_patches/action_controller.rb
242
242
  - lib/time_bandits/monkey_patches/active_record.rb
243
+ - lib/time_bandits/monkey_patches/active_record/log_subscriber.rb
244
+ - lib/time_bandits/monkey_patches/active_record/railties/controller_runtime.rb
245
+ - lib/time_bandits/monkey_patches/active_record/runtime_registry.rb
243
246
  - lib/time_bandits/monkey_patches/memcache-client.rb
244
247
  - lib/time_bandits/monkey_patches/memcached.rb
245
248
  - lib/time_bandits/monkey_patches/redis.rb
@@ -289,7 +292,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
289
292
  - !ruby/object:Gem::Version
290
293
  version: '0'
291
294
  requirements: []
292
- rubygems_version: 3.4.1
295
+ rubygems_version: 3.4.10
293
296
  signing_key:
294
297
  specification_version: 4
295
298
  summary: Custom performance logging for Rails