skylight 4.2.2 → 5.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/CONTRIBUTING.md +1 -7
  4. data/ext/extconf.rb +6 -5
  5. data/ext/libskylight.yml +5 -6
  6. data/ext/skylight_native.c +24 -100
  7. data/lib/skylight.rb +204 -14
  8. data/lib/skylight/api.rb +7 -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 +604 -127
  13. data/lib/skylight/deprecation.rb +17 -0
  14. data/lib/skylight/errors.rb +17 -2
  15. data/lib/skylight/extensions.rb +107 -0
  16. data/lib/skylight/extensions/source_location.rb +280 -0
  17. data/lib/skylight/formatters/http.rb +19 -0
  18. data/lib/skylight/gc.rb +109 -0
  19. data/lib/skylight/helpers.rb +18 -2
  20. data/lib/skylight/instrumenter.rb +326 -15
  21. data/lib/skylight/middleware.rb +138 -1
  22. data/lib/skylight/native.rb +51 -1
  23. data/lib/skylight/native_ext_fetcher.rb +4 -3
  24. data/lib/skylight/normalizers.rb +151 -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/sql.rb +45 -0
  67. data/lib/skylight/probes.rb +181 -0
  68. data/lib/skylight/probes/action_controller.rb +48 -0
  69. data/lib/skylight/probes/action_dispatch.rb +2 -0
  70. data/lib/skylight/probes/action_dispatch/request_id.rb +29 -0
  71. data/lib/skylight/probes/action_dispatch/routing/route_set.rb +28 -0
  72. data/lib/skylight/probes/action_view.rb +43 -0
  73. data/lib/skylight/probes/active_job.rb +27 -0
  74. data/lib/skylight/probes/active_job_enqueue.rb +35 -0
  75. data/lib/skylight/probes/active_model_serializers.rb +50 -0
  76. data/lib/skylight/probes/delayed_job.rb +148 -0
  77. data/lib/skylight/probes/elasticsearch.rb +38 -0
  78. data/lib/skylight/probes/excon.rb +25 -0
  79. data/lib/skylight/probes/excon/middleware.rb +66 -0
  80. data/lib/skylight/probes/faraday.rb +23 -0
  81. data/lib/skylight/probes/graphql.rb +43 -0
  82. data/lib/skylight/probes/httpclient.rb +44 -0
  83. data/lib/skylight/probes/middleware.rb +126 -0
  84. data/lib/skylight/probes/mongo.rb +163 -0
  85. data/lib/skylight/probes/mongoid.rb +13 -0
  86. data/lib/skylight/probes/net_http.rb +54 -0
  87. data/lib/skylight/probes/redis.rb +60 -0
  88. data/lib/skylight/probes/sequel.rb +33 -0
  89. data/lib/skylight/probes/sinatra.rb +63 -0
  90. data/lib/skylight/probes/sinatra_add_middleware.rb +10 -10
  91. data/lib/skylight/probes/tilt.rb +27 -0
  92. data/lib/skylight/railtie.rb +162 -18
  93. data/lib/skylight/sidekiq.rb +48 -0
  94. data/lib/skylight/subscriber.rb +110 -0
  95. data/lib/skylight/test.rb +146 -0
  96. data/lib/skylight/trace.rb +305 -10
  97. data/lib/skylight/user_config.rb +61 -0
  98. data/lib/skylight/util.rb +12 -0
  99. data/lib/skylight/util/allocation_free.rb +26 -0
  100. data/lib/skylight/util/clock.rb +56 -0
  101. data/lib/skylight/util/component.rb +5 -2
  102. data/lib/skylight/util/deploy.rb +7 -10
  103. data/lib/skylight/util/gzip.rb +20 -0
  104. data/lib/skylight/util/http.rb +5 -11
  105. data/lib/skylight/util/instrumenter_method.rb +26 -0
  106. data/lib/skylight/util/logging.rb +138 -0
  107. data/lib/skylight/util/lru_cache.rb +40 -0
  108. data/lib/skylight/vendor/cli/thor/rake_compat.rb +1 -1
  109. data/lib/skylight/version.rb +5 -1
  110. data/lib/skylight/vm/gc.rb +68 -0
  111. metadata +117 -19
@@ -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,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
19
+ block.call
20
+ else
21
+ Skylight.instrument(title: "Enqueue #{name}", category: CAT, description: desc, &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
+ 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,148 @@
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
+ }
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
+ if method_type == :instance_method
103
+ "#{constant_name}##{method_name}"
104
+ else
105
+ "#{constant_name}.#{method_name}"
106
+ end
107
+ end
108
+ end
109
+
110
+ class Probe
111
+ def install
112
+ return unless validate_version && plugin_defined?
113
+
114
+ ::Delayed::Worker.plugins = [Skylight::Probes::DelayedJob::Plugin] | ::Delayed::Worker.plugins
115
+ ::Delayed::Backend::Base.class_eval do
116
+ alias_method :payload_object_without_sk, :payload_object
117
+
118
+ def payload_object
119
+ Skylight::Probes::DelayedJob::InstrumentationProxy.new(payload_object_without_sk)
120
+ end
121
+ end
122
+ end
123
+
124
+ private
125
+
126
+ def plugin_defined?
127
+ defined?(::Skylight::Probes::DelayedJob::Plugin)
128
+ end
129
+
130
+ def validate_version
131
+ spec = Gem.loaded_specs["delayed_job"]
132
+ version = spec&.version
133
+
134
+ if !version || version < Gem::Version.new("4.0.0")
135
+ Skylight.error "The installed version of DelayedJob is not supported on Skylight. " \
136
+ "Your jobs will not be tracked."
137
+
138
+ return false
139
+ end
140
+
141
+ true
142
+ end
143
+ end
144
+ end
145
+
146
+ register(:delayed_job, "Delayed::Worker", "delayed_job", DelayedJob::Probe.new)
147
+ end
148
+ 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 { puts "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