skylight 5.0.1 → 5.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +395 -364
  3. data/CLA.md +1 -1
  4. data/LICENSE.md +7 -17
  5. data/README.md +1 -1
  6. data/ext/extconf.rb +42 -54
  7. data/ext/libskylight.yml +9 -6
  8. data/lib/skylight.rb +20 -30
  9. data/lib/skylight/api.rb +22 -18
  10. data/lib/skylight/cli.rb +47 -46
  11. data/lib/skylight/cli/doctor.rb +50 -50
  12. data/lib/skylight/cli/helpers.rb +19 -19
  13. data/lib/skylight/cli/merger.rb +141 -139
  14. data/lib/skylight/config.rb +265 -300
  15. data/lib/skylight/deprecation.rb +4 -4
  16. data/lib/skylight/errors.rb +3 -4
  17. data/lib/skylight/extensions.rb +17 -29
  18. data/lib/skylight/extensions/source_location.rb +128 -128
  19. data/lib/skylight/formatters/http.rb +1 -3
  20. data/lib/skylight/gc.rb +30 -40
  21. data/lib/skylight/helpers.rb +43 -41
  22. data/lib/skylight/instrumenter.rb +25 -18
  23. data/lib/skylight/middleware.rb +31 -35
  24. data/lib/skylight/native.rb +8 -10
  25. data/lib/skylight/native_ext_fetcher.rb +10 -12
  26. data/lib/skylight/normalizers.rb +43 -39
  27. data/lib/skylight/normalizers/action_controller/process_action.rb +24 -25
  28. data/lib/skylight/normalizers/action_controller/send_file.rb +7 -6
  29. data/lib/skylight/normalizers/action_dispatch/route_set.rb +7 -7
  30. data/lib/skylight/normalizers/active_job/perform.rb +48 -44
  31. data/lib/skylight/normalizers/active_model_serializers/render.rb +7 -3
  32. data/lib/skylight/normalizers/active_storage.rb +11 -13
  33. data/lib/skylight/normalizers/active_support/cache.rb +1 -12
  34. data/lib/skylight/normalizers/coach/handler_finish.rb +1 -3
  35. data/lib/skylight/normalizers/default.rb +1 -9
  36. data/lib/skylight/normalizers/faraday/request.rb +1 -3
  37. data/lib/skylight/normalizers/grape/endpoint.rb +13 -19
  38. data/lib/skylight/normalizers/grape/endpoint_run.rb +16 -18
  39. data/lib/skylight/normalizers/grape/endpoint_run_filters.rb +1 -3
  40. data/lib/skylight/normalizers/graphql/base.rb +23 -28
  41. data/lib/skylight/normalizers/render.rb +19 -21
  42. data/lib/skylight/normalizers/shrine.rb +15 -17
  43. data/lib/skylight/normalizers/sql.rb +4 -4
  44. data/lib/skylight/probes.rb +38 -46
  45. data/lib/skylight/probes/action_controller.rb +32 -28
  46. data/lib/skylight/probes/action_dispatch/request_id.rb +9 -5
  47. data/lib/skylight/probes/action_dispatch/routing/route_set.rb +7 -5
  48. data/lib/skylight/probes/action_view.rb +9 -10
  49. data/lib/skylight/probes/active_job_enqueue.rb +3 -9
  50. data/lib/skylight/probes/active_model_serializers.rb +8 -8
  51. data/lib/skylight/probes/delayed_job.rb +37 -42
  52. data/lib/skylight/probes/elasticsearch.rb +3 -5
  53. data/lib/skylight/probes/excon.rb +1 -1
  54. data/lib/skylight/probes/excon/middleware.rb +22 -23
  55. data/lib/skylight/probes/graphql.rb +2 -7
  56. data/lib/skylight/probes/middleware.rb +14 -5
  57. data/lib/skylight/probes/mongo.rb +83 -91
  58. data/lib/skylight/probes/net_http.rb +1 -1
  59. data/lib/skylight/probes/redis.rb +5 -17
  60. data/lib/skylight/probes/sequel.rb +7 -11
  61. data/lib/skylight/probes/sinatra.rb +8 -5
  62. data/lib/skylight/probes/tilt.rb +2 -4
  63. data/lib/skylight/railtie.rb +121 -135
  64. data/lib/skylight/sidekiq.rb +4 -5
  65. data/lib/skylight/subscriber.rb +31 -33
  66. data/lib/skylight/test.rb +89 -84
  67. data/lib/skylight/trace.rb +121 -115
  68. data/lib/skylight/user_config.rb +14 -17
  69. data/lib/skylight/util/clock.rb +1 -0
  70. data/lib/skylight/util/component.rb +18 -21
  71. data/lib/skylight/util/deploy.rb +11 -13
  72. data/lib/skylight/util/http.rb +104 -105
  73. data/lib/skylight/util/logging.rb +4 -6
  74. data/lib/skylight/util/lru_cache.rb +2 -6
  75. data/lib/skylight/util/platform.rb +2 -6
  76. data/lib/skylight/util/ssl.rb +1 -25
  77. data/lib/skylight/version.rb +1 -1
  78. data/lib/skylight/vm/gc.rb +1 -9
  79. metadata +6 -6
