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
@@ -14,16 +14,18 @@ module Skylight
14
14
  def file_path
15
15
  return @file_path if @file_path
16
16
 
17
- config_path = @config[:user_config_path] || begin
18
- require "etc"
19
- home_dir = ENV["HOME"] || Etc.getpwuid.dir || (ENV["USER"] && File.expand_path("~#{ENV['USER']}"))
20
- if home_dir
21
- File.join(home_dir, ".skylight")
22
- else
23
- raise ConfigError,
24
- "The Skylight `user_config_path` must be defined since the home directory cannot be inferred"
25
- end
26
- end
17
+ config_path =
18
+ @config[:user_config_path] ||
19
+ begin
20
+ require "etc"
21
+ home_dir = ENV["HOME"] || Etc.getpwuid.dir || (ENV["USER"] && File.expand_path("~#{ENV["USER"]}"))
22
+ if home_dir
23
+ File.join(home_dir, ".skylight")
24
+ else
25
+ raise ConfigError,
26
+ "The Skylight `user_config_path` must be defined since the home directory cannot be inferred"
27
+ end
28
+ end
27
29
 
28
30
  @file_path = File.expand_path(config_path)
29
31
  end
@@ -46,16 +48,11 @@ module Skylight
46
48
 
47
49
  def save
48
50
  FileUtils.mkdir_p(File.dirname(file_path))
49
- File.open(file_path, "w") do |f|
50
- f.puts YAML.dump(to_hash)
51
- end
51
+ File.open(file_path, "w") { |f| f.puts YAML.dump(to_hash) }
52
52
  end
53
53
 
54
54
  def to_hash
55
- {
56
- "disable_dev_warning" => disable_dev_warning,
57
- "disable_env_warning" => disable_env_warning
58
- }
55
+ { "disable_dev_warning" => disable_dev_warning, "disable_env_warning" => disable_env_warning }
59
56
  end
60
57
  end
61
58
  end
@@ -15,6 +15,7 @@ module Skylight
15
15
  now = Time.now
16
16
  now.to_i * 1_000_000_000 + now.usec * 1_000
17
17
  end
18
+
18
19
  # rubocop:enable Lint/DuplicateMethods
19
20
 
20
21
  # TODO: rename to secs
@@ -14,7 +14,7 @@ module Skylight
14
14
 
15
15
  def initialize(environment, name, force_worker: false)
16
16
  @environment = environment || DEFAULT_ENVIRONMENT
17
- @name = resolve_name(name, force_worker)
17
+ @name = resolve_name(name, force_worker)
18
18
 
19
19
  raise ArgumentError, "environment can't be blank" if @environment.empty?
20
20
 
@@ -40,35 +40,32 @@ module Skylight
40
40
 
41
41
  # keys here should match those from the main config
42
42
  def as_json(*)
43
- {
44
- component: name,
45
- env: environment
46
- }
43
+ { component: name, env: environment }
47
44
  end
48
45
 
49
46
  private
50
47
 
51
- def program_name
52
- $PROGRAM_NAME
53
- end
48
+ def program_name
49
+ $PROGRAM_NAME
50
+ end
54
51
 
55
- def argv
56
- ARGV
57
- end
52
+ def argv
53
+ ARGV
54
+ end
58
55
 
59
- def resolve_name(given_name, force_worker)
60
- # don't allow workers to be called 'web'
61
- return WORKER_NAME if force_worker && (given_name.nil? || given_name == DEFAULT_NAME)
62
- return DEFAULT_NAME if given_name.nil?
56
+ def resolve_name(given_name, force_worker)
57
+ # don't allow workers to be called 'web'
58
+ return WORKER_NAME if force_worker && (given_name.nil? || given_name == DEFAULT_NAME)
59
+ return DEFAULT_NAME if given_name.nil?
63
60
 
64
- given_name
65
- end
61
+ given_name
62
+ end
66
63
 
67
- def validate_string!(string, kind)
68
- return true if string =~ NAME_FORMAT
64
+ def validate_string!(string, kind)
65
+ return true if string =~ NAME_FORMAT
69
66
 
