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
@@ -2,18 +2,7 @@ module Skylight
2
2
  module Normalizers
3
3
  module ActiveSupport
4
4
  class Cache < Normalizer
5
- %w[
6
- clear
7
- decrement
8
- delete
9
- exist
10
- fetch_hit
11
- generate
12
- increment
13
- read
14
- read_multi
15
- write
16
- ].each do |type|
5
+ %w[clear decrement delete exist fetch_hit generate increment read read_multi write].each do |type|
17
6
  require "skylight/normalizers/active_support/cache_#{type}"
18
7
  end
19
8
  end
@@ -36,9 +36,7 @@ module Skylight
36
36
  response_status = payload.fetch(:response, {}).fetch(:status, "").to_s
37
37
  segments << "error" if response_status.start_with?("4", "5")
38
38
 
39
- if segments.any?
40
- trace.segment = segments.join("+")
41
- end
39
+ trace.segment = segments.join("+") if segments.any?
42
40
  end
43
41
  end
44
42
  end
@@ -17,15 +17,7 @@ module Skylight
17
17
  # @option payload [String] :description
18
18
  # @return [Array, :skip] the normalized array or `:skip` if `name` is not part of a known {Skylight::TIERS tier}
19
19
  def normalize(_trace, name, payload)
20
- if name =~ Skylight::TIER_REGEX
21
- [
22
- name,
23
- payload[:title],
24
- payload[:description]
25
- ]
26
- else
27
- :skip
28
- end
20
+ name =~ Skylight::TIER_REGEX ? [name, payload[:title], payload[:description]] : :skip
29
21
  end
30
22
  end
31
23
  end
@@ -23,9 +23,7 @@ module Skylight
23
23
  def normalize(_trace, _name, payload)
24
24
  uri = payload[:url]
25
25
 
26
- if disabled?
27
- return :skip
28
- end
26
+ return :skip if disabled?
29
27
 
30
28
  opts = Formatters::HTTP.build_opts(payload[:method], uri.scheme, uri.host, uri.port, uri.path, uri.query)
31
29
  description = opts[:title]
@@ -2,32 +2,26 @@ module Skylight
2
2
  module Normalizers
3
3
  module Grape
4
4
  class Endpoint < Normalizer
5
- %w[
6
- run
7
- render
8
- run_filters
9
- ].each do |type|
10
- require "skylight/normalizers/grape/endpoint_#{type}"
11
- end
5
+ %w[run render run_filters].each { |type| require "skylight/normalizers/grape/endpoint_#{type}" }
12
6
 
13
7
  require "skylight/normalizers/grape/format_response"
14
8
 
15
9
  private
16
10
 
17
- def get_method(endpoint)
18
- method = endpoint.options[:method].first
19
- method = "#{method}..." if endpoint.options[:method].length > 1
20
- method
21
- end
11
+ def get_method(endpoint)
12
+ method = endpoint.options[:method].first
13
+ method = "#{method}..." if endpoint.options[:method].length > 1
14
+ method
15
+ end
22
16
 
23
- def get_path(endpoint)
24
- endpoint.options[:path].join("/")
25
- end
17
+ def get_path(endpoint)
18
+ endpoint.options[:path].join("/")
19
+ end
26
20
 
27
- def get_namespace(endpoint)
28
- # slice off preceding slash for data continuity
29
- ::Grape::Namespace.joined_space_path(endpoint.namespace_stackable(:namespace)).to_s[1..-1]
30
- end
21
+ def get_namespace(endpoint)
22
+ # slice off preceding slash for data continuity
23
+ ::Grape::Namespace.joined_space_path(endpoint.namespace_stackable(:namespace)).to_s[1..-1]
24
+ end
31
25
  end
32
26
  end
33
27
  end
@@ -14,27 +14,25 @@ module Skylight
14
14
 
15
15
  private
16
16
 
17
- def get_endpoint_name(endpoint)
18
- method = get_method(endpoint)
19
- path = get_path(endpoint)
20
- namespace = get_namespace(endpoint)
21
-
22
- if namespace && !namespace.empty?
23
- path = "/#{path}" if path[0] != "/"
24
- path = "#{namespace}#{path}"
25
- end
26
-
27
- "#{base_app_name(endpoint)} [#{method}] #{path}".strip
17
+ def get_endpoint_name(endpoint)
18
+ method = get_method(endpoint)
19
+ path = get_path(endpoint)
20
+ namespace = get_namespace(endpoint)
21
+
22
+ if namespace && !namespace.empty?
23
+ path = "/#{path}" if path[0] != "/"
24
+ path = "#{namespace}#{path}"
28
25
  end
29
26
 