@@ -3,14 +3,14 @@
3
3
  require "active_support/deprecation"
4
4
 
5
5
  module Skylight
6
- SKYLIGHT_GEM_ROOT = "#{File.expand_path('../..', __dir__)}/"
6
+ SKYLIGHT_GEM_ROOT = "#{File.expand_path("../..", __dir__)}/"
7
7
 
8
8
  class Deprecation < ActiveSupport::Deprecation
9
9
  private
10
10
 
11
- def ignored_callstack(path)
12
- path.start_with?(SKYLIGHT_GEM_ROOT)
13
- end
11
+ def ignored_callstack(path)
12
+ path.start_with?(SKYLIGHT_GEM_ROOT)
13
+ end
14
14
  end
15
15
 
16
16
  DEPRECATOR = Deprecation.new("6.0", "skylight")
@@ -2,15 +2,14 @@ require "json"
2
2
 
3
3
  module Skylight
4
4
  # @api private
5
- class ConfigError < RuntimeError; end
5
+ class ConfigError < RuntimeError
6
+ end
6
7
 
7
8
  class NativeError < StandardError
8
9
  @classes = {}
9
10
 
10
11
  def self.register(code, name, message)
11
- if @classes.key?(code)
12
- raise "Duplicate error class code: #{code}; name=#{name}"
13
- end
12
+ raise "Duplicate error class code: #{code}; name=#{name}" if @classes.key?(code)
14
13
 
15
14
  Skylight.module_eval <<-RUBY, __FILE__, __LINE__ + 1
16
15
  class #{name}Error < NativeError # class SqlLexError < NativeError
@@ -33,29 +33,21 @@ module Skylight
33
33
  end
34
34
 
35
35
  def process_trace_meta(meta)
36
- extensions.each do |ext|
37
- ext.process_trace_meta(meta)
38
- end
36
+ extensions.each { |ext| ext.process_trace_meta(meta) }
39
37
  end
40
38
 
41
39
  # meta is a mutable hash that will be passed to the instrumenter.
42
40
  # This method bridges Skylight.instrument and instrumenter.instrument.
43
41
  def process_instrument_options(opts, meta)
44
- extensions.each do |ext|
45
- ext.process_instrument_options(opts, meta)
46
- end
42
+ extensions.each { |ext| ext.process_instrument_options(opts, meta) }
47
43
  end
48
44
 
49
45
  def process_normalizer_meta(payload, meta, **opts)
