skylight 5.0.0.beta4 → 5.1.0.beta2

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