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