50
- extensions.each do |ext|
51
- ext.process_normalizer_meta(payload, meta, **opts)
52
- end
46
+ extensions.each { |ext| ext.process_normalizer_meta(payload, meta, **opts) }
53
47
  end
54
48
 
55
49
  def trace_preprocess_meta(meta)
56
- extensions.each do |ext|
57
- ext.trace_preprocess_meta(meta)
58
- end
50
+ extensions.each { |ext| ext.trace_preprocess_meta(meta) }
59
51
  end
60
52
 
61
53
  def allowed_meta_keys
@@ -64,24 +56,20 @@ module Skylight
64
56
 
65
57
  private
66
58
 
67
- attr_reader :extensions, :config
68
-
69
- def find_by_name(ext_name)
70
- begin
71
- Skylight::Extensions.const_get(
72
- ActiveSupport::Inflector.classify(ext_name)
73
- )
74
- rescue NameError
75
- return nil
76
- end.tap do |const|
77
- yield const if block_given?
78
- end
79
- end
59
+ attr_reader :extensions, :config
80
60
 
81
- def rememoize!
82
- @allowed_meta_keys = nil
83
- allowed_meta_keys
84
- end
61
+ def find_by_name(ext_name)
62
+ begin
63
+ Skylight::Extensions.const_get(ActiveSupport::Inflector.classify(ext_name))
64
+ rescue NameError
65
+ return nil
66
+ end.tap { |const| yield const if block_given? }
67
+ end
68
+
69
+ def rememoize!
70
+ @allowed_meta_keys = nil
71
+ allowed_meta_keys
72
+ end
85
73
  end
86
74
 
87
75
  class Extension
@@ -35,8 +35,8 @@ module Skylight
35
35
  source_location = opts[:source_location] || opts[:meta]&.[](:source_location)
36
36
  source_file = opts[:source_file] || opts[:meta]&.[](:source_file)
37
37
  source_line = opts[:source_line] || opts[:meta]&.[](:source_line)
38
- source_name_hint, const_name, method_name = opts[:source_location_hint] ||
39
- opts[:meta]&.[](:source_location_hint)
38
+ source_name_hint, const_name, method_name =
39
+ opts[:source_location_hint] || opts[:meta]&.[](:source_location_hint)
40
40
  instrument_location = opts[:sk_instrument_location]
41
41
 
42
42
  if source_location
@@ -65,16 +65,12 @@ module Skylight
65
65
  def process_normalizer_meta(payload, meta, **opts)
66
66
  if opts[:source_location] && (opts[:source_file] || opts[:source_line])
67
67
  warn "Found both source_location and source_file or source_line in normalizer\n" \
68
- " location=#{opts[:source_location]}; file=#{opts[:source_file]}; line=#{opts[:source_line]}"
68
+ " location=#{opts[:source_location]}; file=#{opts[:source_file]}; line=#{opts[:source_line]}"
69
69
  end
70
70
 
71
71
  sl =
72
72
  if (source_name, constant_name, method_name = opts[:source_location_hint])
73
- dispatch_hinted_source_location(
74
- source_name,
75
- constant_name,
76
- method_name
77
- )
73
+ dispatch_hinted_source_location(source_name, constant_name, method_name)
78
74
  elsif opts[:source_file]
79
75
  [opts[:source_file], opts[:source_line]]
80
76
  end
@@ -82,7 +78,7 @@ module Skylight
82
78
  sl ||= source_location(payload, meta, cache_key: opts[:cache_key])
83
79
 
84
80
  if sl
85
- debug("normalizer source_location=#{sl}")
81
+ trace("normalizer source_location=#{sl}")
86
82
  meta[:source_file], meta[:source_line] = sl
87
83
  end
88
84
 
@@ -96,7 +92,7 @@ module Skylight
96
92
  if meta[:source_location]
97
93
  if source_file || source_line
98
94
  warn "Found both source_location and source_file or source_line, using source_location\n" \
