active_record_query_trace 1.5.4 → 1.8

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
- SHA1:
3
- metadata.gz: 45e5fb5ece85b56cde741aab63f6d4ef1cdd4674
4
- data.tar.gz: 75090841d4650bd2ebde1517fc040fec661a25a1
2
+ SHA256:
3
+ metadata.gz: 06d7497a2165d922974efd1ffc10bf6b6725d1c4705f9b5d5464affca6db069e
4
+ data.tar.gz: 1d5bf04f35d74b69cddc657d9f91795c315ce4b50422cc79aa06843a0299d3e9
5
5
  SHA512:
6
- metadata.gz: 0a1ff57686ca621ef4cd4ed12dadcb0bd6b4f9b2f425492bf3d814e74ff4e4e362ad24985b17f0ccc4ea6f83c8e5f631834ba3cedb9401204cd6dfbb9b3eb377
7
- data.tar.gz: 676166a2fa21b3975290260fba1d8bc83db77a25a765a33bd800a5eb9f8266f823b1da0d3c6e8d181ad37ee92e98ccbfe0bb05e73fc8a54175c2a381133b13b2
6
+ metadata.gz: '09cd3daa1e090acc6e8bafd776c28c3df40f190baccd3799e5b93629a416f890ce4b4514993842830159840fe8d688ef71ba45f54893c62cf053b045c8dd978f'
7
+ data.tar.gz: d53226b99e6eae63ea2b79abf5db75f4f04c546773dc61fda88962d9de2b4c465f84c3af6481b3778d0410e3c4ed07433b29ba29eba5d4481c04efe9d836118c
@@ -1,7 +1,29 @@
1
- # encoding: UTF-8
2
- require 'active_support/log_subscriber'
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record/log_subscriber'
3
4
 
4
5
  module ActiveRecordQueryTrace
6
+ INDENTATION = ' ' * 6
7
+ BACKTRACE_PREFIX = "Query Trace:\n#{INDENTATION}"
8
+ COLORS = {
9
+ true => '38',
10
+ blue: '34',
11
+ light_red: '1;31',
12
+ black: '30',
13
+ purple: '35',
14
+ light_green: '1;32',
15
+ red: '31',
16
+ cyan: '36',
17
+ yellow: '1;33',
18
+ green: '32',
19
+ gray: '37',
20
+ light_blue: '1;34',
21
+ brown: '33',
22
+ dark_gray: '1;30',
23
+ light_purple: '1;35',
24
+ white: '1;37',
25
+ light_cyan: '1;36'
26
+ }.freeze
5
27
 
6
28
  class << self
7
29
  attr_accessor :enabled
@@ -9,89 +31,220 @@ module ActiveRecordQueryTrace
9
31
  attr_accessor :lines
10
32
  attr_accessor :ignore_cached_queries
11
33
  attr_accessor :colorize
34
+ attr_accessor :query_type
35
+ attr_accessor :suppress_logging_of_db_reads
36
+ attr_reader :backtrace_cleaner
37
+
38
+ def backtrace_cleaner=(cleaner)
39
+ @backtrace_cleaner =
40
+ if cleaner.is_a?(Proc)
41
+ cleaner
42
+ else
43
+ proc { |trace| cleaner.clean(trace) }
44
+ end
45
+ end
12
46
  end
13
47
 
14
- module ActiveRecord
15
- class LogSubscriber < ActiveSupport::LogSubscriber
48
+ class CustomLogSubscriber < ActiveRecord::LogSubscriber # rubocop:disable Metrics/ClassLength
49
+ def initialize
50
+ super
51
+ ActiveRecordQueryTrace.enabled = false
52
+ ActiveRecordQueryTrace.level = :app
53
+ ActiveRecordQueryTrace.lines = 5
54
+ ActiveRecordQueryTrace.ignore_cached_queries = false
55
+ ActiveRecordQueryTrace.colorize = false
56
+ ActiveRecordQueryTrace.query_type = :all
57
+ ActiveRecordQueryTrace.suppress_logging_of_db_reads = false
58
+ end
16
59
 
