skylight 3.1.4 → 5.3.4
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +465 -294
- data/CLA.md +1 -1
- data/CONTRIBUTING.md +11 -3
- data/ERRORS.md +3 -0
- data/LICENSE.md +8 -18
- data/README.md +1 -2
- data/bin/skylight +1 -1
- data/ext/extconf.rb +118 -122
- data/ext/libskylight.yml +8 -6
- data/ext/skylight_native.c +56 -100
- data/lib/skylight/api.rb +41 -27
- data/lib/skylight/cli/doctor.rb +68 -70
- data/lib/skylight/cli/helpers.rb +3 -5
- data/lib/skylight/cli/merger.rb +99 -92
- data/lib/skylight/cli.rb +40 -43
- data/lib/skylight/config.rb +656 -201
- data/lib/skylight/data/cacert.pem +730 -1023
- data/lib/skylight/deprecation.rb +17 -0
- data/lib/skylight/errors.rb +34 -16
- data/lib/skylight/extensions/source_location.rb +291 -0
- data/lib/skylight/extensions.rb +95 -0
- data/lib/skylight/formatters/http.rb +18 -0
- data/lib/skylight/gc.rb +99 -0
- data/lib/skylight/helpers.rb +82 -39
- data/lib/skylight/instrumenter.rb +339 -9
- data/lib/skylight/middleware.rb +147 -1
- data/lib/skylight/native.rb +71 -23
- data/lib/skylight/native_ext_fetcher.rb +39 -47
- data/lib/skylight/normalizers/action_controller/process_action.rb +68 -0
- data/lib/skylight/normalizers/action_controller/send_file.rb +51 -0
- data/lib/skylight/normalizers/action_dispatch/process_middleware.rb +22 -0
- data/lib/skylight/normalizers/action_dispatch/route_set.rb +27 -0
- data/lib/skylight/normalizers/action_view/render_collection.rb +24 -0
- data/lib/skylight/normalizers/action_view/render_layout.rb +25 -0
- data/lib/skylight/normalizers/action_view/render_partial.rb +23 -0
- data/lib/skylight/normalizers/action_view/render_template.rb +23 -0
- data/lib/skylight/normalizers/active_job/perform.rb +87 -0
- data/lib/skylight/normalizers/active_model_serializers/render.rb +32 -0
- data/lib/skylight/normalizers/active_record/instantiation.rb +16 -0
- data/lib/skylight/normalizers/active_record/sql.rb +20 -0
- data/lib/skylight/normalizers/active_storage.rb +28 -0
- data/lib/skylight/normalizers/active_support/cache.rb +11 -0
- data/lib/skylight/normalizers/active_support/cache_clear.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_decrement.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_delete.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_exist.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_fetch_hit.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_generate.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_increment.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_read.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_read_multi.rb +16 -0
- data/lib/skylight/normalizers/active_support/cache_write.rb +16 -0
- data/lib/skylight/normalizers/coach/handler_finish.rb +44 -0
- data/lib/skylight/normalizers/coach/middleware_finish.rb +33 -0
- data/lib/skylight/normalizers/couch_potato/query.rb +20 -0
- data/lib/skylight/normalizers/data_mapper/sql.rb +12 -0
- data/lib/skylight/normalizers/default.rb +24 -0
- data/lib/skylight/normalizers/elasticsearch/request.rb +20 -0
- data/lib/skylight/normalizers/faraday/request.rb +38 -0
- data/lib/skylight/normalizers/grape/endpoint.rb +28 -0
- data/lib/skylight/normalizers/grape/endpoint_render.rb +25 -0
- data/lib/skylight/normalizers/grape/endpoint_run.rb +39 -0
- data/lib/skylight/normalizers/grape/endpoint_run_filters.rb +20 -0
- data/lib/skylight/normalizers/grape/format_response.rb +20 -0
- data/lib/skylight/normalizers/graphiti/render.rb +22 -0
- data/lib/skylight/normalizers/graphiti/resolve.rb +31 -0
- data/lib/skylight/normalizers/graphql/base.rb +127 -0
- data/lib/skylight/normalizers/render.rb +79 -0
- data/lib/skylight/normalizers/sequel/sql.rb +12 -0
- data/lib/skylight/normalizers/shrine.rb +32 -0
- data/lib/skylight/normalizers/sql.rb +41 -0
- data/lib/skylight/normalizers.rb +157 -0
- data/lib/skylight/probes/action_controller.rb +52 -0
- data/lib/skylight/probes/action_dispatch/request_id.rb +33 -0
- data/lib/skylight/probes/action_dispatch/routing/route_set.rb +30 -0
- data/lib/skylight/probes/action_dispatch.rb +2 -0
- data/lib/skylight/probes/action_view.rb +42 -0
- data/lib/skylight/probes/active_job.rb +27 -0
- data/lib/skylight/probes/active_job_enqueue.rb +35 -0
- data/lib/skylight/probes/active_model_serializers.rb +50 -0
- data/lib/skylight/probes/active_record_async.rb +96 -0
- data/lib/skylight/probes/delayed_job.rb +144 -0
- data/lib/skylight/probes/elasticsearch.rb +45 -0
- data/lib/skylight/probes/excon/middleware.rb +65 -0
- data/lib/skylight/probes/excon.rb +25 -0
- data/lib/skylight/probes/faraday.rb +23 -0
- data/lib/skylight/probes/graphql.rb +38 -0
- data/lib/skylight/probes/httpclient.rb +44 -0
- data/lib/skylight/probes/middleware.rb +135 -0
- data/lib/skylight/probes/mongo.rb +169 -0
- data/lib/skylight/probes/mongoid.rb +6 -0
- data/lib/skylight/probes/net_http.rb +54 -0
- data/lib/skylight/probes/rack_builder.rb +37 -0
- data/lib/skylight/probes/redis.rb +68 -0
- data/lib/skylight/probes/sequel.rb +29 -0
- data/lib/skylight/probes/sinatra.rb +66 -0
- data/lib/skylight/probes/sinatra_add_middleware.rb +10 -10
- data/lib/skylight/probes/tilt.rb +25 -0
- data/lib/skylight/probes.rb +172 -0
- data/lib/skylight/railtie.rb +172 -15
- data/lib/skylight/sidekiq.rb +47 -0
- data/lib/skylight/sinatra.rb +2 -2
- data/lib/skylight/subscriber.rb +130 -0
- data/lib/skylight/test.rb +147 -0
- data/lib/skylight/trace.rb +331 -15
- data/lib/skylight/user_config.rb +60 -0
- data/lib/skylight/util/allocation_free.rb +26 -0
- data/lib/skylight/util/clock.rb +57 -0
- data/lib/skylight/util/component.rb +47 -9
- data/lib/skylight/util/deploy.rb +24 -40
- data/lib/skylight/util/gzip.rb +20 -0
- data/lib/skylight/util/hostname.rb +4 -4
- data/lib/skylight/util/http.rb +62 -71
- data/lib/skylight/util/instrumenter_method.rb +26 -0
- data/lib/skylight/util/logging.rb +136 -0
- data/lib/skylight/util/lru_cache.rb +36 -0
- data/lib/skylight/util/platform.rb +74 -0
- data/lib/skylight/util/proxy.rb +13 -0
- data/lib/skylight/util/ssl.rb +4 -28
- data/lib/skylight/util.rb +12 -0
- data/lib/skylight/vendor/cli/thor/rake_compat.rb +1 -1
- data/lib/skylight/version.rb +5 -1
- data/lib/skylight/vm/gc.rb +60 -0
- data/lib/skylight.rb +213 -24
- metadata +171 -53
|
@@ -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,96 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Probes
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module FutureResult
|
|
5
|
+
# Applied to ActiveSupport::Notifications::Event
|
|
6
|
+
module AsyncEventExtensions
|
|
7
|
+
# Notify Skylight that the event has started
|
|
8
|
+
def __sk_start!
|
|
9
|
+
subscriber = Skylight.instrumenter.subscriber
|
|
10
|
+
subscriber.start(name, nil, payload)
|
|
11
|
+
trace = Skylight.instrumenter.current_trace
|
|
12
|
+
|
|
13
|
+
# Set a finisher to end the event
|
|
14
|
+
@__sk_finisher = ->(name, payload) do
|
|
15
|
+
subscriber.with_trace(trace) { subscriber.finish(name, nil, payload) }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# End it immediately if we've actually already ended
|
|
19
|
+
__sk_finish! if @end
|
|
20
|
+
rescue StandardError => e
|
|
21
|
+
Skylight.error("Unable to start event for FutureResult: #{e}")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Notify Skylight that the event has finished
|
|
25
|
+
def __sk_finish!
|
|
26
|
+
return unless @__sk_finisher
|
|
27
|
+
|
|
28
|
+
@__sk_finisher.call(name, payload)
|
|
29
|
+
@__sk_finisher = nil
|
|
30
|
+
rescue StandardError => e
|
|
31
|
+
Skylight.error("Unable to finish event for FutureResult: #{e}")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# When the event is marked as finish make sure we notify Skylight
|
|
35
|
+
def finish!
|
|
36
|
+
super
|
|
37
|
+
__sk_finish!
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Applied to FutureResult
|
|
42
|
+
module Instrumentation
|
|
43
|
+
def result(*, **)
|
|
44
|
+
# This instruments the whole FutureResult so that we know when we were executing (potenially) async.
|
|
45
|
+
ActiveSupport::Notifications.instrument("future_result.active_record", { args: @args, kwargs: @kwargs }) do
|
|
46
|
+
super
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def execute_or_wait(*, **)
|
|
53
|
+
# At this point we're actually waiting for the query to have finished executing.
|
|
54
|
+
|
|
55
|
+
begin
|
|
56
|
+
# If the query has already started async, the @event_buffer will be defined.
|
|
57
|
+
# We grab the events (currently only the SQL queries), extend them with our
|
|
58
|
+
# special methods and notify Skylight.
|
|
59
|
+
# We act as if the event has just stared, though the query may already have been
|
|
60
|
+
# running. This means we're essentially just logging blocking time right now.
|
|
61
|
+
|
|
62
|
+
# Dup here just in case more get added somehow during the super call
|
|
63
|
+
events = @event_buffer&.instance_variable_get(:@events).dup
|
|
64
|
+
|
|
65
|
+
events&.each do |event|
|
|
66
|
+
event.singleton_class.prepend(AsyncEventExtensions)
|
|
67
|
+
event.__sk_start!
|
|
68
|
+
end
|
|
69
|
+
rescue StandardError => e
|
|
70
|
+
Skylight.error("Unable to start events for FutureResult: #{e}")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
super
|
|
74
|
+
ensure
|
|
75
|
+
# Once we've actually got a result, we mark each one as finished.
|
|
76
|
+
# Note that it may have already finished, but if it didn't we need to say so now.
|
|
77
|
+
events&.reverse_each(&:__sk_finish!)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class Probe
|
|
82
|
+
def install
|
|
83
|
+
::ActiveRecord::FutureResult.prepend(Instrumentation)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
register(
|
|
90
|
+
:active_record_async,
|
|
91
|
+
"ActiveRecord::FutureResult",
|
|
92
|
+
"active_record/future_result",
|
|
93
|
+
ActiveRecord::FutureResult::Probe.new
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
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,45 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Probes
|
|
3
|
+
module Elasticsearch
|
|
4
|
+
class Probe
|
|
5
|
+
def install
|
|
6
|
+
const =
|
|
7
|
+
if defined?(::Elasticsearch::Transport::Transport::Base)
|
|
8
|
+
::Elasticsearch::Transport::Transport::Base
|
|
9
|
+
elsif defined?(::Elastic::Transport::Transport::Base)
|
|
10
|
+
::Elastic::Transport::Transport::Base
|
|
11
|
+
else
|
|
12
|
+
return false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Prepending doesn't work here since this a module that's already been included
|
|
16
|
+
const.class_eval do
|
|
17
|
+
alias_method :perform_request_without_sk, :perform_request
|
|
18
|
+
def perform_request(method, path, *args, &block)
|
|
19
|
+
ActiveSupport::Notifications.instrument(
|
|
20
|
+
"request.elasticsearch",
|
|
21
|
+
name: "Request",
|
|
22
|
+
method: method,
|
|
23
|
+
path: path
|
|
24
|
+
) do
|
|
25
|
+
# Prevent HTTP-related probes from firing
|
|
26
|
+
Skylight::Normalizers::Faraday::Request.disable do
|
|
27
|
+
disable_skylight_probe(:NetHTTP) do
|
|
28
|
+
disable_skylight_probe(:HTTPClient) { perform_request_without_sk(method, path, *args, &block) }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def disable_skylight_probe(class_name)
|
|
35
|
+
klass = ::ActiveSupport::Inflector.safe_constantize("Skylight::Probes::#{class_name}::Probe")
|
|
36
|
+
(klass ? klass.disable { yield } : yield).tap { Skylight.log(:debug, "re-enabling: #{klass}") }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
register(:elasticsearch, "Elasticsearch", "elasticsearch", Elasticsearch::Probe.new)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require "skylight/formatters/http"
|
|
2
|
+
|
|
3
|
+
module Skylight
|
|
4
|
+
module Probes
|
|
5
|
+
module Excon
|
|
6
|
+
# Middleware for Excon that instruments requests
|
|
7
|
+
class Middleware < ::Excon::Middleware::Base
|
|
8
|
+
def initialize(*)
|
|
9
|
+
@requests = {}.compare_by_identity
|
|
10
|
+
super
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# TODO: Review the following:
|
|
14
|
+
# - Consider whether a LIFO queue would be sufficient
|
|
15
|
+
# - Check that errors can't be called without a request
|
|
16
|
+
|
|
17
|
+
def request_call(datum)
|
|
18
|
+
begin_instrumentation(datum)
|
|
19
|
+
super
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def response_call(datum)
|
|
23
|
+
super
|
|
24
|
+
ensure
|
|
25
|
+
end_instrumentation(datum)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def error_call(datum)
|
|
29
|
+
super
|
|
30
|
+
ensure
|
|
31
|
+
end_instrumentation(datum)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def begin_instrumentation(datum)
|
|
37
|
+
method = datum[:method].to_s
|
|
38
|
+
scheme = datum[:scheme]
|
|
39
|
+
host = datum[:host]
|
|
40
|
+
|
|
41
|
+
# TODO: Maybe don't show other default ports like 443
|
|
42
|
+
port = datum[:port] == 80 ? nil : datum[:port]
|
|
43
|
+
path = datum[:path]
|
|
44
|
+
query = datum[:query]
|
|
45
|
+
|
|
46
|
+
opts = Formatters::HTTP.build_opts(method, scheme, host, port, path, query)
|
|
47
|
+
|
|
48
|
+
@requests[datum] = Skylight.instrument(opts)
|
|
49
|
+
rescue Exception => e
|
|
50
|
+
Skylight.error "failed to begin instrumentation for Excon; msg=%s", e.message
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def end_instrumentation(datum)
|
|
54
|
+
if (request = @requests.delete(datum))
|
|
55
|
+
meta = {}
|
|
56
|
+
meta[:exception_object] = datum[:error] if datum[:error].is_a?(Exception)
|
|
57
|
+
Skylight.done(request, meta)
|
|
58
|
+
end
|
|
59
|
+
rescue Exception => e
|
|
60
|
+
Skylight.error "failed to end instrumentation for Excon; msg=%s", e.message
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Probes
|
|
3
|
+
module Excon
|
|
4
|
+
# Probe for instrumenting Excon requests. Installs {Excon::Middleware} to achieve this.
|
|
5
|
+
class Probe
|
|
6
|
+
def install
|
|
7
|
+
if defined?(::Excon::Middleware)
|
|
8
|
+
# Don't require until installation since it depends on Excon being loaded
|
|
9
|
+
require "skylight/probes/excon/middleware"
|
|
10
|
+
|
|
11
|
+
idx = ::Excon.defaults[:middlewares].index(::Excon::Middleware::Instrumentor)
|
|
12
|
+
|
|
13
|
+
# TODO: Handle possibility of idx being nil
|
|
14
|
+
::Excon.defaults[:middlewares].insert(idx, Probes::Excon::Middleware)
|
|
15
|
+
else
|
|
16
|
+
Skylight.error "The installed version of Excon doesn't support Middlewares. " \
|
|
17
|
+
"The Excon probe will be disabled."
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
register(:excon, "Excon", "excon", Excon::Probe.new)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Probes
|
|
3
|
+
module Faraday
|
|
4
|
+
module Instrumentation
|
|
5
|
+
def builder
|
|
6
|
+
unless defined?(@__sk__setup)
|
|
7
|
+
@__sk__setup = true
|
|
8
|
+
@builder.insert 0, ::Faraday::Request::Instrumentation
|
|
9
|
+
end
|
|
10
|
+
@builder
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Probe
|
|
15
|
+
def install
|
|
16
|
+
::Faraday::Connection.prepend(Instrumentation)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
register(:faraday, "Faraday", "faraday", Faraday::Probe.new)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/inflector"
|
|
4
|
+
|
|
5
|
+
module Skylight
|
|
6
|
+
module Probes
|
|
7
|
+
module GraphQL
|
|
8
|
+
module Instrumentation
|
|
9
|
+
def initialize(*, **)
|
|
10
|
+
super
|
|
11
|
+
|
|
12
|
+
return unless defined?(@tracers)
|
|
13
|
+
|
|
14
|
+
unless @tracers.include?(::GraphQL::Tracing::ActiveSupportNotificationsTracing)
|
|
15
|
+
@tracers << ::GraphQL::Tracing::ActiveSupportNotificationsTracing
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class Probe
|
|
21
|
+
def install
|
|
22
|
+
tracing_klass_name = "::GraphQL::Tracing::ActiveSupportNotificationsTracing"
|
|
23
|
+
klasses_to_probe = %w[::GraphQL::Execution::Multiplex ::GraphQL::Query]
|
|
24
|
+
|
|
25
|
+
return unless ([tracing_klass_name] + klasses_to_probe).all?(&method(:safe_constantize))
|
|
26
|
+
|
|
27
|
+
klasses_to_probe.each { |klass_name| safe_constantize(klass_name).prepend(Instrumentation) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def safe_constantize(klass_name)
|
|
31
|
+
ActiveSupport::Inflector.safe_constantize(klass_name)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
register(:graphql, "GraphQL", "graphql", GraphQL::Probe.new)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require "skylight/formatters/http"
|
|
2
|
+
|
|
3
|
+
module Skylight
|
|
4
|
+
module Probes
|
|
5
|
+
module HTTPClient
|
|
6
|
+
module Instrumentation
|
|
7
|
+
# HTTPClient has request methods on the class object itself,
|
|
8
|
+
# but they internally instantiate a client and perform the method
|
|
9
|
+
# on that, so this instance method override will cover both
|
|
10
|
+
# `HTTPClient.get(...)` and `HTTPClient.new.get(...)`
|
|
11
|
+
|
|
12
|
+
def do_request(method, uri, *)
|
|
13
|
+
return super if Probes::HTTPClient::Probe.disabled?
|
|
14
|
+
|
|
15
|
+
opts = Formatters::HTTP.build_opts(method, uri.scheme, uri.host, uri.port, uri.path, uri.query)
|
|
16
|
+
|
|
17
|
+
Skylight.instrument(opts) { super }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Probe
|
|
22
|
+
DISABLED_KEY = :__skylight_httpclient_disabled
|
|
23
|
+
|
|
24
|
+
def self.disable
|
|
25
|
+
old_value = Thread.current[DISABLED_KEY]
|
|
26
|
+
Thread.current[DISABLED_KEY] = true
|
|
27
|
+
yield
|
|
28
|
+
ensure
|
|
29
|
+
Thread.current[DISABLED_KEY] = old_value
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.disabled?
|
|
33
|
+
!!Thread.current[DISABLED_KEY]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def install
|
|
37
|
+
::HTTPClient.prepend(Instrumentation)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
register(:httpclient, "HTTPClient", "httpclient", HTTPClient::Probe.new)
|
|
43
|
+
end
|
|
44
|
+
end
|