skylight 5.0.0.beta4 → 5.1.0.beta2

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +399 -362
  3. data/CLA.md +1 -1
  4. data/CONTRIBUTING.md +1 -1
  5. data/LICENSE.md +7 -17
  6. data/README.md +1 -1
  7. data/ext/extconf.rb +42 -54
  8. data/ext/libskylight.yml +10 -5
  9. data/lib/skylight.rb +20 -30
  10. data/lib/skylight/api.rb +22 -18
  11. data/lib/skylight/cli.rb +47 -46
  12. data/lib/skylight/cli/doctor.rb +50 -50
  13. data/lib/skylight/cli/helpers.rb +19 -19
  14. data/lib/skylight/cli/merger.rb +141 -139
  15. data/lib/skylight/config.rb +267 -310
  16. data/lib/skylight/deprecation.rb +4 -4
  17. data/lib/skylight/errors.rb +3 -4
  18. data/lib/skylight/extensions.rb +17 -29
  19. data/lib/skylight/extensions/source_location.rb +128 -128
  20. data/lib/skylight/formatters/http.rb +1 -3
  21. data/lib/skylight/gc.rb +30 -40
  22. data/lib/skylight/helpers.rb +57 -30
  23. data/lib/skylight/instrumenter.rb +25 -18
  24. data/lib/skylight/middleware.rb +31 -35
  25. data/lib/skylight/native.rb +8 -10
  26. data/lib/skylight/native_ext_fetcher.rb +10 -12
  27. data/lib/skylight/normalizers.rb +43 -38
  28. data/lib/skylight/normalizers/action_controller/process_action.rb +24 -25
  29. data/lib/skylight/normalizers/action_controller/send_file.rb +7 -6
  30. data/lib/skylight/normalizers/action_dispatch/route_set.rb +7 -7
  31. data/lib/skylight/normalizers/active_job/perform.rb +48 -44
  32. data/lib/skylight/normalizers/active_model_serializers/render.rb +7 -3
  33. data/lib/skylight/normalizers/active_storage.rb +11 -13
  34. data/lib/skylight/normalizers/active_support/cache.rb +1 -12
  35. data/lib/skylight/normalizers/coach/handler_finish.rb +1 -3
  36. data/lib/skylight/normalizers/default.rb +1 -9
  37. data/lib/skylight/normalizers/faraday/request.rb +1 -3
  38. data/lib/skylight/normalizers/grape/endpoint.rb +13 -19
  39. data/lib/skylight/normalizers/grape/endpoint_run.rb +16 -18
  40. data/lib/skylight/normalizers/grape/endpoint_run_filters.rb +1 -3
  41. data/lib/skylight/normalizers/graphql/base.rb +23 -28
  42. data/lib/skylight/normalizers/render.rb +19 -21
  43. data/lib/skylight/normalizers/shrine.rb +32 -0
  44. data/lib/skylight/normalizers/sql.rb +4 -4
  45. data/lib/skylight/probes.rb +38 -46
  46. data/lib/skylight/probes/action_controller.rb +32 -28
  47. data/lib/skylight/probes/action_dispatch/request_id.rb +9 -5
  48. data/lib/skylight/probes/action_dispatch/routing/route_set.rb +7 -5
  49. data/lib/skylight/probes/action_view.rb +9 -10
  50. data/lib/skylight/probes/active_job_enqueue.rb +3 -9
  51. data/lib/skylight/probes/active_model_serializers.rb +8 -8
  52. data/lib/skylight/probes/delayed_job.rb +37 -42
  53. data/lib/skylight/probes/elasticsearch.rb +4 -6
  54. data/lib/skylight/probes/excon.rb +1 -1
  55. data/lib/skylight/probes/excon/middleware.rb +22 -23
  56. data/lib/skylight/probes/graphql.rb +2 -7
  57. data/lib/skylight/probes/middleware.rb +14 -5
  58. data/lib/skylight/probes/mongo.rb +83 -91
  59. data/lib/skylight/probes/net_http.rb +1 -1
  60. data/lib/skylight/probes/redis.rb +5 -17
  61. data/lib/skylight/probes/sequel.rb +7 -11
  62. data/lib/skylight/probes/sinatra.rb +8 -5
  63. data/lib/skylight/probes/tilt.rb +2 -4
  64. data/lib/skylight/railtie.rb +121 -135
  65. data/lib/skylight/sidekiq.rb +4 -5
  66. data/lib/skylight/subscriber.rb +31 -33
  67. data/lib/skylight/test.rb +89 -84
  68. data/lib/skylight/trace.rb +121 -115
  69. data/lib/skylight/user_config.rb +14 -17
  70. data/lib/skylight/util/clock.rb +1 -0
  71. data/lib/skylight/util/component.rb +18 -21
  72. data/lib/skylight/util/deploy.rb +11 -13
  73. data/lib/skylight/util/http.rb +104 -105
  74. data/lib/skylight/util/logging.rb +4 -6
  75. data/lib/skylight/util/lru_cache.rb +2 -6
  76. data/lib/skylight/util/platform.rb +2 -6
  77. data/lib/skylight/util/ssl.rb +1 -25
  78. data/lib/skylight/version.rb +1 -1
  79. data/lib/skylight/vm/gc.rb +1 -9
  80. metadata +20 -5