17
- def initialize
18
- super
19
- ActiveRecordQueryTrace.enabled = false
20
- ActiveRecordQueryTrace.level = :app
21
- ActiveRecordQueryTrace.lines = 5
22
- ActiveRecordQueryTrace.ignore_cached_queries = false
23
- ActiveRecordQueryTrace.colorize = false
60
+ def sql(event)
61
+ payload = event.payload
62
+ return unless display_backtrace?(payload)
24
63
 
25
- if ActiveRecordQueryTrace.level != :app
26
- # Rails by default silences all backtraces that match Rails::BacktraceCleaner::APP_DIRS_PATTERN
27
- Rails.backtrace_cleaner.remove_silencers!
28
- end
64
+ setup_backtrace_cleaner unless ActiveRecordQueryTrace.backtrace_cleaner
65
+
66
+ trace = fully_formatted_trace # Memoize
67
+ debug(trace) unless trace.blank?
68
+ end
69
+
70
+ attach_to :active_record
71
+
72
+ private
73
+
74
+ def cached_query?(payload)
75
+ return false unless ActiveRecordQueryTrace.ignore_cached_queries
76
+ payload[:cached] || payload[:name] == 'CACHE'
77
+ end
78
+
79
+ # TODO: refactor and remove rubocop:disable comments.
80
+ def display_backtrace?(payload)
81
+ ActiveRecordQueryTrace.enabled \
82
+ && !transaction_begin_or_commit_query?(payload) \
83
+ && !schema_query?(payload) \
84
+ && !cached_query?(payload) \
85
+ && !(ActiveRecordQueryTrace.suppress_logging_of_db_reads && db_read_query?(payload)) \
86
+ && display_backtrace_for_query_type?(payload)
87
+ end
88
+
89
+ def display_backtrace_for_query_type?(payload)
90
+ case ActiveRecordQueryTrace.query_type
91
+ when :all then true
92
+ when :read then db_read_query?(payload)
93
+ when :write then !db_read_query?(payload)
94
+ else
95
+ raise 'Invalid ActiveRecordQueryTrace.query_type value ' \
96
+ "#{ActiveRecordQueryTrace.level}. Should be :all, :read, or :write."
29
97
  end
98
+ end
99
+
100
+ def db_read_query?(payload)
101
+ payload[:sql] !~ /INSERT|UPDATE|DELETE/
102
+ end
103
+
104
+ def fully_formatted_trace
105
+ cleaned_trace = clean_trace(original_trace)
106
+ return if cleaned_trace.blank?
107
+ stringified_trace = BACKTRACE_PREFIX + lines_to_display(cleaned_trace).join("\n" + INDENTATION)
108
+ colorize_text(stringified_trace)
109
+ end
30
110
 
31
- def sql(event)
32
- if ActiveRecordQueryTrace.enabled
33
- index = begin
34
- if ActiveRecordQueryTrace.lines == 0
35
- 0..-1
36
- else
37
- 0..(ActiveRecordQueryTrace.lines - 1)
38
- end
39
- end
40
-
41
- payload = event.payload
42
- return if payload[:name] == 'SCHEMA'
43
- return if ActiveRecordQueryTrace.ignore_cached_queries && payload[:name] == 'CACHE'
44
-
45
- cleaned_trace = clean_trace(caller)[index].join("\n from ")
46
- debug(" Query Trace > " + colorize_text(cleaned_trace)) unless cleaned_trace.blank?
111
+ # Must be called after the backtrace cleaner.
112
+ def lines_to_display(full_trace)
113
+ ActiveRecordQueryTrace.lines.zero? ? full_trace : full_trace.first(ActiveRecordQueryTrace.lines)
114
+ end
115
+
116
+ def transaction_begin_or_commit_query?(payload)
117
+ payload[:sql] =~ /\Abegin transaction|commit transaction|BEGIN|COMMIT\Z/
118
+ end
119
+
120
+ def schema_query?(payload)
121
+ payload[:name] == 'SCHEMA'
122
+ end
123
+
124
+ # rubocop:disable Metrics/MethodLength
125
+ def clean_trace(full_trace)
126
+ case ActiveRecordQueryTrace.level
127
+ when :full
128
+ trace = full_trace
129
+ when :app, :rails
130
+ trace = Rails.backtrace_cleaner.clean(full_trace)
131
+ when :custom
132
+ unless ActiveRecordQueryTrace.backtrace_cleaner
133
+ raise 'Configure your backtrace cleaner first via ActiveRecordQueryTrace.backtrace_cleaner = MyCleaner'
47
134
  end