70
- raise ArgumentError, "#{kind} can only contain lowercase letters, numbers, and dashes"
71
- end
67
+ raise ArgumentError, "#{kind} can only contain lowercase letters, numbers, and dashes"
68
+ end
72
69
  end
73
70
  end
74
71
  end
@@ -48,9 +48,7 @@ module Skylight
48
48
 
49
49
  def initialize(*)
50
50
  super
51
- if description && !id
52
- warn "The configured deploy will be ignored as an id or git_sha must be provided."
53
- end
51
+ warn "The configured deploy will be ignored as an id or git_sha must be provided." if description && !id
54
52
  end
55
53
 
56
54
  def id
@@ -86,13 +84,13 @@ module Skylight
86
84
 
87
85
  private
88
86
 
89
- def get_info
90
- info_path = config[:'heroku.dyno_info_path']
87
+ def get_info
88
+ info_path = config[:'heroku.dyno_info_path']
91
89
 
92
- if File.exist?(info_path) && (info = JSON.parse(File.read(info_path)))
93
- info["release"]
94
- end
90
+ if File.exist?(info_path) && (info = JSON.parse(File.read(info_path)))
91
+ info["release"]
95
92
  end
93
+ end
96
94
  end
97
95
 
98
96
  class GitDeploy < EmptyDeploy
@@ -105,12 +103,12 @@ module Skylight
105
103
 
106
104
  private
107
105
 
108
- def get_info
109
- Dir.chdir(config.root) do
110
- info = `git log -1 --pretty="%H %s" 2>&1`
111
- info.split(" ", 2).map(&:strip) if $CHILD_STATUS.success?
112
- end
106
+ def get_info
107
+ Dir.chdir(config.root) do
108
+ info = `git log -1 --pretty="%H %s" 2>&1`
109
+ info.split(" ", 2).map(&:strip) if $CHILD_STATUS.success?
113
110
  end
111
+ end
114
112
  end
115
113
 
116
114
  DEPLOY_TYPES = [DefaultDeploy, HerokuDeploy, GitDeploy].freeze
@@ -10,14 +10,14 @@ module Skylight
10
10
  module Util
11
11
  class HTTP
12
12
  CONTENT_ENCODING = "content-encoding".freeze
13
- CONTENT_LENGTH = "content-length".freeze
14
- CONTENT_TYPE = "content-type".freeze
15
- ACCEPT = "Accept".freeze
16
- X_VERSION_HDR = "x-skylight-agent-version".freeze
13
+ CONTENT_LENGTH = "content-length".freeze
14
+ CONTENT_TYPE = "content-type".freeze
15
+ ACCEPT = "Accept".freeze
16
+ X_VERSION_HDR = "x-skylight-agent-version".freeze
17
17
  APPLICATION_JSON = "application/json".freeze
18
- AUTHORIZATION = "authorization".freeze
19
- DEFLATE = "deflate".freeze
20
- GZIP = "gzip".freeze
18
+ AUTHORIZATION = "authorization".freeze
19
+ DEFLATE = "deflate".freeze
20
+ GZIP = "gzip".freeze
21
21
 
22
22
  include Logging
23
23
 
@@ -35,7 +35,8 @@ module Skylight
35
35
  end
36
36
  end
37
37
 
38
- class ReadResponseError < StandardError; end
38
+ class ReadResponseError < StandardError
39
+ end
39
40
 
40
41
  def initialize(config, service = :auth, opts = {})
41
42
  @config = config
@@ -46,7 +47,7 @@ module Skylight
46
47
 
47
48
  url = URI.parse(url)
48
49
 
49
- @ssl = url.scheme == "https"
50
+ @ssl = url.scheme == "https"
50
51
  @host = url.host
51
52
  @port = url.port
52
53
 
@@ -81,131 +82,129 @@ module Skylight
81
82
 
82
83
  private
83
84
 
84
- def get_timeout(type, config, service, opts)
85
- config.duration_ms("#{service}_http_#{type}_timeout") ||
86
- opts[:timeout] || 15
87
- end
85
+ def get_timeout(type, config, service, opts)
86
+ config.duration_ms("#{service}_http_#{type}_timeout") || opts[:timeout] || 15
87
+ end
88
88
 