30
- def base_app_name(endpoint)
31
- ep = endpoint.options[:for]
32
- return ep.name if ep.name
27
+ "#{base_app_name(endpoint)} [#{method}] #{path}".strip
28
+ end
29
+
30
+ def base_app_name(endpoint)
31
+ ep = endpoint.options[:for]
32
+ return ep.name if ep.name
33
33
 
34
- if ep.respond_to?(:base) && ep.base.respond_to?(:name)
35
- ep.base.name
36
- end
37
- end
34
+ ep.base.name if ep.respond_to?(:base) && ep.base.respond_to?(:name)
35
+ end
38
36
  end
39
37
  end
40
38
  end
@@ -10,9 +10,7 @@ module Skylight
10
10
  filters = payload[:filters]
11
11
  type = payload[:type]
12
12
 
13
- if (!filters || filters.empty?) || !type
14
- return :skip
15
- end
13
+ return :skip if (!filters || filters.empty?) || !type
16
14
 
17
15
  [CAT, "#{type.to_s.capitalize} Filters", nil]
18
16
  end
@@ -25,12 +25,7 @@ module Skylight::Normalizers::GraphQL
25
25
 
26
26
  def self.inherited(klass)
27
27
  super
28
- klass.const_set(
29
- :KEY,
30
- ActiveSupport::Inflector.underscore(
31
- ActiveSupport::Inflector.demodulize(klass.name)
32
- ).freeze
33
- )
28
+ klass.const_set(:KEY, ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(klass.name)).freeze)
34
29
  end
35
30
 
36
31
  def self.key
@@ -43,15 +38,13 @@ module Skylight::Normalizers::GraphQL
43
38
 
44
39
  private
45
40
 
46
- def key
47
- self.class.key
48
- end
41
+ def key
42
+ self.class.key
43
+ end
49
44
 
50
- def extract_query_name(query)
51
- query&.context&.[](:skylight_endpoint) ||
52
- query&.operation_name ||
53
- ANONYMOUS
54
- end
45
+ def extract_query_name(query)
46
+ query&.context&.[](:skylight_endpoint) || query&.operation_name || ANONYMOUS
47
+ end
55
48
  end
56
49
 
57
50
  class Lex < Base
@@ -82,17 +75,21 @@ module Skylight::Normalizers::GraphQL
82
75
  # In graphql-ruby's case, the calculation of the operation name is lazy, and
83
76
  # has not been done yet at the point where execute_multiplex starts.
84
77
  # [1] https://graphql.org/learn/serving-over-http/#post-request
85
- queries, has_errors = payload[:multiplex].queries.each_with_object([Set.new, Set.new]) do |query, (names, errors)|
86
- names << extract_query_name(query)
87
- errors << query.static_errors.any?
88
- end
89
-
90
- trace.endpoint = "graphql:#{queries.sort.join('+')}"
91
- trace.compound_response_error_status = if has_errors.all?
92
- :all
93
- elsif has_errors.any?
94
- :partial
95
- end
78
+ queries, has_errors =
79
+ payload[:multiplex]
80
+ .queries
81
+ .each_with_object([Set.new, Set.new]) do |query, (names, errors)|
82
+ names << extract_query_name(query)
83
+ errors << query.static_errors.any?
84
+ end
85
+
86
+ trace.endpoint = "graphql:#{queries.sort.join("+")}"
87
+ trace.compound_response_error_status =
88
+ if has_errors.all?
89
+ :all
90
+ elsif has_errors.any?
91
+ :partial
92
+ end
96
93
  end
97
94
  end
98
95
 
@@ -106,9 +103,7 @@ module Skylight::Normalizers::GraphQL
106
103
  def normalize(trace, _name, payload)
107
104
  query_name = extract_query_name(payload[:query])
108
105
 
109
- if query_name == ANONYMOUS
110
- meta = { mute_children: true }
111
- end
106
+ meta = { mute_children: true } if query_name == ANONYMOUS
112
107
 
113
108
  # This is probably always overriden by execute_multiplex#normalize_after,
114
109
  # but in the case of a single query, it will be the same value anyway.
@@ -50,32 +50,30 @@ module Skylight
50
50
 
51
51
  private
52
52
 
53
- def relative_path?(path)
54
- !absolute_path?(path)
55
- end
53
+ def relative_path?(path)
54
+ !absolute_path?(path)
55
+ end
56
56
 
57
- SEPARATOR_BYTE = File::SEPARATOR.ord
57
+ SEPARATOR_BYTE = File::SEPARATOR.ord
58
58
 