99
- " location=#{meta[:source_location]}; file=#{source_file}; line=#{source_line}"
95
+ " location=#{meta[:source_location]}; file=#{source_file}; line=#{source_line}"
100
96
  end
101
97
 
102
98
  unless meta[:source_location].is_a?(String)
@@ -109,9 +105,7 @@ module Skylight
109
105
  warn "Ignoring source_line without source_file; source_line=#{source_line}"
110
106
  end
111
107
 
112
- if meta[:source_location]
113
- debug("source_location=#{meta[:source_location]}")
114
- end
108
+ trace("source_location=#{meta[:source_location]}") if meta[:source_location]
115
109
  end
116
110
 
117
111
  def allowed_meta_keys
@@ -120,97 +114,101 @@ module Skylight
120
114
 
121
115
  protected
122
116
 
123
- def dispatch_hinted_source_location(source_name, const_name, method_name)
124
- return unless const_name && method_name
117
+ def dispatch_hinted_source_location(source_name, const_name, method_name)
118
+ return unless const_name && method_name
125
119
 
126
- instance_method_source_location(const_name, method_name, source_name: source_name)
127
- end
120
+ instance_method_source_location(const_name, method_name, source_name: source_name)
121
+ end
128
122
 
129
- # from normalizers.rb
130
- # Returns an array of file and line
131
- def source_location(payload, meta, cache_key: nil)
132
- # FIXME: what should precedence be?
133
- if meta.is_a?(Hash) && meta[:source_location]
134
- meta.delete(:source_location)
135
- elsif payload.is_a?(Hash) && payload[:sk_source_location]
136
- payload[:sk_source_location]
137
- elsif (location = find_caller(cache_key: cache_key))
138
- [location.absolute_path, location.lineno]
139
- end
123
+ # from normalizers.rb
124
+ # Returns an array of file and line
125
+ def source_location(payload, meta, cache_key: nil)
126
+ # FIXME: what should precedence be?
127
+ if meta.is_a?(Hash) && meta[:source_location]
128
+ meta.delete(:source_location)
129
+ elsif payload.is_a?(Hash) && payload[:sk_source_location]
130
+ payload[:sk_source_location]
131
+ elsif (location = find_caller(cache_key: cache_key))
132
+ [location.absolute_path, location.lineno]
140
133
  end
134
+ end
141
135
 
142
- def find_caller(cache_key: nil)
143
- # starting at 4 to skip Skylight extension processing logic
144
- locations = ::Kernel.caller_locations(4..MAX_CALLER_DEPTH)
136
+ def find_caller(cache_key: nil)
137
+ # starting at 4 to skip Skylight extension processing logic
138
+ locations = ::Kernel.caller_locations(4..MAX_CALLER_DEPTH)
145
139
 
146
- if cache_key
147
- localized_cache_key = [cache_key, locations.map(&:lineno)].hash
148
- @caller_cache.fetch(localized_cache_key) { find_caller_inner(locations) }
149
- else
150
- find_caller_inner(locations)
151
- end
140
+ if cache_key
141
+ localized_cache_key = [cache_key, locations.map(&:lineno)].hash
142
+ @caller_cache.fetch(localized_cache_key) { find_caller_inner(locations) }
143
+ else
144
+ find_caller_inner(locations)
152
145
  end
146
+ end
153
147
 
154
- def project_path?(path)
155
- return false unless path
148
+ def project_path?(path)
149
+ return false unless path
156
150
 
157
- # Must be in the project root
158
- return false unless path.start_with?(config.root.to_s)
159
- # Must not be Bundler's vendor location
160
- return false if defined?(Bundler) && path.start_with?(Bundler.bundle_path.to_s)
161
- # Must not be Ruby files
162
- return false if path.include?("/ruby-#{RUBY_VERSION}/lib/ruby/")
151
+ # Must be in the project root
152
+ return false unless path.start_with?(config.root.to_s)
163
153
 
