skylight 3.1.4 → 5.3.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|