skylight 3.1.4 → 5.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +465 -294
  3. data/CLA.md +1 -1
  4. data/CONTRIBUTING.md +11 -3
  5. data/ERRORS.md +3 -0
  6. data/LICENSE.md +8 -18
  7. data/README.md +1 -2
  8. data/bin/skylight +1 -1
  9. data/ext/extconf.rb +118 -122
  10. data/ext/libskylight.yml +8 -6
  11. data/ext/skylight_native.c +56 -100
  12. data/lib/skylight/api.rb +41 -27
  13. data/lib/skylight/cli/doctor.rb +68 -70
  14. data/lib/skylight/cli/helpers.rb +3 -5
  15. data/lib/skylight/cli/merger.rb +99 -92
  16. data/lib/skylight/cli.rb +40 -43
  17. data/lib/skylight/config.rb +656 -201
  18. data/lib/skylight/data/cacert.pem +730 -1023
  19. data/lib/skylight/deprecation.rb +17 -0
  20. data/lib/skylight/errors.rb +34 -16
  21. data/lib/skylight/extensions/source_location.rb +291 -0
  22. data/lib/skylight/extensions.rb +95 -0
  23. data/lib/skylight/formatters/http.rb +18 -0
  24. data/lib/skylight/gc.rb +99 -0
  25. data/lib/skylight/helpers.rb +82 -39
  26. data/lib/skylight/instrumenter.rb +339 -9
  27. data/lib/skylight/middleware.rb +147 -1
  28. data/lib/skylight/native.rb +71 -23
  29. data/lib/skylight/native_ext_fetcher.rb +39 -47
  30. data/lib/skylight/normalizers/action_controller/process_action.rb +68 -0
  31. data/lib/skylight/normalizers/action_controller/send_file.rb +51 -0
  32. data/lib/skylight/normalizers/action_dispatch/process_middleware.rb +22 -0
  33. data/lib/skylight/normalizers/action_dispatch/route_set.rb +27 -0
  34. data/lib/skylight/normalizers/action_view/render_collection.rb +24 -0
  35. data/lib/skylight/normalizers/action_view/render_layout.rb +25 -0
  36. data/lib/skylight/normalizers/action_view/render_partial.rb +23 -0
  37. data/lib/skylight/normalizers/action_view/render_template.rb +23 -0
  38. data/lib/skylight/normalizers/active_job/perform.rb +87 -0
  39. data/lib/skylight/normalizers/active_model_serializers/render.rb +32 -0
  40. data/lib/skylight/normalizers/active_record/instantiation.rb +16 -0
  41. data/lib/skylight/normalizers/active_record/sql.rb +20 -0
  42. data/lib/skylight/normalizers/active_storage.rb +28 -0
  43. data/lib/skylight/normalizers/active_support/cache.rb +11 -0
  44. data/lib/skylight/normalizers/active_support/cache_clear.rb +16 -0
  45. data/lib/skylight/normalizers/active_support/cache_decrement.rb +16 -0
  46. data/lib/skylight/normalizers/active_support/cache_delete.rb +16 -0
  47. data/lib/skylight/normalizers/active_support/cache_exist.rb +16 -0
  48. data/lib/skylight/normalizers/active_support/cache_fetch_hit.rb +16 -0
  49. data/lib/skylight/normalizers/active_support/cache_generate.rb +16 -0
  50. data/lib/skylight/normalizers/active_support/cache_increment.rb +16 -0
  51. data/lib/skylight/normalizers/active_support/cache_read.rb +16 -0
  52. data/lib/skylight/normalizers/active_support/cache_read_multi.rb +16 -0
  53. data/lib/skylight/normalizers/active_support/cache_write.rb +16 -0
  54. data/lib/skylight/normalizers/coach/handler_finish.rb +44 -0
  55. data/lib/skylight/normalizers/coach/middleware_finish.rb +33 -0
  56. data/lib/skylight/normalizers/couch_potato/query.rb +20 -0
  57. data/lib/skylight/normalizers/data_mapper/sql.rb +12 -0
  58. data/lib/skylight/normalizers/default.rb +24 -0
  59. data/lib/skylight/normalizers/elasticsearch/request.rb +20 -0
  60. data/lib/skylight/normalizers/faraday/request.rb +38 -0
  61. data/lib/skylight/normalizers/grape/endpoint.rb +28 -0
  62. data/lib/skylight/normalizers/grape/endpoint_render.rb +25 -0
  63. data/lib/skylight/normalizers/grape/endpoint_run.rb +39 -0
  64. data/lib/skylight/normalizers/grape/endpoint_run_filters.rb +20 -0
  65. data/lib/skylight/normalizers/grape/format_response.rb +20 -0
  66. data/lib/skylight/normalizers/graphiti/render.rb +22 -0
  67. data/lib/skylight/normalizers/graphiti/resolve.rb +31 -0
  68. data/lib/skylight/normalizers/graphql/base.rb +127 -0
  69. data/lib/skylight/normalizers/render.rb +79 -0
  70. data/lib/skylight/normalizers/sequel/sql.rb +12 -0
  71. data/lib/skylight/normalizers/shrine.rb +32 -0
  72. data/lib/skylight/normalizers/sql.rb +41 -0
  73. data/lib/skylight/normalizers.rb +157 -0
  74. data/lib/skylight/probes/action_controller.rb +52 -0
  75. data/lib/skylight/probes/action_dispatch/request_id.rb +33 -0
  76. data/lib/skylight/probes/action_dispatch/routing/route_set.rb +30 -0
  77. data/lib/skylight/probes/action_dispatch.rb +2 -0
  78. data/lib/skylight/probes/action_view.rb +42 -0
  79. data/lib/skylight/probes/active_job.rb +27 -0
  80. data/lib/skylight/probes/active_job_enqueue.rb +35 -0
  81. data/lib/skylight/probes/active_model_serializers.rb +50 -0
  82. data/lib/skylight/probes/active_record_async.rb +96 -0
  83. data/lib/skylight/probes/delayed_job.rb +144 -0
  84. data/lib/skylight/probes/elasticsearch.rb +45 -0
  85. data/lib/skylight/probes/excon/middleware.rb +65 -0
  86. data/lib/skylight/probes/excon.rb +25 -0
  87. data/lib/skylight/probes/faraday.rb +23 -0
  88. data/lib/skylight/probes/graphql.rb +38 -0
  89. data/lib/skylight/probes/httpclient.rb +44 -0
  90. data/lib/skylight/probes/middleware.rb +135 -0
  91. data/lib/skylight/probes/mongo.rb +169 -0
  92. data/lib/skylight/probes/mongoid.rb +6 -0
  93. data/lib/skylight/probes/net_http.rb +54 -0
  94. data/lib/skylight/probes/rack_builder.rb +37 -0
  95. data/lib/skylight/probes/redis.rb +68 -0
  96. data/lib/skylight/probes/sequel.rb +29 -0
  97. data/lib/skylight/probes/sinatra.rb +66 -0
  98. data/lib/skylight/probes/sinatra_add_middleware.rb +10 -10
  99. data/lib/skylight/probes/tilt.rb +25 -0
  100. data/lib/skylight/probes.rb +172 -0
  101. data/lib/skylight/railtie.rb +172 -15
  102. data/lib/skylight/sidekiq.rb +47 -0
  103. data/lib/skylight/sinatra.rb +2 -2
  104. data/lib/skylight/subscriber.rb +130 -0
  105. data/lib/skylight/test.rb +147 -0
  106. data/lib/skylight/trace.rb +331 -15
  107. data/lib/skylight/user_config.rb +60 -0
  108. data/lib/skylight/util/allocation_free.rb +26 -0
  109. data/lib/skylight/util/clock.rb +57 -0
  110. data/lib/skylight/util/component.rb +47 -9
  111. data/lib/skylight/util/deploy.rb +24 -40
  112. data/lib/skylight/util/gzip.rb +20 -0
  113. data/lib/skylight/util/hostname.rb +4 -4
  114. data/lib/skylight/util/http.rb +62 -71
  115. data/lib/skylight/util/instrumenter_method.rb +26 -0
  116. data/lib/skylight/util/logging.rb +136 -0
  117. data/lib/skylight/util/lru_cache.rb +36 -0
  118. data/lib/skylight/util/platform.rb +74 -0
  119. data/lib/skylight/util/proxy.rb +13 -0
  120. data/lib/skylight/util/ssl.rb +4 -28
  121. data/lib/skylight/util.rb +12 -0
  122. data/lib/skylight/vendor/cli/thor/rake_compat.rb +1 -1
  123. data/lib/skylight/version.rb +5 -1
  124. data/lib/skylight/vm/gc.rb +60 -0
  125. data/lib/skylight.rb +213 -24
  126. metadata +171 -53
