active_record_query_trace 1.5.4 → 1.6
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 +5 -5
- data/lib/active_record_query_trace.rb +179 -72
- data/lib/active_record_query_trace/version.rb +5 -0
- metadata +125 -12
- data/lib/version.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: dafae1b675da678759ad1ba18101f418fb97193f178a57dcb98442e0760f2e1a
|
4
|
+
data.tar.gz: 9748c99d235fef46ef85f432c0419191e05baccd9c28be61f7e090c9cac356ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b40a57407d833bdd54c45a0db89592dbcd5ead14e4b17cc78d3e9f6c33614163f67a57c75969c2c59396fb544eb96e390943265fe7bd0fd9fc57494cce0d985
|
7
|
+
data.tar.gz: e6a1d216f71480d97d0d254c03de448838ece938af770cf80602ee1cae92f1c5988379d0826551f5dda1eed9a80f3a8b33455e97f63022179665cd61c21e9c6b
|
@@ -1,7 +1,29 @@
|
|
1
|
-
#
|
2
|
-
|
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,174 @@ 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
|
12
36
|
end
|
13
37
|
|
14
|
-
|
15
|
-
|
38
|
+
class CustomLogSubscriber < ActiveRecord::LogSubscriber
|
39
|
+
def initialize
|
40
|
+
super
|
41
|
+
ActiveRecordQueryTrace.enabled = false
|
42
|
+
ActiveRecordQueryTrace.level = :app
|
43
|
+
ActiveRecordQueryTrace.lines = 5
|
44
|
+
ActiveRecordQueryTrace.ignore_cached_queries = false
|
45
|
+
ActiveRecordQueryTrace.colorize = false
|
46
|
+
ActiveRecordQueryTrace.query_type = :all
|
47
|
+
ActiveRecordQueryTrace.suppress_logging_of_db_reads = false
|
48
|
+
end
|
16
49
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
ActiveRecordQueryTrace.level = :app
|
21
|
-
ActiveRecordQueryTrace.lines = 5
|
22
|
-
ActiveRecordQueryTrace.ignore_cached_queries = false
|
23
|
-
ActiveRecordQueryTrace.colorize = false
|
50
|
+
def sql(event)
|
51
|
+
payload = event.payload
|
52
|
+
return unless display_backtrace?(payload)
|
24
53
|
|
25
|
-
|
26
|
-
# Rails by default silences all backtraces that match Rails::BacktraceCleaner::APP_DIRS_PATTERN
|
27
|
-
Rails.backtrace_cleaner.remove_silencers!
|
28
|
-
end
|
29
|
-
end
|
54
|
+
setup_backtrace_cleaner
|
30
55
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
56
|
+
trace = fully_formatted_trace # Memoize
|
57
|
+
debug(trace) unless trace.blank?
|
58
|
+
end
|
59
|
+
|
60
|
+
attach_to :active_record
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
65
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
66
|
+
# TODO: refactor and remove rubocop:disable comments.
|
67
|
+
def display_backtrace?(payload)
|
68
|
+
ActiveRecordQueryTrace.enabled \
|
69
|
+
&& !transaction_begin_or_commit_query?(payload) \
|
70
|
+
&& !schema_query?(payload) \
|
71
|
+
&& !(ActiveRecordQueryTrace.ignore_cached_queries && payload[:cached]) \
|
72
|
+
&& !(ActiveRecordQueryTrace.suppress_logging_of_db_reads && db_read_query?(payload)) \
|
73
|
+
&& display_backtrace_for_query_type?(payload)
|
74
|
+
end
|
75
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
76
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
77
|
+
|
78
|
+
def display_backtrace_for_query_type?(payload)
|
79
|
+
invalid_type_msg = 'Invalid ActiveRecordQueryTrace.query_type value ' \
|
80
|
+
"#{ActiveRecordQueryTrace.level}. Should be :all, :read, or :write."
|
81
|
+
|
82
|
+
case ActiveRecordQueryTrace.query_type
|
83
|
+
when :all then true
|
84
|
+
when :read then db_read_query?(payload)
|
85
|
+
when :write then !db_read_query?(payload)
|
86
|
+
else raise(invalid_type_msg)
|
48
87
|
end
|
88
|
+
end
|
49
89
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
90
|
+
def db_read_query?(payload)
|
91
|
+
!payload[:sql].match(/(INSERT|UPDATE|DELETE)/)
|
92
|
+
end
|
93
|
+
|
94
|
+
def fully_formatted_trace
|
95
|
+
cleaned_trace = clean_trace(original_trace)
|
96
|
+
return if cleaned_trace.blank?
|
97
|
+
stringified_trace = BACKTRACE_PREFIX + lines_to_display(cleaned_trace).join("\n" + INDENTATION)
|
98
|
+
colorize_text(stringified_trace)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Must be called after the backtrace cleaner.
|
102
|
+
def lines_to_display(full_trace)
|
103
|
+
ActiveRecordQueryTrace.lines.zero? ? full_trace : full_trace.first(ActiveRecordQueryTrace.lines)
|
104
|
+
end
|
105
|
+
|
106
|
+
def transaction_begin_or_commit_query?(payload)
|
107
|
+
payload[:sql].match(/\A(begin transaction|commit transaction|BEGIN|COMMIT)\Z/)
|
108
|
+
end
|
109
|
+
|
110
|
+
def schema_query?(payload)
|
111
|
+
payload[:name] == 'SCHEMA'
|
112
|
+
end
|
113
|
+
|
114
|
+
def clean_trace(full_trace)
|
115
|
+
invalid_level_msg = 'Invalid ActiveRecordQueryTrace.level value ' \
|
116
|
+
"#{ActiveRecordQueryTrace.level}. Should be :full, :rails, or :app."
|
117
|
+
raise(invalid_level_msg) unless %i[full app rails].include?(ActiveRecordQueryTrace.level)
|
118
|
+
|
119
|
+
trace = ActiveRecordQueryTrace.level == :full ? full_trace : Rails.backtrace_cleaner.clean(full_trace)
|
120
|
+
# We cant use a Rails::BacktraceCleaner filter to display only the relative
|
121
|
+
# path of application trace lines because it breaks the silencer that selects
|
122
|
+
# the lines to display or hide based on whether they include `Rails.root`.
|
123
|
+
trace.map { |line| line.gsub("#{Rails.root}/", '') }
|
124
|
+
end
|
125
|
+
|
126
|
+
# Rails by default silences all backtraces that *do not* match
|
127
|
+
# Rails::BacktraceCleaner::APP_DIRS_PATTERN. In other words, the default
|
128
|
+
# silencer filters out all framework backtrace lines, leaving only the
|
129
|
+
# application lines.
|
130
|
+
def setup_backtrace_cleaner
|
131
|
+
setup_backtrace_cleaner_path
|
132
|
+
return unless ActiveRecordQueryTrace.level == :rails
|
133
|
+
Rails.backtrace_cleaner.remove_filters!
|
134
|
+
Rails.backtrace_cleaner.remove_silencers!
|
135
|
+
Rails.backtrace_cleaner.add_silencer do |line|
|
136
|
+
line.match(%r{#{Regexp.escape(Rails.root.to_s)}(?!/vendor)})
|
69
137
|
end
|
138
|
+
end
|
70
139
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
140
|
+
# Rails relies on backtrace cleaner to set the application root directory
|
141
|
+
# filter. The problem is that the backtrace cleaner is initialized before
|
142
|
+
# this gem. This ensures that the value of `root` used by the filter
|
143
|
+
# is correct.
|
144
|
+
def setup_backtrace_cleaner_path
|
145
|
+
return unless Rails.backtrace_cleaner.instance_variable_get(:@root) == '/'
|
146
|
+
Rails.backtrace_cleaner.instance_variable_set :@root, Rails.root.to_s
|
147
|
+
end
|
148
|
+
|
149
|
+
# Allow query to be colorized in the terminal
|
150
|
+
def colorize_text(text)
|
151
|
+
return text unless ActiveRecordQueryTrace.colorize
|
152
|
+
"\e[#{color_code}m#{text}\e[0m"
|
153
|
+
end
|
154
|
+
|
155
|
+
# Wrapper used for testing purposes.
|
156
|
+
def original_trace
|
157
|
+
caller
|
158
|
+
end
|
78
159
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
when
|
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"
|
160
|
+
def color_code
|
161
|
+
# Backward compatibility for string color names with space as word separator.
|
162
|
+
color_code =
|
163
|
+
case ActiveRecordQueryTrace.colorize
|
164
|
+
when Symbol then COLORS[ActiveRecordQueryTrace.colorize]
|
165
|
+
when String then COLORS[ActiveRecordQueryTrace.colorize.tr("\s", '_').to_sym]
|
90
166
|
end
|
91
|
-
end
|
92
167
|
|
93
|
-
|
168
|
+
error_msg = 'ActiveRecordQueryTrace.colorize was set to an invalid ' \
|
169
|
+
"color. Use one of #{COLORS.keys} or a valid color code."
|
170
|
+
|
171
|
+
raise error_msg unless valid_color_code?(color_code)
|
172
|
+
color_code
|
173
|
+
end
|
94
174
|
|
175
|
+
def valid_color_code?(color_code)
|
176
|
+
/\A\d+(;\d+)?\Z/.match(color_code)
|
95
177
|
end
|
96
178
|
end
|
97
179
|
end
|
180
|
+
|
181
|
+
# The following code is used to suppress specific entries from the log. The
|
182
|
+
# "around alias" technique is used to allow `ActiveSupport::LogSubscriber#debug`
|
183
|
+
# to be overwritten while preserving the original version, which can still be
|
184
|
+
# called.
|
185
|
+
#
|
186
|
+
# I would prefer using Module#prepend here, but alias_method does not work if
|
187
|
+
# called from within a prepended module.
|
188
|
+
#
|
189
|
+
# Note that:
|
190
|
+
# - #debug is used to log queries but also other things, do not mess it up.
|
191
|
+
# - Some queries include both SELECT and a write operation such as INSERT,
|
192
|
+
# UPDATE or DELETE. That means that checking for the presence of SELECT is not
|
193
|
+
# enough to ensure it is not a write query.
|
194
|
+
#
|
195
|
+
# TODO: move to a separate file.
|
196
|
+
ActiveSupport::LogSubscriber.class_eval do
|
197
|
+
alias_method :original_debug, :debug
|
198
|
+
|
199
|
+
def debug(*args, &block)
|
200
|
+
return if ActiveRecordQueryTrace.suppress_logging_of_db_reads \
|
201
|
+
&& args.first !~ /(INSERT|UPDATE|DELETE|#{ActiveRecordQueryTrace::BACKTRACE_PREFIX})/
|
202
|
+
original_debug(*args, &block)
|
203
|
+
end
|
204
|
+
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.
|
4
|
+
version: '1.6'
|
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:
|
12
|
-
dependencies:
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
date: 2019-03-05 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: '0'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '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'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '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: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '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: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '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'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '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: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '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'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
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/
|
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,19 @@ 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
147
|
version: '0'
|
35
148
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
149
|
requirements:
|
37
|
-
- -
|
150
|
+
- - ">="
|
38
151
|
- !ruby/object:Gem::Version
|
39
152
|
version: '0'
|
40
153
|
requirements: []
|
41
154
|
rubyforge_project:
|
42
|
-
rubygems_version: 2.
|
155
|
+
rubygems_version: 2.7.8
|
43
156
|
signing_key:
|
44
157
|
specification_version: 4
|
45
|
-
summary: Print stack trace of all queries to the Rails log. Helpful to find where
|
158
|
+
summary: Print stack trace of all DB queries to the Rails log. Helpful to find where
|
46
159
|
queries are being executed in your application.
|
47
160
|
test_files: []
|
data/lib/version.rb
DELETED