89
- def build_request(type, endpoint, hdrs, length = nil)
90
- headers = {}
89
+ def build_request(type, endpoint, hdrs, length = nil)
90
+ headers = {}
91
91
 
92
- headers[CONTENT_LENGTH] = length.to_s if length
93
- headers[AUTHORIZATION] = authentication if authentication
94
- headers[ACCEPT] = APPLICATION_JSON
95
- headers[X_VERSION_HDR] = VERSION
96
- headers[CONTENT_ENCODING] = GZIP if length && @deflate
92
+ headers[CONTENT_LENGTH] = length.to_s if length
93
+ headers[AUTHORIZATION] = authentication if authentication
94
+ headers[ACCEPT] = APPLICATION_JSON
95
+ headers[X_VERSION_HDR] = VERSION
96
+ headers[CONTENT_ENCODING] = GZIP if length && @deflate
97
97
 
98
- hdrs.each do |k, v|
99
- headers[k] = v
100
- end
98
+ hdrs.each { |k, v| headers[k] = v }
101
99
 
102
- type.new(endpoint, headers)
100
+ type.new(endpoint, headers)
101
+ end
102
+
103
+ def do_request(http, req)
104
+ begin
105
+ client = http.start
106
+ rescue StandardError => e
107
+ # TODO: Retry here
108
+ raise StartError, e
103
109
  end
104
110
 
105
- def do_request(http, req)
106
- begin
107
- client = http.start
108
- rescue => e
109
- # TODO: Retry here
110
- raise StartError, e
111
- end
111
+ begin
112
+ res = client.request(req)
113
+ rescue *READ_EXCEPTIONS => e
114
+ raise ReadResponseError, e.inspect
115
+ end
112
116
 
113
- begin
114
- res = client.request(req)
115
- rescue *READ_EXCEPTIONS => e
116
- raise ReadResponseError, e.inspect
117
- end
117
+ yield res
118
+ ensure
119
+ client&.finish
120
+ end
118
121
 
119
- yield res
120
- ensure
121
- client&.finish
122
+ def execute(req, body = nil)
123
+ t do
124
+ fmt "executing HTTP request; host=%s; port=%s; path=%s, body=%s",
125
+ @host,
126
+ @port,
127
+ req.path,
128
+ body && body.bytesize
122
129
  end
123
130
 
124
- def execute(req, body = nil)
125
- t do
126
- fmt "executing HTTP request; host=%s; port=%s; path=%s, body=%s",
127
- @host, @port, req.path, body && body.bytesize
128
- end
131
+ if body
132
+ body = Gzip.compress(body) if @deflate
133
+ req.body = body
134
+ end
129
135
 
130
- if body
131
- body = Gzip.compress(body) if @deflate
132
- req.body = body
133
- end
136
+ http = Net::HTTP.new(@host, @port, @proxy_addr, @proxy_port, @proxy_user, @proxy_pass)
134
137
 
135
- http = Net::HTTP.new(@host, @port,
136
- @proxy_addr, @proxy_port, @proxy_user, @proxy_pass)
138
+ http.open_timeout = @open_timeout
139
+ http.read_timeout = @read_timeout
137
140
 
138
- http.open_timeout = @open_timeout
139
- http.read_timeout = @read_timeout
141
+ if @ssl
142
+ http.use_ssl = true
140
143
 
141
- if @ssl
142
- http.use_ssl = true
144
+ http.ca_file = SSL.ca_cert_file_or_default unless SSL.ca_cert_file?
143
145
 
144
- unless SSL.ca_cert_file?
145
- http.ca_file = SSL.ca_cert_file_or_default
146
- end
146
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
147
+ end
147
148
 
148
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
149
+ do_request(http, req) do |res|
150
+ unless res.code =~ /2\d\d/
151
+ debug "server responded with #{res.code}"
152
+ t { fmt "body=%s", res.body }
149
153
  end
150
154
 
151
- do_request(http, req) do |res|
152
- unless res.code =~ /2\d\d/
153
- debug "server responded with #{res.code}"
154
- t { fmt "body=%s", res.body }
155
- end
155
+ Response.new(res.code.to_i, res, res.body)
156
+ end
157
+ rescue Exception => e
158
+ error "http %s %s failed; error=%s; msg=%s", req.method, req.path, e.class, e.message
159
+ t { e.backtrace.join("\n") }
160
+ ErrorResponse.new(req, e)
161
+ end
156
162
 