164
- # So it must be a project file
165
- true
166
- end
154
+ # Must not be Bundler's vendor location
155
+ return false if defined?(Bundler) && path.start_with?(Bundler.bundle_path.to_s)
167
156
 
168
- def instance_method_source_location(constant_name, method_name, source_name: :instance_method)
169
- @instance_method_source_location_cache.fetch([constant_name, method_name, source_name]) do
170
- if (constant = ::ActiveSupport::Dependencies.safe_constantize(constant_name))
171
- if constant.instance_methods.include?(:"before_instrument_#{method_name}")
172
- method_name = :"before_instrument_#{method_name}"
173
- end
174
- begin
175
- unbound_method = case source_name
176
- when :instance_method
177
- find_instance_method(constant, method_name)
178
- when :own_instance_method
179
- find_own_instance_method(constant, method_name)
180
- when :instance_method_super
181
- find_instance_method_super(constant, method_name)
182
- when :class_method
183
- find_class_method(constant, method_name)
184
- end
185
-
186
- unbound_method&.source_location
187
- rescue NameError
188
- nil
189
- end
157
+ # Must not be Ruby files
158
+ return false if path.include?("/ruby-#{RUBY_VERSION}/lib/ruby/")
159
+
160
+ # So it must be a project file
161
+ true
162
+ end
163
+
164
+ def instance_method_source_location(constant_name, method_name, source_name: :instance_method)
165
+ @instance_method_source_location_cache.fetch([constant_name, method_name, source_name]) do
166
+ if (constant = ::ActiveSupport::Dependencies.safe_constantize(constant_name))
167
+ if constant.instance_methods.include?(:"before_instrument_#{method_name}")
168
+ method_name = :"before_instrument_#{method_name}"
190
169
  end
191
- end
192
- end
170
+ begin
171
+ unbound_method =
172
+ case source_name
173
+ when :instance_method
174
+ find_instance_method(constant, method_name)
175
+ when :own_instance_method
176
+ find_own_instance_method(constant, method_name)
177
+ when :instance_method_super
178
+ find_instance_method_super(constant, method_name)
179
+ when :class_method
180
+ find_class_method(constant, method_name)
181
+ end
193
182
 
194
- def sanitize_source_location(path, line)
195
- # Do this first since gems may be vendored in the app repo. However, it might be slower.
196
- # Should we cache matches?
197
- if (gem_name = find_source_gem(path))
198
- path = gem_name
199
- line = nil
200
- elsif project_path?(path)
201
- # Get relative path to root
202
- path = Pathname.new(path).relative_path_from(config.root).to_s
203
- else
204
- return
183
+ unbound_method&.source_location
184
+ rescue NameError
185
+ nil
186
+ end
205
187
  end
188
+ end
189
+ end
206
190
 
207
- line ? "#{path}:#{line}" : path
191
+ def sanitize_source_location(path, line)
192
+ # Do this first since gems may be vendored in the app repo. However, it might be slower.
193
+ # Should we cache matches?
194
+ if (gem_name = find_source_gem(path))
195
+ path = gem_name
196
+ line = nil
197
+ elsif project_path?(path)
198
+ # Get relative path to root
199
+ path = Pathname.new(path).relative_path_from(config.root).to_s
200
+ else
201
+ return
208
202
  end
209
203
 
204
+ line ? "#{path}:#{line}" : path
205
+ end
206
+
210
207
  private
211
208
 
212
- def gem_require_trie
213
- @gem_require_trie ||= begin
209
+ def gem_require_trie
210
+ @gem_require_trie ||=
211
+ begin
214
212
  trie = {}
215
213
 
216
214
  Gem.loaded_specs.each do |name, spec|
@@ -219,10 +217,12 @@ module Skylight
219
217
  spec.full_require_paths.each do |path|
220
218
  t1 = trie
221
219
 
