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
@@ -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