skylight 5.0.1 → 5.1.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.
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