222
- path.split(File::SEPARATOR).each do |segment|
223
- t1[segment] ||= {}
224
- t1 = t1[segment]
225
- end
220
+ path
221
+ .split(File::SEPARATOR)
222
+ .each do |segment|
223
+ t1[segment] ||= {}
224
+ t1 = t1[segment]
225
+ end
226
226
 
227
227
  t1[:name] = name
228
228
  end
@@ -230,62 +230,62 @@ module Skylight
230
230
 
231
231
  trie
232
232
  end
233
- end
233
+ end
234
234
 
235
- def find_source_gem(path)
236
- return nil unless path
235
+ def find_source_gem(path)
236
+ return nil unless path
237
237
 
238
- trie = gem_require_trie
238
+ trie = gem_require_trie
239
239
 
240
- path.split(File::SEPARATOR).each do |segment|
240
+ path
241
+ .split(File::SEPARATOR)
242
+ .each do |segment|
241
243
  trie = trie[segment]
242
244
  break unless trie
243
245
  return trie[:name] if trie[:name]
244
246
  end
245
247
 
246
- nil
247
- end
248
+ nil
249
+ end
248
250
 
249
- def find_caller_inner(locations)
250
- # Start at file before this one
251
- # NOTE: We could start farther back now to avoid more Skylight files
252
- locations.find do |l|
253
- absolute_path = l.absolute_path
254
- find_source_gem(absolute_path) || project_path?(absolute_path)
255
- end
251
+ def find_caller_inner(locations)
252
+ # Start at file before this one
253
+ # NOTE: We could start farther back now to avoid more Skylight files
254
+ locations.find do |l|
255
+ absolute_path = l.absolute_path
256
+ find_source_gem(absolute_path) || project_path?(absolute_path)
256
257
  end
258
+ end
257
259
 
258
- # walks up the inheritance tree until it finds the last method
259
- # without a super_method definition.
260
- def find_instance_method_super(constant, method_name)
261
- return unless (unbound_method = find_instance_method(constant, method_name))
260
+ # walks up the inheritance tree until it finds the last method
261
+ # without a super_method definition.
262
+ def find_instance_method_super(constant, method_name)
263
+ return unless (unbound_method = find_instance_method(constant, method_name))
262
264
 
263
- while unbound_method.super_method
264
- unbound_method = unbound_method.super_method
265
- end
265
+ unbound_method = unbound_method.super_method while unbound_method.super_method
266
266
 
267
- unbound_method
268
- end
267
+ unbound_method
268
+ end
269
269
 
270
- # walks up the inheritance tree until it finds the instance method
271
- # belonging to the constant given (skip prepended modules)
272
- def find_own_instance_method(constant, method_name)
273
- return unless (unbound_method = find_instance_method(constant, method_name))
270
+ # walks up the inheritance tree until it finds the instance method
271
+ # belonging to the constant given (skip prepended modules)
272
+ def find_own_instance_method(constant, method_name)
273
+ return unless (unbound_method = find_instance_method(constant, method_name))
274
274
 
275
- while unbound_method.owner != constant && unbound_method.super_method
276
- unbound_method = unbound_method.super_method
277
- end
278
-
279
- unbound_method if unbound_method.owner == constant
275
+ while unbound_method.owner != constant && unbound_method.super_method
276
+ unbound_method = unbound_method.super_method
280
277
  end
281
278
 
282
- def find_instance_method(constant, method_name)
283
- constant.instance_method(method_name)
284
- end
279
+ unbound_method if unbound_method.owner == constant
280
+ end
285
281
 
286
- def find_class_method(constant, method_name)
287
- constant.method(method_name)
288
- end
282
+ def find_instance_method(constant, method_name)
283
+ constant.instance_method(method_name)
284
+ end
285
+
286
+ def find_class_method(constant, method_name)
287
+ constant.method(method_name)
288
+ end
289
289
  end
290
290
  end
291
291
  end