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