active_record_query_trace 1.5.4 → 1.8

Sign up to get free protection for your applications and to get access to all the features.
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