59
- if File.const_defined?(:NULL) ? File::NULL == "NUL" : RbConfig::CONFIG["host_os"] =~ /mingw|mswin32/
60
- # This is a DOSish environment
61
- ALT_SEPARATOR_BYTE = File::ALT_SEPARATOR&.ord
62
- COLON_BYTE = ":".ord
63
- SEPARATOR_BYTES = [SEPARATOR_BYTE, ALT_SEPARATOR_BYTE].freeze
59
+ if File.const_defined?(:NULL) ? File::NULL == "NUL" : RbConfig::CONFIG["host_os"] =~ /mingw|mswin32/
60
+ # This is a DOSish environment
61
+ ALT_SEPARATOR_BYTE = File::ALT_SEPARATOR&.ord
62
+ COLON_BYTE = ":".ord
63
+ SEPARATOR_BYTES = [SEPARATOR_BYTE, ALT_SEPARATOR_BYTE].freeze
64
64
 
65
- def absolute_path?(path)
66
- if alpha?(path.getbyte(0)) && path.getbyte(1) == COLON_BYTE
67
- SEPARATOR_BYTES.include?(path.getbyte(2))
68
- end
69
- end
65
+ def absolute_path?(path)
66
+ SEPARATOR_BYTES.include?(path.getbyte(2)) if alpha?(path.getbyte(0)) && path.getbyte(1) == COLON_BYTE
67
+ end
70
68
 
71
- def alpha?(byte)
72
- (byte >= 65 && byte <= 90) || (byte >= 97 && byte <= 122)
73
- end
74
- else
75
- def absolute_path?(path)
76
- path.getbyte(0) == SEPARATOR_BYTE
77
- end
69
+ def alpha?(byte)
70
+ (byte >= 65 && byte <= 90) || (byte >= 97 && byte <= 122)
71
+ end
72
+ else
73
+ def absolute_path?(path)
74
+ path.getbyte(0) == SEPARATOR_BYTE
78
75
  end
76
+ end
79
77
  end
80
78
  end
81
79
  end
@@ -0,0 +1,32 @@
1
+ module Skylight
2
+ module Normalizers
3
+ class Shrine < Normalizer
4
+ TITLES = {
5
+ "upload.shrine" => "Upload",
6
+ "download.shrine" => "Download",
7
+ "open.shrine" => "Open",
8
+ "exists.shrine" => "Exists",
9
+ "delete.shrine" => "Delete",
10
+ "metadata.shrine" => "Metadata",
11
+ "mime_type.shrine" => "MIME Type",
12
+ "image_dimensions.shrine" => "Image Dimensions",
13
+ "signature.shrine" => "Signature",
14
+ "extension.shrine" => "Extension",
15
+ "derivation.shrine" => "Derivation",
16
+ "derivatives.shrine" => "Derivatives",
17
+ "data_uri.shrine" => "Data URI",
18
+ "remote_url.shrine" => "Remote URL"
19
+ }.freeze
20
+
21
+ TITLES.each_key { |key| register key }
22
+
23
+ def normalize(_trace, name, _payload)
24
+ title = ["Shrine", TITLES[name]].join(" ")
25
+
26
+ cat = "app.#{name.split(".").reverse.join(".")}"
27
+
28
+ [cat, title, nil]
29
+ end
30
+ end
31
+ end
32
+ end
@@ -19,7 +19,7 @@ module Skylight
19
19
  when "SCHEMA", "CACHE"
20
20
  return :skip
21
21
  else
22
- name = CAT
22
+ name = CAT
23
23
  title = payload[:name] || "SQL"
24
24
  end
25
25
 
@@ -30,9 +30,9 @@ module Skylight
30
30
  unless sql.valid_encoding?
31
31
  if config[:log_sql_parse_errors]
32
32
  config.logger.error "[#{Skylight::SqlLexError.formatted_code}] Unable to extract binds from non-UTF-8 " \
33
- "query. " \
34
- "encoding=#{payload[:sql].encoding.name} " \
35
- "sql=#{payload[:sql].inspect} "
33
+ "query. " \
34
+ "encoding=#{payload[:sql].encoding.name} " \
35
+ "sql=#{payload[:sql].inspect} "
36
36
  end
37
37
 
38
38
  sql = nil
@@ -26,31 +26,35 @@ module Skylight
26
26
 
27
27
  private
28
28
 