135
+ trace = ActiveRecordQueryTrace.backtrace_cleaner.call(full_trace)
136
+ else
137
+ raise 'Invalid ActiveRecordQueryTrace.level value ' \
138
+ "#{ActiveRecordQueryTrace.level}. Should be :full, :rails, or :app."
48
139
  end
49
140
 
50
- # Allow query to be colorized in the terminal
51
- def colorize_text(text)
52
- return text unless ActiveRecordQueryTrace.colorize
53
- # Try to convert the choosen color from string to integer or try
54
- # to use the colorize as the color code
55
- colors = {
56
- true => "38", "blue" => "34", "light red" => "1;31",
57
- "black" => "30", "purple" => "35", "light green" => "1;32",
58
- "red" => "31", "cyan" => "36", "yellow" => "1;33",
59
- "green" => "32", "gray" => "37", "light blue" => "1;34",
60
- "brown" => "33", "dark gray" => "1;30", "light purple" => "1;35",
61
- "white" => "1;37", "light cyan" => "1;36"
62
- }
63
- color_code = colors[ActiveRecordQueryTrace.colorize] ||
64
- ActiveRecordQueryTrace.colorize.to_s
65
- unless /\d+(;\d+){0,1}/.match(color_code)
66
- raise "Invalid color. Use one of #{ colors.keys } or a valid color code"
67
- end
68
- "\e[#{ color_code }m#{ text }\e[0m"
141
+ # We cant use a Rails::BacktraceCleaner filter to display only the relative
142
+ # path of application trace lines because it breaks the silencer that selects
143
+ # the lines to display or hide based on whether they include `Rails.root`.
144
+ trace.map { |line| line.sub(rails_root_prefix, '') }
145
+ end
146
+ # rubocop:enable Metrics/MethodLength
147
+
148
+ # Rails by default silences all backtraces that *do not* match
149
+ # Rails::BacktraceCleaner::APP_DIRS_PATTERN. In other words, the default
150
+ # silencer filters out all framework backtrace lines, leaving only the
151
+ # application lines.
152
+ def setup_backtrace_cleaner
153
+ setup_backtrace_cleaner_path
154
+ return if ActiveRecordQueryTrace.level == :full
155
+
156
+ remove_filters_and_silencers
157
+
158
+ case ActiveRecordQueryTrace.level
159
+ when :app
160
+ Rails.backtrace_cleaner.add_silencer { |line| line !~ rails_root_regexp }
161
+ when :rails
162
+ Rails.backtrace_cleaner.add_silencer { |line| line =~ rails_root_regexp }
69
163
  end
164
+ end
70
165
 
