skylight 4.3.2 → 5.0.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -3
  3. data/CONTRIBUTING.md +2 -8
  4. data/ext/extconf.rb +6 -5
  5. data/ext/libskylight.yml +7 -6
  6. data/ext/skylight_native.c +22 -99
  7. data/lib/skylight.rb +211 -14
  8. data/lib/skylight/api.rb +10 -3
  9. data/lib/skylight/cli.rb +4 -3
  10. data/lib/skylight/cli/doctor.rb +13 -14
  11. data/lib/skylight/cli/merger.rb +6 -4
  12. data/lib/skylight/config.rb +597 -127
  13. data/lib/skylight/deprecation.rb +17 -0
  14. data/lib/skylight/errors.rb +21 -6
  15. data/lib/skylight/extensions.rb +107 -0
  16. data/lib/skylight/extensions/source_location.rb +291 -0
  17. data/lib/skylight/formatters/http.rb +20 -0
  18. data/lib/skylight/gc.rb +109 -0
  19. data/lib/skylight/helpers.rb +69 -26
  20. data/lib/skylight/instrumenter.rb +326 -15
  21. data/lib/skylight/middleware.rb +138 -1
  22. data/lib/skylight/native.rb +52 -2
  23. data/lib/skylight/native_ext_fetcher.rb +4 -3
  24. data/lib/skylight/normalizers.rb +153 -0
  25. data/lib/skylight/normalizers/action_controller/process_action.rb +69 -0
  26. data/lib/skylight/normalizers/action_controller/send_file.rb +50 -0
  27. data/lib/skylight/normalizers/action_dispatch/process_middleware.rb +22 -0
  28. data/lib/skylight/normalizers/action_dispatch/route_set.rb +27 -0
  29. data/lib/skylight/normalizers/action_view/render_collection.rb +24 -0
  30. data/lib/skylight/normalizers/action_view/render_layout.rb +25 -0
  31. data/lib/skylight/normalizers/action_view/render_partial.rb +23 -0
  32. data/lib/skylight/normalizers/action_view/render_template.rb +23 -0
  33. data/lib/skylight/normalizers/active_job/perform.rb +86 -0
  34. data/lib/skylight/normalizers/active_model_serializers/render.rb +28 -0
  35. data/lib/skylight/normalizers/active_record/instantiation.rb +16 -0
  36. data/lib/skylight/normalizers/active_record/sql.rb +12 -0
  37. data/lib/skylight/normalizers/active_storage.rb +30 -0
  38. data/lib/skylight/normalizers/active_support/cache.rb +22 -0
  39. data/lib/skylight/normalizers/active_support/cache_clear.rb +16 -0
  40. data/lib/skylight/normalizers/active_support/cache_decrement.rb +16 -0
  41. data/lib/skylight/normalizers/active_support/cache_delete.rb +16 -0
  42. data/lib/skylight/normalizers/active_support/cache_exist.rb +16 -0
  43. data/lib/skylight/normalizers/active_support/cache_fetch_hit.rb +16 -0
  44. data/lib/skylight/normalizers/active_support/cache_generate.rb +16 -0
  45. data/lib/skylight/normalizers/active_support/cache_increment.rb +16 -0
  46. data/lib/skylight/normalizers/active_support/cache_read.rb +16 -0
  47. data/lib/skylight/normalizers/active_support/cache_read_multi.rb +16 -0
  48. data/lib/skylight/normalizers/active_support/cache_write.rb +16 -0
  49. data/lib/skylight/normalizers/coach/handler_finish.rb +46 -0
  50. data/lib/skylight/normalizers/coach/middleware_finish.rb +33 -0
  51. data/lib/skylight/normalizers/couch_potato/query.rb +20 -0
  52. data/lib/skylight/normalizers/data_mapper/sql.rb +12 -0
  53. data/lib/skylight/normalizers/default.rb +32 -0
  54. data/lib/skylight/normalizers/elasticsearch/request.rb +20 -0
  55. data/lib/skylight/normalizers/faraday/request.rb +40 -0
  56. data/lib/skylight/normalizers/grape/endpoint.rb +34 -0
  57. data/lib/skylight/normalizers/grape/endpoint_render.rb +25 -0
  58. data/lib/skylight/normalizers/grape/endpoint_run.rb +41 -0
  59. data/lib/skylight/normalizers/grape/endpoint_run_filters.rb +22 -0
  60. data/lib/skylight/normalizers/grape/format_response.rb +20 -0
  61. data/lib/skylight/normalizers/graphiti/render.rb +22 -0
  62. data/lib/skylight/normalizers/graphiti/resolve.rb +31 -0
  63. data/lib/skylight/normalizers/graphql/base.rb +132 -0
  64. data/lib/skylight/normalizers/render.rb +81 -0
  65. data/lib/skylight/normalizers/sequel/sql.rb +12 -0
  66. data/lib/skylight/normalizers/shrine.rb +34 -0
  67. data/lib/skylight/normalizers/sql.rb +45 -0
  68. data/lib/skylight/probes.rb +181 -0
  69. data/lib/skylight/probes/action_controller.rb +48 -0
  70. data/lib/skylight/probes/action_dispatch.rb +2 -0
  71. data/lib/skylight/probes/action_dispatch/request_id.rb +29 -0
  72. data/lib/skylight/probes/action_dispatch/routing/route_set.rb +28 -0
  73. data/lib/skylight/probes/action_view.rb +43 -0
  74. data/lib/skylight/probes/active_job.rb +27 -0
  75. data/lib/skylight/probes/active_job_enqueue.rb +41 -0
  76. data/lib/skylight/probes/active_model_serializers.rb +50 -0
  77. data/lib/skylight/probes/delayed_job.rb +149 -0
  78. data/lib/skylight/probes/elasticsearch.rb +38 -0
  79. data/lib/skylight/probes/excon.rb +25 -0
  80. data/lib/skylight/probes/excon/middleware.rb +66 -0
  81. data/lib/skylight/probes/faraday.rb +23 -0
  82. data/lib/skylight/probes/graphql.rb +43 -0
  83. data/lib/skylight/probes/httpclient.rb +44 -0
  84. data/lib/skylight/probes/middleware.rb +126 -0
  85. data/lib/skylight/probes/mongo.rb +164 -0
  86. data/lib/skylight/probes/mongoid.rb +13 -0
  87. data/lib/skylight/probes/net_http.rb +54 -0
  88. data/lib/skylight/probes/redis.rb +63 -0
  89. data/lib/skylight/probes/sequel.rb +33 -0
  90. data/lib/skylight/probes/sinatra.rb +63 -0
  91. data/lib/skylight/probes/sinatra_add_middleware.rb +10 -10
  92. data/lib/skylight/probes/tilt.rb +27 -0
  93. data/lib/skylight/railtie.rb +162 -18
  94. data/lib/skylight/sidekiq.rb +48 -0
  95. data/lib/skylight/subscriber.rb +110 -0
  96. data/lib/skylight/test.rb +146 -0
  97. data/lib/skylight/trace.rb +307 -10
  98. data/lib/skylight/user_config.rb +61 -0
  99. data/lib/skylight/util.rb +12 -0
  100. data/lib/skylight/util/allocation_free.rb +26 -0
  101. data/lib/skylight/util/clock.rb +56 -0
  102. data/lib/skylight/util/component.rb +5 -2
  103. data/lib/skylight/util/deploy.rb +7 -10
  104. data/lib/skylight/util/gzip.rb +20 -0
  105. data/lib/skylight/util/http.rb +4 -10
  106. data/lib/skylight/util/instrumenter_method.rb +26 -0
  107. data/lib/skylight/util/logging.rb +138 -0
  108. data/lib/skylight/util/lru_cache.rb +40 -0
  109. data/lib/skylight/util/platform.rb +1 -1
  110. data/lib/skylight/vendor/cli/thor/rake_compat.rb +1 -1
  111. data/lib/skylight/version.rb +5 -1
  112. data/lib/skylight/vm/gc.rb +68 -0
  113. metadata +126 -13