29
- def log_install_exception(err)
30
- description = err.class.to_s
31
- description << ": #{err.message}" unless err.message.empty?
32
-
33
- backtrace = err.backtrace.map { |l| " #{l}" }.join("\n")
34
-
35
- gems =
36
- begin
37
- Bundler.locked_gems.dependencies.map { |d| [d.name, d.requirement.to_s] }
38
- rescue # rubocop:disable Lint/SuppressedException
39
- end
40
-
41
- error = "[SKYLIGHT] [#{Skylight::VERSION}] Encountered an error while installing the " \
42
- "probe for #{const_name}. Please notify support@skylight.io with the debugging " \
43
- "information below. It's recommended that you disable this probe until the " \
44
- "issue is resolved." \
45
- "\n\nERROR: #{description}\n\n#{backtrace}\n\n"
46
-
47
- if gems
48
- gems_string = gems.map { |g| " #{g[0]} #{g[1]}" }.join("\n")
49
- error << "GEMS:\n\n#{gems_string}\n\n"
29
+ def log_install_exception(err)
30
+ description = err.class.to_s
31
+ description << ": #{err.message}" unless err.message.empty?
32
+
33
+ backtrace = err.backtrace.map { |l| " #{l}" }.join("\n")
34
+
35
+ # rubocop:disable Lint/SuppressedException
36
+ gems =
37
+ begin
38
+ Bundler.locked_gems.dependencies.map { |d| [d.name, d.requirement.to_s] }
39
+ rescue StandardError
50
40
  end
51
41
 
52
- $stderr.puts(error)
42
+ # rubocop:enable Lint/SuppressedException
43
+
44
+ error =
45
+ "[SKYLIGHT] [#{Skylight::VERSION}] Encountered an error while installing the " \
46
+ "probe for #{const_name}. Please notify support@skylight.io with the debugging " \
47
+ "information below. It's recommended that you disable this probe until the " \
48
+ "issue is resolved." \
49
+ "\n\nERROR: #{description}\n\n#{backtrace}\n\n"
50
+
51
+ if gems
52
+ gems_string = gems.map { |g| " #{g[0]} #{g[1]}" }.join("\n")
53
+ error << "GEMS:\n\n#{gems_string}\n\n"
53
54
  end
55
+
56
+ $stderr.puts(error)
57
+ end
54
58
  end
55
59
 
56
60
  class << self
@@ -62,11 +66,7 @@ module Skylight
62
66
  pending = registered.values - installed.values
63
67
 
64
68
  pending.each do |registration|
65
- if registration.constant_available?
66
- install_probe(registration)
67
- else
68
- register_require_hook(registration)
69
- end
69
+ registration.constant_available? ? install_probe(registration) : register_require_hook(registration)
70
70
  end
71
71
  end
72
72
 
@@ -79,14 +79,14 @@ module Skylight
79
79
 
80
80
  def add_path(path)
81
81
  root = Pathname.new(path)
82
- Pathname.glob(root.join("./**/*.rb")).each do |f|
83
- name = f.relative_path_from(root).sub_ext("").to_s
84
- if available.key?(name)
85
- raise "duplicate probe name: #{name}; original=#{available[name]}; new=#{f}"
86
- end
82
+ Pathname
83
+ .glob(root.join("./**/*.rb"))
84
+ .each do |f|
85
+ name = f.relative_path_from(root).sub_ext("").to_s
86
+ raise "duplicate probe name: #{name}; original=#{available[name]}; new=#{f}" if available.key?(name)
87
87
 
88
- available[name] = f
89
- end
88
+ available[name] = f
89
+ end
90
90
  end
91
91
 
92
92
  def available
@@ -95,13 +95,9 @@ module Skylight
95
95
 
96
96
  def probe(*probes)
97
97
  unknown = probes.map(&:to_s) - available.keys
98
- unless unknown.empty?
99
- raise ArgumentError, "unknown probes: #{unknown.join(', ')}"
100
- end
98
+ raise ArgumentError, "unknown probes: #{unknown.join(", ")}" unless unknown.empty?
101
99
 
102
- probes.each do |p|
103
- require available[p.to_s]
104
- end
100
+ probes.each { |p| require available[p.to_s] }
105
101
  end
106
102
 
107
103
  def registered
@@ -117,9 +113,7 @@ module Skylight
117
113
  end
118
114
 
119
115
  def register(name, *args)
120
- if registered.key?(name)
121
- raise "already registered: #{name}"
122
- end
116
+ raise "already registered: #{name}" if registered.key?(name)
123
117
 
124
118
  registered[name] = ProbeRegistration.new(name, *args)
125
119
 
@@ -156,9 +150,7 @@ module Skylight
156
150
  return unless require_hooks.key?(require_path)
157
151
 
158
152
  # dup because we may be mutating the array
159
- require_hooks[require_path].dup.each do |registration|
160
- yield registration
161
- end
153
+ require_hooks[require_path].dup.each { |registration| yield registration }
162
154
  end
163
155
  end
164
156