71
- def clean_trace(trace)
72
- # Rails relies on backtrace cleaner to set the application root directory filter
73
- # the problem is that the backtrace cleaner is initialized before the application
74
- # this ensures that the value of `root` used by the filter is set to the application root
75
- if Rails.backtrace_cleaner.instance_variable_get(:@root) == '/'
76
- Rails.backtrace_cleaner.instance_variable_set :@root, Rails.root.to_s
77
- end
166
+ # Rails relies on backtrace cleaner to set the application root directory
167
+ # filter. The problem is that the backtrace cleaner is initialized before
168
+ # this gem. This ensures that the value of `root` used by the filter
169
+ # is correct.
170
+ def setup_backtrace_cleaner_path
171
+ return unless Rails.backtrace_cleaner.instance_variable_get(:@root) == '/'
172
+ Rails.backtrace_cleaner.instance_variable_set :@root, Rails.root.to_s
173
+ end
78
174
 
79
- case ActiveRecordQueryTrace.level
80
- when :full
81
- trace
82
- when :rails
83
- Rails.respond_to?(:backtrace_cleaner) ? Rails.backtrace_cleaner.clean(trace) : trace
84
- when :app
85
- Rails.backtrace_cleaner.remove_silencers!
86
- Rails.backtrace_cleaner.add_silencer { |line| not line =~ /^(app|lib|engines)\// }
87
- Rails.backtrace_cleaner.clean(trace)
88
- else
89
- raise "Invalid ActiveRecordQueryTrace.level value '#{ActiveRecordQueryTrace.level}' - should be :full, :rails, or :app"
175
+ def remove_filters_and_silencers
176
+ Rails.backtrace_cleaner.remove_filters!
177
+ Rails.backtrace_cleaner.remove_silencers!
178
+ end
179
+
180
+ # Allow query to be colorized in the terminal
181
+ def colorize_text(text)
182
+ return text unless ActiveRecordQueryTrace.colorize
183
+ "\e[#{color_code}m#{text}\e[0m"
184
+ end
185
+
186
+ # Wrapper used for testing purposes.
187
+ def original_trace
188
+ caller
189
+ end
190
+
191
+ def color_code
192
+ return @color_code if @color_code && @configured_color == ActiveRecordQueryTrace.colorize
193
+
194
+ @configured_color = ActiveRecordQueryTrace.colorize
195
+
196
+ # Backward compatibility for string color names with space as word separator.
197
+ @color_code =
198
+ case ActiveRecordQueryTrace.colorize
199
+ when Symbol, true then COLORS[ActiveRecordQueryTrace.colorize]
200
+ when String then COLORS[ActiveRecordQueryTrace.colorize.tr("\s", '_').to_sym]
90
201
  end
91
- end
202
+ end
92
203
 
93
- attach_to :active_record
204
+ def validate_color_code(color_code)
205
+ valid_color_code?(color_code) || raise(
206
+ 'ActiveRecordQueryTrace.colorize was set to an invalid ' \
207
+ "color. Use one of #{COLORS.keys} or a valid color code."
208
+ )
209
+ end
210
+
211
+ def valid_color_code?(color_code)
212
+ /\A\d+(?:;\d+)?\Z/ =~ color_code
213
+ end
94
214
 
215
+ def rails_root_prefix
216
+ @rails_root_prefix ||= "#{Rails.root}/"
95
217
  end
218
+
219
+ # This cannot be set in a constant as Rails.root is not yet available when
220
+ # this file is loaded.
221
+ def rails_root_regexp
222
+ @rails_root_regexp ||= %r{#{Regexp.escape(Rails.root.to_s)}(?!\/vendor)}
223
+ end
224
+ end
225
+ end
226
+
227
+ # The following code is used to suppress specific entries from the log. The
228
+ # "around alias" technique is used to allow `ActiveSupport::LogSubscriber#debug`
229
+ # to be overwritten while preserving the original version, which can still be
230
+ # called.
231
+ #
232
+ # I would prefer using Module#prepend here, but alias_method does not work if
233
+ # called from within a prepended module.
234
+ #
235
+ # Note that:
236
+ # - #debug is used to log queries but also other things, do not mess it up.
237
+ # - Some queries include both SELECT and a write operation such as INSERT,
238
+ # UPDATE or DELETE. That means that checking for the presence of SELECT is not
239
+ # enough to ensure it is not a write query.
240
+ #
241
+ # TODO: move to a separate file.
242
+ ActiveSupport::LogSubscriber.class_eval do
243
+ alias_method :original_debug, :debug
244
+
245
+ def debug(*args, &block)
246
+ return if ActiveRecordQueryTrace.suppress_logging_of_db_reads \
247
+ && args.first !~ /INSERT|UPDATE|DELETE|#{ActiveRecordQueryTrace::BACKTRACE_PREFIX}/
248
+ original_debug(*args, &block)
96
249
  end
97
250
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecordQueryTrace
4
+ VERSION = '1.8'
5
+ end
metadata CHANGED
@@ -1,25 +1,138 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_query_trace
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.4
4
+ version: '1.8'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Caughlan
8
+ - Bruno Facca
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2016-10-12 00:00:00.000000000 Z
12
- dependencies: []
13
- description: Print stack trace of all queries to the Rails log. Helpful to find where
14
- queries are being executed in your application.
15
- email: toolbag@gmail.com
12
+ date: 2020-10-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 4.0.0
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 4.0.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: pry
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 0.13.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 0.13.0
42
+ - !ruby/object:Gem::Dependency
43
+ name: pry-byebug
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: 3.9.0
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: 3.9.0
56
+ - !ruby/object:Gem::Dependency
57
+ name: rspec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 3.8.0
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 3.8.0
70
+ - !ruby/object:Gem::Dependency
71
+ name: rubocop
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: 0.65.0
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 0.65.0
84
+ - !ruby/object:Gem::Dependency
85
+ name: rubocop-rspec
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 1.32.0
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: 1.32.0
98
+ - !ruby/object:Gem::Dependency
99
+ name: simplecov
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: 0.16.1
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: 0.16.1
112
+ - !ruby/object:Gem::Dependency
113
+ name: sqlite3
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: 1.3.6
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: 1.3.6
126
+ description: Print stack trace of all DB queries to the Rails log. Helpful to find
127
+ where queries are being executed in your application.
128
+ email: bruno@facca.info
16
129
  executables: []
17
130
  extensions: []
18
131
  extra_rdoc_files: []
19
132
  files:
20
133
  - lib/active_record_query_trace.rb
21
- - lib/version.rb
22
- homepage: https://github.com/ruckus/active-record-query-trace
134
+ - lib/active_record_query_trace/version.rb
135
+ homepage: https://github.com/brunofacca/active-record-query-trace
23
136
  licenses:
24
137
  - MIT
25
138
  metadata: {}
@@ -29,19 +142,21 @@ require_paths:
29
142
  - lib
30
143
  required_ruby_version: !ruby/object:Gem::Requirement
31
144
  requirements:
32
- - - '>='
145
+ - - ">="
33
146
  - !ruby/object:Gem::Version
34
- version: '0'
147
+ version: '2.4'
148
+ - - "<"
149
+ - !ruby/object:Gem::Version
150
+ version: '4.0'
35
151
  required_rubygems_version: !ruby/object:Gem::Requirement
36
152
  requirements:
37
- - - '>='
153
+ - - ">="
38
154
  - !ruby/object:Gem::Version
39
155
  version: '0'
40
156
  requirements: []
41
- rubyforge_project:
42
- rubygems_version: 2.0.6
157
+ rubygems_version: 3.0.6
43
158
  signing_key:
44
159
  specification_version: 4
45
- summary: Print stack trace of all queries to the Rails log. Helpful to find where
160
+ summary: Print stack trace of all DB queries to the Rails log. Helpful to find where
46
161
  queries are being executed in your application.
47
162
  test_files: []
@@ -1,3 +0,0 @@
1
- module ActiveRecordQueryTrace
2
- VERSION = '1.5.4'
3
- end