@@ -11,9 +11,7 @@ module Skylight
11
11
  # @param [String] query Request query string
12
12
  # @return [Hash] a hash containing `:category`, `:title`, and `:annotations`
13
13
  def self.build_opts(method, _scheme, host, _port, _path, _query)
14
- { category: "api.http.#{method.downcase}",
15
- title: "#{method.upcase} #{host}",
16
- internal: true }
14
+ { category: "api.http.#{method.downcase}", title: "#{method.upcase} #{host}", internal: true }
17
15
  end
18
16
  end
19
17
  end
data/lib/skylight/gc.rb CHANGED
@@ -3,10 +3,10 @@ require "skylight/util/logging"
3
3
  module Skylight
4
4
  # @api private
5
5
  class GC
6
- METHODS = %i[enable total_time].freeze
7
- TH_KEY = :SK_GC_CURR_WINDOW
6
+ METHODS = %i[enable total_time].freeze
7
+ TH_KEY = :SK_GC_CURR_WINDOW
8
8
  MAX_COUNT = 1000
9
- MAX_TIME = 30_000_000
9
+ MAX_TIME = 30_000_000
10
10
 
11
11
  include Util::Logging
12
12
 
@@ -14,9 +14,9 @@ module Skylight
14
14
 
15
15
  def initialize(config, profiler)
16
16
  @listeners = []
17
- @config = config
18
- @lock = Mutex.new
19
- @time = 0
17
+ @config = config
18
+ @lock = Mutex.new
19
+ @time = 0
20
20
 
21
21
  if METHODS.all? { |m| profiler.respond_to?(m) }
22
22
  @profiler = profiler
@@ -46,9 +46,7 @@ module Skylight
46
46
  # Cleanup any listeners that might have leaked
47
47
  @listeners.shift until @listeners[0].time < MAX_TIME
48
48
 
49
- if @listeners.length > MAX_COUNT
50
- @listeners.shift
51
- end
49
+ @listeners.shift if @listeners.length > MAX_COUNT
52
50
  end
53
51
 
54
52
  win
@@ -58,52 +56,44 @@ module Skylight
58
56
  end
59
57
 
60
58
  def release(win)
61
- @lock.synchronize do
62
- @listeners.delete(win)
63
- end
59
+ @lock.synchronize { @listeners.delete(win) }
64
60
  end
65
61
 
66
62
  def update
67
- @lock.synchronize do
68
- __update
69
- end
63
+ @lock.synchronize { __update }
70
64
 
71
65
  nil
72
66
  end
73
67
 
74
68
  private
75
69
 
76
- def __update
77
- time = @profiler.total_time
78
- diff = time - @time
79
- @time = time
70
+ def __update
71
+ time = @profiler.total_time
72
+ diff = time - @time
73
+ @time = time
80
74
 