@@ -0,0 +1,48 @@
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
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(:action_controller, "ActionController::Instrumentation", "action_controller/metal/instrumentation",
46
+ ActionController::Probe.new)
47
+ end
48
+ end
@@ -0,0 +1,2 @@
1
+ Skylight.probe("action_dispatch/request_id")
2
+ Skylight.probe("action_dispatch/routing/route_set")
@@ -0,0 +1,29 @@
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(:action_dispatch, "ActionDispatch::RequestId", "action_dispatch/middleware/request_id",
27
+ ActionDispatch::RequestId::Probe.new)
28
+ end
29
+ end
@@ -0,0 +1,28 @@
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") do
11
+ super
12
+ end
13
+ end
14
+ end
15
+
16
+ class Probe
17
+ def install
18
+ ::ActionDispatch::Routing::RouteSet.prepend(Instrumentation)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ register(:rails_router, "ActionDispatch::Routing::RouteSet", "action_dispatch/routing/route_set",
26
+ ActionDispatch::Routing::RouteSet::Probe.new)
27
+ end
28
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skylight
4
+ module Probes
5
+ module ActionView
6
+ module Instrumentation
7
+ def render_with_layout(*args) #:nodoc:
8
+ path, locals = case args.length
9
+ when 2
10
+ args
11
+ when 4
12
+ # Rails > 6.0.0.beta3 arguments are (view, template, path, locals)
13
+ [args[2], args[3]]
14
+ end
15
+
16
+ layout = nil
17
+
18
+ if path
19
+ layout = find_layout(path, locals.keys, [formats.first])
20
+ end
21
+
22
+ if layout
23
+ ActiveSupport::Notifications.instrument("render_template.action_view", identifier: layout.identifier) do
24
+ super
25
+ end
26
+ else
27
+ super
28
+ end
29
+ end
30
+ end
31
+
32
+ class Probe
33
+ def install
34
+ return if ::ActionView.gem_version >= Gem::Version.new("6.1.0.alpha")
35
+
36
+ ::ActionView::TemplateRenderer.prepend(Instrumentation)
37
+ end
38
+ end
39
+ end
40
+
41
+ register(:action_view, "ActionView::TemplateRenderer", "action_view", ActionView::Probe.new)
42
+ end
43
+ end
@@ -0,0 +1,27 @@
1
+ module Skylight
2
+ module Probes
3
+ module ActiveJob
4
+ TITLE = "ActiveJob.execute".freeze
5
+
6
+ module Instrumentation
7
+ def execute(*)
8
+ Skylight.trace(TITLE, "app.job.execute", component: :worker) do |trace|
9
+ # See normalizers/active_job/perform for endpoint/segment assignment
10
+ super
11
+ rescue Exception
12
+ trace.segment = "error" if trace
13
+ raise
14
+ end
15
+ end
16
+ end
17
+
18
+ class Probe
19
+ def install
20
+ ::ActiveJob::Base.singleton_class.prepend(Instrumentation)
21
+ end
22
+ end
23
+ end
24
+
25
+ register(:active_job, "ActiveJob::Base", "active_job/base", ActiveJob::Probe.new)
26
+ end
27
+ end
@@ -0,0 +1,41 @@
1
+ module Skylight
2
+ module Probes
3
+ module ActiveJob
4
+ class EnqueueProbe
5
+ CAT = "other.active_job.enqueue".freeze
6
+
7
+ def install
8
+ ::ActiveJob::Base.around_enqueue do |job, block|
9
+ job_class = job.class
10
+ adapter_name = EnqueueProbe.normalize_adapter_name(job_class)
11
+
12
+ # If this is an ActionMailer::DeliveryJob, we'll report this as the mailer title
13
+ # and include ActionMailer::DeliveryJob in the description.
14
+ name, job_class_name = Normalizers::ActiveJob::Perform.normalize_title(job)
15
+ descriptors = ["adapter: '#{adapter_name}'", "queue: '#{job.queue_name}'"]
16
+ descriptors << "job: '#{job_class_name}'" if job_class_name
17
+ desc = "{ #{descriptors.join(', ')} }"
18
+ rescue
19
+ block.call
20
+ else
21
+ Skylight.instrument(
22
+ title: "Enqueue #{name}",
23
+ category: CAT,
24
+ description: desc,
25
+ internal: true,
26
+ &block
27
+ )
28
+ end
29
+
30
+ self.class.instance_eval do
31
+ def normalize_adapter_name(job_class)
32
+ job_class.queue_adapter_name
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ register(:active_job_enqueue, "ActiveJob::Base", "active_job/base", ActiveJob::EnqueueProbe.new)
40
+ end
41
+ end
@@ -0,0 +1,50 @@
1
+ module Skylight
2
+ module Probes
3
+ module ActiveModelSerializers
4
+ module Instrumentation
5
+ def as_json(*)
6
+ payload = { serializer: self.class }
7
+ ActiveSupport::Notifications.instrument("render.active_model_serializers", payload) { super }
8
+ end
9
+ end
10
+
11
+ class Probe
12
+ def install
13
+ version = nil
14
+
15
+ # File moved location between version
16
+ %w[serializer serializers].each do |dir|
17
+ require "active_model/#{dir}/version"
18
+ rescue LoadError # rubocop:disable Lint/SuppressedException
19
+ end
20
+
21
+ if Gem.loaded_specs["active_model_serializers"]
22
+ version = Gem.loaded_specs["active_model_serializers"].version
23
+ end
24
+
25
+ if !version || version < Gem::Version.new("0.5.0")
26
+ Skylight.error "Instrumention is only available for ActiveModelSerializers version 0.5.0 and greater."
27
+ return
28
+ end
29
+
30
+ # We don't actually support the RCs correctly, requires
31
+ # a release after 0.10.0.rc3
32
+ if version >= Gem::Version.new("0.10.0.rc1")
33
+ # AS::N is built in to newer versions
34
+ return
35
+ end
36
+
37
+ # End users could override as_json without calling super, but it's likely safer
38
+ # than overriding serializable_array/hash/object.
39
+
40
+ [::ActiveModel::Serializer, ::ActiveModel::ArraySerializer].each do |klass|
41
+ klass.prepend(Instrumentation)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ register(:active_model_serializers, "ActiveModel::Serializer", "active_model/serializer",
48
+ ActiveModelSerializers::Probe.new)
49
+ end
50
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+
5
+ module Skylight
6
+ module Probes
7
+ module DelayedJob
8
+ begin
9
+ require "delayed/plugin"
10
+
11
+ class Plugin < ::Delayed::Plugin
12
+ callbacks do |lifecycle|
13
+ lifecycle.around(:perform) do |worker, job, &block|
14
+ sk_instrument(worker, job, &block)
15
+ end
16
+
17
+ lifecycle.after(:error) do |_worker, _job|
18
+ Skylight.trace&.segment = "error"
19
+ end
20
+ end
21
+
22
+ class << self
23
+ include Skylight::Util::Logging
24
+
25
+ def sk_instrument(_worker, job)
26
+ endpoint = Skylight::Probes::DelayedJob.handler_name(job)
27
+
28
+ Skylight.trace(endpoint,
29
+ "app.delayed_job.worker",
30
+ "Delayed::Worker#run",
31
+ component: :worker,
32
+ segment: job.queue,
33
+ meta: { source_location: "delayed_job" }) do
34
+ t { "Delayed::Job beginning trace" }
35
+ yield
36
+ end
37
+ end
38
+ end
39
+ end
40
+ rescue LoadError
41
+ $stderr.puts "[SKYLIGHT] The delayed_job probe was requested, but Delayed::Plugin was not defined."
42
+ end
43
+
44
+ UNKNOWN = "<Delayed::Job Unknown>"
45
+
46
+ def self.handler_name(job)
47
+ payload_object = if job.respond_to?(:payload_object_without_sk)
48
+ job.payload_object_without_sk
49
+ else
50
+ job.payload_object
51
+ end
52
+
53
+ payload_object_name(payload_object)
54
+ end
55
+
56
+ def self.payload_object_name(payload_object)
57
+ if payload_object.is_a?(::Delayed::PerformableMethod)
58
+ payload_object.display_name
59
+ else
60
+ # In the case of ActiveJob-wrapped jobs, there is quite a bit of job-specific metadata
61
+ # in `job.name`, which would break aggregation and potentially leak private data in job args.
62
+ # Use class name instead to avoid this.
63
+ payload_object.class.name
64
+ end
65
+ rescue
66
+ UNKNOWN
67
+ end
68
+
69
+ def self.payload_object_source_meta(payload_object)
70
+ if payload_object.is_a?(::Delayed::PerformableMethod)
71
+ if payload_object.object.is_a?(Module)
72
+ [:class_method, payload_object.object.name, payload_object.method_name.to_s]
73
+ else
74
+ [:instance_method, payload_object.object.class.name, payload_object.method_name.to_s]
75
+ end
76
+ else
77
+ [:instance_method, payload_object.class.name, "perform"]
78
+ end
79
+ end
80
+
81
+ class InstrumentationProxy < SimpleDelegator
82
+ def perform
83
+ source_meta = Skylight::Probes::DelayedJob.payload_object_source_meta(__getobj__)
84
+
85
+ opts = {
86
+ category: "app.delayed_job.job",
87
+ title: format_source(*source_meta),
88
+ meta: { source_location_hint: source_meta },
89
+ internal: true
90
+ }
91
+
92
+ Skylight.instrument(opts) { __getobj__.perform }
93
+ end
94
+
95
+ # Used by Delayed::Backend::Base to determine Job#name
96
+ def display_name
97
+ __getobj__.respond_to?(:display_name) ? __getobj__.display_name : __getobj__.class.name
98
+ end
99
+
100
+ private
101
+
102
+ def format_source(method_type, constant_name, method_name)
103
+ if method_type == :instance_method
104
+ "#{constant_name}##{method_name}"
105
+ else
106
+ "#{constant_name}.#{method_name}"
107
+ end
108
+ end
109
+ end
110
+
111
+ class Probe
112
+ def install
113
+ return unless validate_version && plugin_defined?
114
+
115
+ ::Delayed::Worker.plugins = [Skylight::Probes::DelayedJob::Plugin] | ::Delayed::Worker.plugins
116
+ ::Delayed::Backend::Base.class_eval do
117
+ alias_method :payload_object_without_sk, :payload_object
118
+
119
+ def payload_object
120
+ Skylight::Probes::DelayedJob::InstrumentationProxy.new(payload_object_without_sk)
121
+ end
122
+ end
123
+ end
124
+
125
+ private
126
+
127
+ def plugin_defined?
128
+ defined?(::Skylight::Probes::DelayedJob::Plugin)
129
+ end
130
+
131
+ def validate_version
132
+ spec = Gem.loaded_specs["delayed_job"]
133
+ version = spec&.version
134
+
135
+ if !version || version < Gem::Version.new("4.0.0")
136
+ Skylight.error "The installed version of DelayedJob is not supported on Skylight. " \
137
+ "Your jobs will not be tracked."
138
+
139
+ return false
140
+ end
141
+
142
+ true
143
+ end
144
+ end
145
+ end
146
+
147
+ register(:delayed_job, "Delayed::Worker", "delayed_job", DelayedJob::Probe.new)
148
+ end
149
+ end
@@ -0,0 +1,38 @@
1
+ module Skylight
2
+ module Probes
3
+ module Elasticsearch
4
+ class Probe
5
+ def install
6
+ # Prepending doesn't work here since this a module that's already been included
7
+ ::Elasticsearch::Transport::Transport::Base.class_eval do
8
+ alias_method :perform_request_without_sk, :perform_request
9
+ def perform_request(method, path, *args, &block)
10
+ ActiveSupport::Notifications.instrument(
11
+ "request.elasticsearch",
12
+ name: "Request",
13
+ method: method,
14
+ path: path
15
+ ) do
16
+ # Prevent HTTP-related probes from firing
17
+ Skylight::Normalizers::Faraday::Request.disable do
18
+ disable_skylight_probe(:NetHTTP) do
19
+ disable_skylight_probe(:HTTPClient) do
20
+ perform_request_without_sk(method, path, *args, &block)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ def disable_skylight_probe(class_name)
28
+ klass = ::ActiveSupport::Inflector.safe_constantize("Skylight::Probes::#{class_name}::Probe")
29
+ (klass ? klass.disable { yield } : yield).tap { Skylight.log(:debug, "re-enabling: #{klass}") }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ register(:elasticsearch, "Elasticsearch", "elasticsearch", Elasticsearch::Probe.new)
37
+ end
38
+ end