@@ -0,0 +1,20 @@
1
+ module Skylight
2
+ module Normalizers
3
+ module Grape
4
+ class EndpointRunFilters < Endpoint
5
+ register "endpoint_run_filters.grape"
6
+
7
+ CAT = "app.grape.filters".freeze
8
+
9
+ def normalize(_trace, _name, payload)
10
+ filters = payload[:filters]
11
+ type = payload[:type]
12
+
13
+ return :skip if (!filters || filters.empty?) || !type
14
+
15
+ [CAT, "#{type.to_s.capitalize} Filters", nil]
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module Skylight
2
+ module Normalizers
3
+ module Grape
4
+ class FormatResponse < Normalizer
5
+ register "format_response.grape"
6
+
7
+ CAT = "view.grape.format_response".freeze
8
+
9
+ def normalize(_trace, _name, payload)
10
+ if (formatter = payload[:formatter])
11
+ title = formatter.is_a?(Module) ? formatter.to_s : formatter.class.to_s
12
+ [CAT, title, nil]
13
+ else
14
+ :skip
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skylight
4
+ module Normalizers
5
+ module Graphiti
6
+ class Render < Normalizer
7
+ register "render.graphiti"
8
+
9
+ CAT = "view.render.graphiti"
10
+ ANONYMOUS = "<Anonymous Resource>"
11
+
12
+ def normalize(_trace, _name, payload)
13
+ resource_class = payload[:proxy]&.resource&.class
14
+ title = "Render #{resource_class&.name || ANONYMOUS}"
15
+ desc = nil
16
+
17
+ [CAT, title, desc]
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skylight
4
+ module Normalizers
5
+ module Graphiti
6
+ class Resolve < Normalizer
7
+ register "resolve.graphiti"
8
+
9
+ CAT = "app.resolve.graphiti"
10
+
11
+ ANONYMOUS_RESOURCE = "<Anonymous Resource>"
12
+ ANONYMOUS_ADAPTER = "<Anonymous Adapter>"
13
+
14
+ def normalize(_trace, _name, payload)
15
+ resource = payload[:resource]
16
+
17
+ if (sideload = payload[:sideload])
18
+ type = sideload.type.to_s.split("_").map(&:capitalize).join(" ")
19
+ desc = "Custom Scope" if sideload.class.scope_proc
20
+ else
21
+ type = "Primary"
22
+ end
23
+
24
+ title = "Resolve #{type} #{resource.class.name || ANONYMOUS_RESOURCE}"
25
+
26
+ [CAT, title, desc]
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/inflector"
4
+
5
+ module Skylight::Normalizers::GraphQL
6
+ # Some AS::N events in GraphQL are not super useful.
7
+ # We are purposefully ignoring the following keys (and you probably shouldn't add them):
8
+ # - "graphql.analyze_multiplex"
9
+ # - "graphql.execute_field" (very frequently called)
10
+ # - "graphql.execute_field_lazy"
11
+
12
+ class Base < Skylight::Normalizers::Normalizer
13
+ ANONYMOUS = "[anonymous]"
14
+ CAT = "app.graphql"
15
+
16
+ if defined?(::GraphQL::VERSION) && Gem::Version.new(::GraphQL::VERSION) >= Gem::Version.new("1.10")
17
+ def self.register_graphql
18
+ register("#{key}.graphql")
19
+ end
20
+ else
21
+ def self.register_graphql
22
+ register("graphql.#{key}")
23
+ end
24
+ end
25
+
26
+ def self.inherited(klass)
27
+ super
28
+ klass.const_set(:KEY, ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(klass.name)).freeze)
29
+ end
30
+
31
+ def self.key
32
+ self::KEY
33
+ end
34
+
35
+ def normalize(_trace, _name, _payload)
36
+ [CAT, "graphql.#{key}", nil]
37
+ end
38
+
39
+ private
40
+
41
+ def key
42
+ self.class.key
43
+ end
44
+
45
+ def extract_query_name(query)
46
+ query&.context&.[](:skylight_endpoint) || query&.operation_name || ANONYMOUS
47
+ end
48
+ end
49
+
50
+ class Lex < Base
51
+ register_graphql
52
+ end
53
+
54
+ class Parse < Base
55
+ register_graphql
56
+ end
57
+
58
+ class Validate < Base
59
+ register_graphql
60
+ end
61
+
62
+ class ExecuteMultiplex < Base
63
+ register_graphql
64
+
65
+ def normalize_after(trace, _span, _name, payload)
66
+ # This is in normalize_after because the queries may not have
67
+ # an assigned operation name before they are executed.
68
+ # For example, if you send a single query with a defined operation name, e.g.:
69
+ # ```graphql
70
+ # query MyNamedQuery { user(id: 1) { name } }
71
+ # ```
72
+ # ... but do _not_ send the operationName request param, the GraphQL docs[1]
73
+ # specify that the executor should use the operation name from the definition.
74
+ #
75
+ # In graphql-ruby's case, the calculation of the operation name is lazy, and
76
+ # has not been done yet at the point where execute_multiplex starts.
77
+ # [1] https://graphql.org/learn/serving-over-http/#post-request
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
93
+ end
94
+ end
95
+
96
+ class AnalyzeQuery < Base
97
+ register_graphql
98
+ end
99
+
100
+ class ExecuteQuery < Base
101
+ register_graphql
102
+
103
+ def normalize(trace, _name, payload)
104
+ query_name = extract_query_name(payload[:query])
105
+
106
+ meta = { mute_children: true } if query_name == ANONYMOUS
107
+
108
+ # This is probably always overriden by execute_multiplex#normalize_after,
109
+ # but in the case of a single query, it will be the same value anyway.
110
+ trace.endpoint = "graphql:#{query_name}"
111
+
112
+ [CAT, "graphql.#{key}: #{query_name}", nil, meta]
113
+ end
114
+ end
115
+
116
+ class ExecuteQueryLazy < ExecuteQuery
117
+ register_graphql
118
+
119
+ def normalize(trace, _name, payload)
120
+ if payload[:query]
121
+ super
122
+ elsif payload[:multiplex]
123
+ [CAT, "graphql.#{key}.multiplex", nil]
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,79 @@
1
+ module Skylight
2
+ module Normalizers
3
+ # Base Normalizer for Rails rendering
4
+ class RenderNormalizer < Normalizer
5
+ include Skylight::Util::AllocationFree
6
+
7
+ def setup
8
+ @paths = []
9
+
10
+ Gem.path.each do |path|
11
+ @paths << "#{path}/bundler/gems".freeze
12
+ @paths << "#{path}/gems".freeze
13
+ @paths << path
14
+ end
15
+
16
+ @paths.concat(Array(config["normalizers.render.view_paths"]))
17
+ end
18
+
19
+ # Generic normalizer for renders
20
+ # @param category [String]
21
+ # @param payload [Hash]
22
+ # @option payload [String] :identifier
23
+ # @return [Array]
24
+ def normalize_render(category, payload)
25
+ if (path = payload[:identifier])
26
+ title = relative_path(path)
27
+ end
28
+
29
+ [category, title, nil]
30
+ end
31
+
32
+ def relative_path(path)
33
+ return path if relative_path?(path)
34
+
35
+ if (root = array_find(@paths) { |p| path.start_with?(p) })
36
+ start = root.size
37
+ start += 1 if path.getbyte(start) == SEPARATOR_BYTE
38
+
39
+ path[start, path.size].sub(
40
+ # Matches a Gem Version or 12-digit hex (sha)
41
+ # that is preceeded by a `-` and followed by `/`
42
+ # Also matches 'app/views/' if it exists
43
+ %r{-(?:#{Gem::Version::VERSION_PATTERN}|[0-9a-f]{12})/(?:app/views/)*},
44
+ ": ".freeze
45
+ )
46
+ else
47
+ "Absolute Path".freeze
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def relative_path?(path)
54
+ !absolute_path?(path)
55
+ end
56
+
57
+ SEPARATOR_BYTE = File::SEPARATOR.ord
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
64
+
65
+ def absolute_path?(path)
66
+ SEPARATOR_BYTES.include?(path.getbyte(2)) if alpha?(path.getbyte(0)) && path.getbyte(1) == COLON_BYTE
67
+ end
68
+
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
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,12 @@
1
+ require "skylight/normalizers/sql"
2
+
3
+ module Skylight
4
+ module Normalizers
5
+ module Sequel
6
+ # Normalizer for SQL requests
7
+ class SQL < Normalizers::SQL
8
+ register "sql.sequel"
9
+ end
10
+ end
11
+ end
12
+ 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
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Skylight
6
+ module Normalizers
7
+ # Normalizer for SQL requests
8
+ class SQL < Normalizer
9
+ CAT = "db.sql.query"
10
+
11
+ # @param trace [Skylight::Messages::Trace::Builder] ignored, only present to match API
12
+ # @param name [String] ignored, only present to match API
13
+ # @param payload [Hash]
14
+ # @option payload [String] [:name] The SQL operation
15
+ # @option payload [Hash] [:binds] The bound parameters
16
+ # @return [Array]
17
+ def normalize(_trace, _name, payload)
18
+ return :skip if payload[:name] == "SCHEMA" || payload[:name] == "CACHE"
19
+
20
+ title = payload[:name] || "SQL"
21
+
22
+ # We can only handle UTF-8 encoded strings.
23
+ # (Construction method here avoids extra allocations)
24
+ sql = String.new.concat("<sk-sql>", payload[:sql], "</sk-sql>").force_encoding(Encoding::UTF_8)
25
+
26
+ unless sql.valid_encoding?
27
+ if config[:log_sql_parse_errors]
28
+ config.logger.error "[#{Skylight::SqlLexError.formatted_code}] Unable to extract binds from non-UTF-8 " \
29
+ "query. " \
30
+ "encoding=#{payload[:sql].encoding.name} " \
31
+ "sql=#{payload[:sql].inspect} "
32
+ end
33
+
34
+ sql = nil
35
+ end
36
+
37
+ [CAT, title, sql]
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,157 @@
1
+ module Skylight
2
+ # @api private
3
+ # Convert AS::N events to Skylight events
4
+ module Normalizers
5
+ def self.registry
6
+ @registry ||= {}
7
+ end
8
+
9
+ def self.register(name, klass, opts = {})
10
+ enabled = opts[:enabled] != false
11
+ registry[name] = [klass, enabled]
12
+ end
13
+
14
+ def self.unregister(name)
15
+ @registry.delete(name)
16
+ end
17
+
18
+ def self.enable(*names, enabled: true)
19
+ names.each do |name|
20
+ matches = registry.select { |n, _| n =~ /(^|\.)#{name}$/ }
21
+ raise ArgumentError, "no normalizers match #{name}" if matches.empty?
22
+
23
+ matches.each_value { |v| v[1] = enabled }
24
+ end
25
+ end
26
+
27
+ def self.disable(*names)
28
+ enable(*names, enabled: false)
29
+ end
30
+
31
+ def self.build(config)
32
+ normalizers = {}
33
+
34
+ registry.each do |key, (klass, enabled)|
35
+ next unless enabled
36
+
37
+ unless klass.method_defined?(:normalize)
38
+ # TODO: Warn
39
+ next
40
+ end
41
+
42
+ normalizers[key] = klass.new(config)
43
+ end
44
+
45
+ Container.new(normalizers)
46
+ end
47
+
48
+ class Normalizer
49
+ def self.register(name, opts = {})
50
+ Normalizers.register(name, self, opts)
51
+ end
52
+
53
+ attr_reader :config
54
+
55
+ include Util::Logging
56
+
57
+ def initialize(config)
58
+ @config = config
59
+ setup if respond_to?(:setup)
60
+ end
61
+
62
+ def normalize(_trace, _name, _payload)
63
+ :skip
64
+ end
65
+
66
+ def normalize_with_meta(trace, name, payload)
67
+ # If we have a normal response but no meta, add it
68
+ cat, title, desc, meta = ret = normalize(trace, name, payload)
69
+ return cat if cat == :skip
70
+
71
+ meta ||= {}
72
+ cache_key = ret.hash
73
+ process_meta(trace, name, payload, meta, cache_key: cache_key)
74
+
75
+ [cat, title, desc, meta]
76
+ end
77
+
78
+ def normalize_after(trace, span, name, payload); end
79
+
80
+ private
81
+
82
+ def process_meta(trace, _name, payload, meta, cache_key: nil)
83
+ trace.instrumenter.extensions.process_normalizer_meta(
84
+ payload,
85
+ meta,
86
+ cache_key: cache_key,
87
+ **process_meta_options(payload)
88
+ )
89
+ end
90
+
91
+ def process_meta_options(_payload)
92
+ {}
93
+ end
94
+ end
95
+
96
+ require "skylight/normalizers/default"
97
+ DEFAULT = Default.new
98
+
99
+ class Container
100
+ def initialize(normalizers)
101
+ @normalizers = normalizers
102
+ end
103
+
104
+ def keys
105
+ @normalizers.keys
106
+ end
107
+
108
+ def each_key(&block)
109
+ @normalizers.each_key(&block)
110
+ end
111
+
112
+ def normalize(trace, name, payload)
113
+ normalizer_for(name).normalize_with_meta(trace, name, payload)
114
+ end
115
+
116
+ def normalize_after(trace, span, name, payload)
117
+ normalizer_for(name).normalize_after(trace, span, name, payload)
118
+ end
119
+
120
+ def normalizer_for(name)
121
+ # We never expect to hit the default case since we only register listeners
122
+ # for items that we know have normalizers. For now, though, we'll play it
123
+ # safe and provide a fallback.
124
+ @normalizers.fetch(name, DEFAULT)
125
+ end
126
+ end
127
+
128
+ %w[
129
+ action_controller/process_action
130
+ action_controller/send_file
131
+ action_dispatch/process_middleware
132
+ action_dispatch/route_set
133
+ action_view/render_collection
134
+ action_view/render_partial
135
+ action_view/render_template
136
+ action_view/render_layout
137
+ active_job/perform
138
+ active_model_serializers/render
139
+ active_record/instantiation
140
+ active_record/sql
141
+ active_storage
142
+ active_support/cache
143
+ coach/handler_finish
144
+ coach/middleware_finish
145
+ couch_potato/query
146
+ data_mapper/sql
147
+ elasticsearch/request
148
+ faraday/request
149
+ grape/endpoint
150
+ graphiti/resolve
151
+ graphiti/render
152
+ graphql/base
153
+ sequel/sql
154
+ shrine
155
+ ].each { |file| require "skylight/normalizers/#{file}" }
156
+ end
157
+ end
@@ -0,0 +1,52 @@
1
+ module Skylight
2
+ module Probes
3
+ module ActionController
4
+ class Probe
5
+ def install
6
+ # Prepending doesn't work here since this a module that's already been included
7
+ ::ActionController::Instrumentation.class_eval do
8
+ private
9
+
10
+ alias_method :append_info_to_payload_without_sk, :append_info_to_payload
11
+ def append_info_to_payload(payload)
12
+ append_info_to_payload_without_sk(payload)
13
+
14
+ payload[:sk_rendered_format] = sk_rendered_mime.try(:ref)
15
+ payload[:sk_variant] = request.respond_to?(:variant) ? request.variant : nil
16
+ end
17
+
18
+ def sk_rendered_mime
19
+ if respond_to?(:media_type)
20
+ mt = media_type
21
+ return mt && Mime::Type.lookup(mt)
22
+ end
23
+
24
+ if content_type.is_a?(Mime::Type)
25
+ content_type
26
+ elsif content_type.respond_to?(:to_s)
27
+ type_str = content_type.to_s.split(";").first
28
+ Mime::Type.lookup(type_str) unless type_str.blank?
29
+ elsif respond_to?(:rendered_format) && rendered_format
30
+ rendered_format
31
+ end
32
+ rescue StandardError
33
+ # There are cases in which actionpack can return
34
+ # a stringified representation of a Mime::NullType instance,
35
+ # which is invalid for a number of reasons. This string raises
36
+ # errors when piped through Mime::Type.lookup, so it's probably
37
+ # best to just return nil in those cases.
38
+ nil
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ register(
46
+ :action_controller,
47
+ "ActionController::Instrumentation",
48
+ "action_controller/metal/instrumentation",
49
+ ActionController::Probe.new
50
+ )
51
+ end
52
+ end
@@ -0,0 +1,33 @@
1
+ module Skylight
2
+ module Probes
3
+ module ActionDispatch
4
+ module RequestId
5
+ module Instrumentation
6
+ def call(env)
7
+ @skylight_request_id = env["skylight.request_id"]
8
+ super
9
+ end
10
+
11
+ private
12
+
13
+ def internal_request_id
14
+ @skylight_request_id || super
15
+ end
16
+ end
17
+
18
+ class Probe
19
+ def install
20
+ ::ActionDispatch::RequestId.prepend(Instrumentation)
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ register(
27
+ :action_dispatch,
28
+ "ActionDispatch::RequestId",
29
+ "action_dispatch/middleware/request_id",
30
+ ActionDispatch::RequestId::Probe.new
31
+ )
32
+ end
33
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skylight
4
+ module Probes
5
+ module ActionDispatch
6
+ module Routing
7
+ module RouteSet
8
+ module Instrumentation
9
+ def call(env)
10
+ ActiveSupport::Notifications.instrument("route_set.action_dispatch") { super }
11
+ end
12
+ end
13
+
14
+ class Probe
15
+ def install
16
+ ::ActionDispatch::Routing::RouteSet.prepend(Instrumentation)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ register(
24
+ :rails_router,
25
+ "ActionDispatch::Routing::RouteSet",
26
+ "action_dispatch/routing/route_set",
27
+ ActionDispatch::Routing::RouteSet::Probe.new
28
+ )
29
+ end
30
+ end
@@ -0,0 +1,2 @@
1
+ Skylight.probe("action_dispatch/request_id")
2
+ Skylight.probe("action_dispatch/routing/route_set")