81
- if diff > 0
82
- @listeners.each do |l|
83
- l.add(diff)
84
- end
85
- end
86
- end
75
+ @listeners.each { |l| l.add(diff) } if diff > 0
76
+ end
87
77
 
88
- class Window
89
- attr_reader :time
78
+ class Window
79
+ attr_reader :time
90
80
 
91
- def initialize(global)
92
- @global = global
93
- @time = 0
94
- end
81
+ def initialize(global)
82
+ @global = global
83
+ @time = 0
84
+ end
95
85
 
96
- def update
97
- @global&.update
98
- end
86
+ def update
87
+ @global&.update
88
+ end
99
89
 
100
- def add(time)
101
- @time += time
102
- end
90
+ def add(time)
91
+ @time += time
92
+ end
103
93
 
104
- def release
105
- @global&.release(self)
106
- end
94
+ def release
95
+ @global&.release(self)
107
96
  end
97
+ end
108
98
  end
109
99
  end
@@ -14,7 +14,7 @@ module Skylight
14
14
 
15
15
  if (opts = @__sk_instrument_next_method)
16
16
  @__sk_instrument_next_method = nil
17
- instrument_method(name, opts)
17
+ instrument_method(name, **opts)
18
18
  end
19
19
  end
20
20
 
@@ -24,7 +24,7 @@ module Skylight
24
24
 
25
25
  if (opts = @__sk_instrument_next_method)
26
26
  @__sk_instrument_next_method = nil
27
- instrument_class_method(name, opts)
27
+ instrument_class_method(name, **opts)
28
28
  end
29
29
  end
30
30
 
@@ -77,14 +77,12 @@ module Skylight
77
77
  # do_expensive_stuff
78
78
  # end
79
79
  # end
80
- def instrument_method(*args)
81
- opts = args.pop if args.last.is_a?(Hash)
82
-
80
+ def instrument_method(*args, **opts)
83
81
  if (name = args.pop)
84
82
  title = "#{self}##{name}"
85
- __sk_instrument_method_on(self, name, title, opts || {})
83
+ __sk_instrument_method_on(self, name, title, **opts)
86
84
  else
87
- @__sk_instrument_next_method = opts || {}
85
+ @__sk_instrument_next_method = opts
88
86
  end
89
87
  end
90
88
 
@@ -123,32 +121,58 @@ module Skylight
123
121
  #
124
122
  # instrument_class_method :my_method, title: 'Expensive work'
125
123
  # end
126
- def instrument_class_method(name, opts = {})
124
+ def instrument_class_method(name, **opts)
127
125
  # NOTE: If the class is defined anonymously and then assigned to a variable this code
128
126
  # will not be aware of the updated name.
129
127
  title = "#{self}.#{name}"
130
- __sk_instrument_method_on(__sk_singleton_class, name, title, opts || {})
128
+ __sk_instrument_method_on(__sk_singleton_class, name, title, **opts)
131
129
  end
132
130
 
133
131
  private
134
132
 
135
- def __sk_instrument_method_on(klass, name, title, opts)
136
- category = (opts[:category] || "app.method").to_s
137
- title = (opts[:title] || title).to_s
138
- desc = opts[:description].to_s if opts[:description]
133
+ HAS_ARGUMENT_FORWARDING = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
134
+
135
+ def __sk_instrument_method_on(klass, name, title, **opts)
136
+ category = (opts[:category] || "app.method").to_s
137
+ title = (opts[:title] || title).to_s
138
+ desc = opts[:description].to_s if opts[:description]
139
+
140
+ # NOTE: The source location logic happens before we have have a config so we can'
141
+ # check if source locations are enabled. However, it only happens once so the potential impact
142
+ # should be minimal. This would more appropriately belong to Extensions::SourceLocation,
143
+ # but as that is a runtime concern, and this happens at compile time, there isn't currently
144
+ # a clean way to turn this on and off. The absence of the extension will cause the
145
+ # source_file and source_line to be removed from the trace span before it is submitted.
146
+ source_file, source_line = klass.instance_method(name).source_location
147
+
148
+ # We should strongly prefer using the new argument-forwarding syntax (...) where available.
149
+ # In Ruby 2.7, the following are known to be syntax errors:
150
+ #
151
+ # - mixing positional arguments with argument forwarding (e.g., send(:method_name, ...))
152
+ # - calling a setter method with multiple arguments, unless dispatched via send or public_send.
153
+ #
154
+ # So it is possible, though not recommended, to define setter methods that take multiple arguments,
155
+ # keywords, and/or blocks. Unfortunately, this means that for setters, we still need to explicitly
156
+ # forward the different argument types.
157
+ is_setter_method = name.to_s.end_with?("=")
139
158
 