157
- Response.new(res.code.to_i, res, res.body)
158
- end
159
- rescue Exception => e
160
- error "http %s %s failed; error=%s; msg=%s", req.method, req.path, e.class, e.message
161
- t { e.backtrace.join("\n") }
162
- ErrorResponse.new(req, e)
163
- end
164
-
165
- class Response
166
- attr_reader :status, :headers, :body, :exception
167
-
168
- def initialize(status, headers, body)
169
- @status = status
170
- @headers = headers
171
-
172
- if (headers[CONTENT_TYPE] || "").include?(APPLICATION_JSON)
173
- begin
174
- @body = JSON.parse(body)
175
- rescue JSON::ParserError
176
- @body = body # not really JSON I guess
177
- end
178
- else
179
- @body = body
163
+ class Response
164
+ attr_reader :status, :headers, :body, :exception
165
+
166
+ def initialize(status, headers, body)
167
+ @status = status
168
+ @headers = headers
169
+
170
+ if (headers[CONTENT_TYPE] || "").include?(APPLICATION_JSON)
171
+ begin
172
+ @body = JSON.parse(body)
173
+ rescue JSON::ParserError
174
+ @body = body # not really JSON I guess
180
175
  end
176
+ else
177
+ @body = body
181
178
  end
179
+ end
182
180
 
183
- def success?
184
- status >= 200 && status < 300
185
- end
181
+ def success?
182
+ status >= 200 && status < 300
183
+ end
186
184
 
187
- def to_s
188
- body.to_s
189
- end
185
+ def to_s
186
+ body.to_s
187
+ end
190
188
 
191
- def get(key)
192
- body.dig(*key.split(".")) if body.is_a?(Hash)
193
- end
189
+ def get(key)
190
+ body.dig(*key.split(".")) if body.is_a?(Hash)
191
+ end
194
192
 
195
- def respond_to_missing?(name, include_all = false)
196
- super || body.respond_to?(name, include_all)
197
- end
193
+ def respond_to_missing?(name, include_all = false)
194
+ super || body.respond_to?(name, include_all)
195
+ end
198
196
 
199
- def method_missing(name, *args, &blk)
200
- if respond_to_missing?(name)
201
- body.send(name, *args, &blk)
202
- else
203
- super
204
- end
197
+ def method_missing(name, *args, &blk)
198
+ if respond_to_missing?(name)
199
+ body.send(name, *args, &blk)
200
+ else
201
+ super
205
202
  end
206
203
  end
204
+ end
207
205
 
208
- ErrorResponse = Struct.new(:request, :exception) do
206
+ ErrorResponse =
207
+ Struct.new(:request, :exception) do
209
208
  def status
210
209
  nil
211
210
  end
@@ -14,9 +14,7 @@ module Skylight
14
14
  # Try to avoid writing to STDOUT/STDERR twice
15
15
  logger_logdev = @logger.instance_variable_get(:@logdev)
16
16
  logger_out = logger_logdev.respond_to?(:dev) ? logger_logdev.dev : nil
17
- if logger_out != $stdout && logger_out != $stderr
18
- @logger.<<(*args)
19
- end
17
+ @logger.<<(*args) if logger_out != $stdout && logger_out != $stderr
20
18
  end
21
19
 
22
20
  def close; end
@@ -85,8 +83,8 @@ module Skylight
85
83
 
86
84
  alias log_trace trace
87
85
  alias log_debug debug
88
- alias log_info info
89
- alias log_warn warn
86
+ alias log_info info
87
+ alias log_warn warn
90
88
  alias log_error error
91
89
 
92
90
  # Alias for `Kernel#sprintf`
@@ -123,7 +121,7 @@ module Skylight
123
121
  else
124
122
  Kernel.warn "Invalid logger"
125
123
  end
126
- # Fallback to stderr for warn and error levels
124
+ # Fallback to stderr for warn and error levels
127
125
  elsif %i[warn error].include?(level)
128
126
  $stderr.puts format("[SKYLIGHT] #{msg}", *args)
129
127
  end