140
- # NOTE: The source location logic happens before we have have a config so we can'
141
- # check if source locations are enabled. However, it only happens once so the potential impact
142
- # should be minimal. This would more appropriately belong to Extensions::SourceLocation,
143
- # but as that is a runtime concern, and this happens at compile time, there isn't currently
144
- # a clean way to turn this on and off. The absence of the extension will cause the
145
- # source_file and source_line to be removed from the trace span before it is submitted.
146
- source_file, source_line = klass.instance_method(name).source_location
159
+ arg_string =
160
+ if HAS_ARGUMENT_FORWARDING
161
+ is_setter_method ? "*args, **kwargs, &blk" : "..."
162
+ else
163
+ "*args, &blk"
164
+ end
165
+
166
+ original_method_dispatch =
167
+ if is_setter_method
168
+ "self.send(:before_instrument_#{name}, #{arg_string})"
169
+ else
170
+ "before_instrument_#{name}(#{arg_string})"
171
+ end
147
172
 
148
- klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
173
+ klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
149
174
  alias_method :"before_instrument_#{name}", :"#{name}" # alias_method :"before_instrument_process", :"process"
150
- #
151
- def #{name}(*args, &blk) # def process(*args, &blk)
175
+ def #{name}(#{arg_string}) # def process(*args, **kwargs, &blk)
152
176
  span = Skylight.instrument( # span = Skylight.instrument(
153
177
  category: :"#{category}", # category: :"app.method",
154
178
  title: #{title.inspect}, # title: "process",
@@ -157,8 +181,9 @@ module Skylight
157
181
  source_line: #{source_line.inspect}) # source_line: 123)
158
182
  #
159
183
  meta = {} # meta = {}
184
+ #
160
185
  begin # begin
161
- send(:before_instrument_#{name}, *args, &blk) # send(:before_instrument_process)
186
+ #{original_method_dispatch} # self.before_instrument_process(...)
162
187
  rescue Exception => e # rescue Exception => e
163
188
  meta[:exception_object] = e # meta[:exception_object] = e
164
189
  raise e # raise e
@@ -173,15 +198,17 @@ module Skylight
173
198
  private :"#{name}" # private :"process"
174
199
  end # end
175
200
  RUBY
176
- end
201
+ end
177
202
 
178
- if respond_to?(:singleton_class)
179
- alias __sk_singleton_class singleton_class
180
- else
181
- def __sk_singleton_class
182
- class << self; self; end
203
+ if respond_to?(:singleton_class)
204
+ alias __sk_singleton_class singleton_class
205
+ else
206
+ def __sk_singleton_class
207
+ class << self
208
+ self
183
209
  end
184
210
  end
211
+ end
185
212
  end
186
213
 
187
214
  # @api private
@@ -99,16 +99,14 @@ module Skylight
99
99
  end
100
100
 
101
101
  def current_trace=(trace)
102
- t { "setting current_trace=#{trace ? trace.uuid : 'nil'}; thread=#{Thread.current.object_id}" }
102
+ t { "setting current_trace=#{trace ? trace.uuid : "nil"}; thread=#{Thread.current.object_id}" }
103
103
  @trace_info.current = trace
104
104
  end
105
105
 
106
106
  def validate_installation
107
107
  # Warn if there was an error installing Skylight.
108
108
 
109
- if defined?(Skylight.check_install_errors)
110
- Skylight.check_install_errors(config)
111
- end
109
+ Skylight.check_install_errors(config) if defined?(Skylight.check_install_errors)
112
110
 
113
111
  if !Skylight.native? && defined?(Skylight.warn_skylight_native_missing)
114
112
  Skylight.warn_skylight_native_missing(config)
@@ -143,9 +141,7 @@ module Skylight
143
141
  end
144
142
 
145
143
  def silence_warnings(context)
146
- @warnings_silenced || @mutex.synchronize do
147
- @warnings_silenced ||= {}
148
- end
144
+ @warnings_silenced || @mutex.synchronize { @warnings_silenced ||= {} }
149
145
 
150
146
  @warnings_silenced[context] = true
151
147
  end
@@ -203,8 +199,18 @@ module Skylight
203
199
  begin
204
200
  meta ||= {}
205
201
  extensions.process_trace_meta(meta)
206
- trace = Trace.new(self, endpoint, Skylight::Util::Clock.nanos, cat, title, desc,
207
- meta: meta, segment: segment, component: component)
202
+ trace =
203
+ Trace.new(
204
+ self,
205
+ endpoint,
206
+ Skylight::Util::Clock.nanos,
207
+ cat,
208
+ title,
209
+ desc,
210
+ meta: meta,
211
+ segment: segment,
212
+ component: component
213
+ )
208
214
  rescue Exception => e
209
215
  log_error e.message
210
216
  t { e.backtrace.join("\n") }
@@ -301,7 +307,7 @@ module Skylight
301
307
  finalize_endpoint_segment(trace)
302
308
  native_submit_trace(trace)
303
309
  true
304
- rescue => e
310
+ rescue StandardError => e
305
311
  handle_instrumenter_error(trace, e)
306
312
  end
307
313
  end
@@ -332,14 +338,15 @@ module Skylight
332
338
  def finalize_endpoint_segment(trace)
333
339
  return unless (segment = trace.segment)
334
340
 
335
- segment = case trace.compound_response_error_status
336
- when :all
337
- "error"
338
- when :partial
339
- "#{segment}+error"
340
- else
341
- segment
342
- end
341
+ segment =
342
+ case trace.compound_response_error_status
343
+ when :all
344
+ "error"
345
+ when :partial
346
+ "#{segment}+error"
347
+ else
348
+ segment
349
+ end
343
350
 
344
351
  trace.endpoint += "<sk-segment>#{segment}</sk-segment>"
345
352
  end
@@ -49,12 +49,16 @@ module Skylight
49
49
  def self.with_after_close(resp, debug_identifier: "unknown", &block)
50
50
  unless resp.respond_to?(:to_ary)
51
51
  if resp.respond_to?(:to_a)
52
- Skylight.warn("Rack response from \"#{debug_identifier}\" cannot be implicitly converted to an array. " \
53
- "This is in violation of the Rack SPEC and will raise an error in future versions.")
52
+ Skylight.warn(
53
+ "Rack response from \"#{debug_identifier}\" cannot be implicitly converted to an array. " \
54
+ "This is in violation of the Rack SPEC and will raise an error in future versions."
55
+ )
54
56
  resp = resp.to_a
55
57
  else
56
- Skylight.error("Rack response from \"#{debug_identifier}\" cannot be converted to an array. This is in " \
57
- "violation of the Rack SPEC and may cause problems with Skylight operation.")
58
+ Skylight.error(
59
+ "Rack response from \"#{debug_identifier}\" cannot be converted to an array. This is in " \
60
+ "violation of the Rack SPEC and may cause problems with Skylight operation."
61
+ )
58
62
  return resp
59
63
  end
60
64
  end
@@ -91,11 +95,7 @@ module Skylight
91
95
 
92
96
  resp = @app.call(env)
93
97
 
94
- if trace
95
- Middleware.with_after_close(resp, debug_identifier: "Rack App: #{@app.class}") { trace.submit }
96
- else
97
- resp
98
- end
98
+ trace ? Middleware.with_after_close(resp, debug_identifier: "Rack App: #{@app.class}") { trace.submit } : resp
99
99
  rescue Exception => e
100
100
  t { "middleware exception: #{e}\n#{e.backtrace.join("\n")}" }
101
101
  trace&.submit
@@ -106,36 +106,32 @@ module Skylight
106
106
 
107
107
  private
108
108
 
109
- def log_context
110
- # Don't cache this, it will change
111
- { request_id: @current_request_id, inst: Skylight.instrumenter&.uuid }
112
- end
109
+ def log_context
110
+ # Don't cache this, it will change
111
+ { request_id: @current_request_id, inst: Skylight.instrumenter&.uuid }
112
+ end
113
113
 
114
- # Allow for overwriting
115
- def endpoint_name(_env)
116
- "Rack"
117
- end
114
+ # Allow for overwriting
115
+ def endpoint_name(_env)
116
+ "Rack"
117
+ end
118
118
 
119
- def endpoint_meta(_env)
120
- { source_location: Trace::SYNTHETIC }
121
- end
119
+ def endpoint_meta(_env)
120
+ { source_location: Trace::SYNTHETIC }
121
+ end
122
122
 
123
- # Request ID code based on ActionDispatch::RequestId
124
- def set_request_id(env)
125
- existing_request_id = env["action_dispatch.request_id"] || env["HTTP_X_REQUEST_ID"]
126
- @current_request_id = env["skylight.request_id"] = make_request_id(existing_request_id)
127
- end
123
+ # Request ID code based on ActionDispatch::RequestId
124
+ def set_request_id(env)
125
+ existing_request_id = env["action_dispatch.request_id"] || env["HTTP_X_REQUEST_ID"]
126
+ @current_request_id = env["skylight.request_id"] = make_request_id(existing_request_id)
127
+ end
128
128
 
129
- def make_request_id(request_id)
130
- if request_id && !request_id.empty?
131
- request_id.gsub(/[^\w\-]/, "".freeze)[0...255]
132
- else
133
- internal_request_id
134
- end
135
- end
129
+ def make_request_id(request_id)
130
+ request_id && !request_id.empty? ? request_id.gsub(/[^\w\-]/, "".freeze)[0...255] : internal_request_id
131
+ end
136
132
 
137
- def internal_request_id
138
- SecureRandom.uuid
139
- end
133
+ def internal_request_id
134
+ SecureRandom.uuid
135
+ end
140
136
  end
141
137
  end
@@ -105,20 +105,18 @@ module Skylight
105
105
  install_log = File.expand_path("../../ext/install.log", __dir__)
106
106
 
107
107
  if File.exist?(install_log) && File.read(install_log) =~ /ERROR/
108
- config.alert_logger.error \
109
- "[SKYLIGHT] [#{Skylight::VERSION}] The Skylight native extension failed to install. " \
110
- "Please check #{install_log} and notify support@skylight.io. " \
111
- "The missing extension will not affect the functioning of your application."
108
+ config.alert_logger.error "[SKYLIGHT] [#{Skylight::VERSION}] The Skylight native extension failed to install. " \
109
+ "Please check #{install_log} and notify support@skylight.io. " \
110
+ "The missing extension will not affect the functioning of your application."
112
111
  end
113
112
  end
114
113
 
115
114
  # @api private
116
115
  def self.warn_skylight_native_missing(config)
117
- config.alert_logger.error \
118
- "[SKYLIGHT] [#{Skylight::VERSION}] The Skylight native extension for " \
119
- "your platform wasn't found. Supported operating systems are " \
120
- "Linux 2.6.18+ and Mac OS X 10.8+. The missing extension will not " \
121
- "affect the functioning of your application. If you are on a " \
122
- "supported platform, please contact support at support@skylight.io."
116
+ config.alert_logger.error "[SKYLIGHT] [#{Skylight::VERSION}] The Skylight native extension for " \
117
+ "your platform wasn't found. Supported operating systems are " \
118
+ "Linux 2.6.18+ and Mac OS X 10.8+. The missing extension will not " \
119
+ "affect the functioning of your application. If you are on a " \
120
+ "supported platform, please contact support at support@skylight.io."
123
121
  end
124
122
  end