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,135 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Probes
|
|
3
|
+
module Middleware
|
|
4
|
+
# for Rails >= 6.0, which includes InstrumentationProxy
|
|
5
|
+
module InstrumentationExtensions
|
|
6
|
+
def initialize(middleware, class_name)
|
|
7
|
+
super
|
|
8
|
+
|
|
9
|
+
# NOTE: Caching here leads to better performance, but will not notice if the method is overridden
|
|
10
|
+
# We don't have access to the config here so we can't check whether source locations are enabled.
|
|
11
|
+
# However, this only happens once per middleware so it should be minimal impact.
|
|
12
|
+
@payload[:sk_source_location] =
|
|
13
|
+
begin
|
|
14
|
+
if middleware.is_a?(Proc)
|
|
15
|
+
middleware.source_location
|
|
16
|
+
elsif middleware.respond_to?(:call)
|
|
17
|
+
middleware.method(:call).source_location
|
|
18
|
+
end
|
|
19
|
+
rescue StandardError
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# for Rails <= 5.2 ActionDispatch::MiddlewareStack::Middleware
|
|
26
|
+
module Instrumentation
|
|
27
|
+
def build(*)
|
|
28
|
+
Instrumentation.sk_instrument_middleware(super)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.sk_instrument_middleware(middleware)
|
|
32
|
+
return middleware if middleware.is_a?(Skylight::Middleware)
|
|
33
|
+
|
|
34
|
+
# Not sure how this would actually happen
|
|
35
|
+
return middleware if middleware.respond_to?(:__has_sk__)
|
|
36
|
+
|
|
37
|
+
# On Rails 3, ActionDispatch::Session::CookieStore is frozen, for one
|
|
38
|
+
return middleware if middleware.frozen?
|
|
39
|
+
|
|
40
|
+
Skylight::Probes::Middleware::Probe.add_instrumentation(middleware)
|
|
41
|
+
|
|
42
|
+
middleware
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class Probe
|
|
47
|
+
DISABLED_KEY = :__skylight_middleware_disabled
|
|
48
|
+
|
|
49
|
+
def self.disable!
|
|
50
|
+
@disabled = true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.enable!
|
|
54
|
+
@disabled = false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.disabled?
|
|
58
|
+
!!@disabled
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
module InstanceInstrumentation
|
|
62
|
+
def call(*args)
|
|
63
|
+
return super(*args) if Skylight::Probes::Middleware::Probe.disabled?
|
|
64
|
+
|
|
65
|
+
trace = Skylight.instrumenter&.current_trace
|
|
66
|
+
return super(*args) unless trace
|
|
67
|
+
|
|
68
|
+
begin
|
|
69
|
+
name = self.class.name || __sk_default_name
|
|
70
|
+
|
|
71
|
+
trace.endpoint = name
|
|
72
|
+
|
|
73
|
+
source_file, source_line = method(__method__).super_method.source_location
|
|
74
|
+
|
|
75
|
+
spans =
|
|
76
|
+
Skylight.instrument(
|
|
77
|
+
title: name,
|
|
78
|
+
category: __sk_category,
|
|
79
|
+
source_file: source_file,
|
|
80
|
+
source_line: source_line
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
proxied_response =
|
|
84
|
+
Skylight::Middleware.with_after_close(super(*args), debug_identifier: "Middleware: #{name}") do
|
|
85
|
+
Skylight.done(spans)
|
|
86
|
+
end
|
|
87
|
+
rescue Exception => e
|
|
88
|
+
Skylight.done(spans, exception_object: e)
|
|
89
|
+
raise
|
|
90
|
+
ensure
|
|
91
|
+
unless e || proxied_response
|
|
92
|
+
# If we've gotten to this point, the most likely scenario is that
|
|
93
|
+
# a throw/catch has bypassed a portion of the callstack. Since these spans would not otherwise
|
|
94
|
+
# be closed, mark them deferred to indicate that they should be implicitly closed.
|
|
95
|
+
# See Trace#deferred_spans or Trace#stop for more information.
|
|
96
|
+
Skylight.done(spans, defer: true)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def __sk_default_name
|
|
102
|
+
"Anonymous Middleware"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def __sk_category
|
|
106
|
+
"rack.middleware"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def __has_sk__
|
|
110
|
+
true
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def self.add_instrumentation(middleware)
|
|
115
|
+
middleware.singleton_class.prepend(InstanceInstrumentation)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def install
|
|
119
|
+
if defined?(::ActionDispatch::MiddlewareStack::InstrumentationProxy)
|
|
120
|
+
::ActionDispatch::MiddlewareStack::InstrumentationProxy.prepend(InstrumentationExtensions)
|
|
121
|
+
else
|
|
122
|
+
::ActionDispatch::MiddlewareStack::Middleware.prepend(Instrumentation)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
register(
|
|
129
|
+
:middleware,
|
|
130
|
+
"ActionDispatch::MiddlewareStack::Middleware",
|
|
131
|
+
"actionpack/action_dispatch",
|
|
132
|
+
Middleware::Probe.new
|
|
133
|
+
)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Probes
|
|
3
|
+
module Mongo
|
|
4
|
+
CAT = "db.mongo.command".freeze
|
|
5
|
+
|
|
6
|
+
class Probe
|
|
7
|
+
def install
|
|
8
|
+
subscriber = Subscriber.new
|
|
9
|
+
|
|
10
|
+
# From the mongo driver:
|
|
11
|
+
#
|
|
12
|
+
# > Global subscriptions must be established prior to creating
|
|
13
|
+
# > clients. When a client is constructed it copies subscribers from
|
|
14
|
+
# > the Global module; subsequent subscriptions or unsubscriptions
|
|
15
|
+
# > on the Global module have no effect on already created clients.
|
|
16
|
+
#
|
|
17
|
+
# So, for existing clients created before the Skylight initializer
|
|
18
|
+
# runs, we'll have to subscribe to those individually.
|
|
19
|
+
::Mongoid::Clients.clients.each { |_name, client| client.subscribe(::Mongo::Monitoring::COMMAND, subscriber) }
|
|
20
|
+
|
|
21
|
+
::Mongo::Monitoring::Global.subscribe(::Mongo::Monitoring::COMMAND, subscriber)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class Subscriber
|
|
26
|
+
include Skylight::Util::Logging
|
|
27
|
+
|
|
28
|
+
COMMANDS = %i[insert find count distinct update findandmodify findAndModify delete aggregate].freeze
|
|
29
|
+
|
|
30
|
+
COMMAND_NAMES = { findandmodify: "findAndModify".freeze }.freeze
|
|
31
|
+
|
|
32
|
+
def initialize
|
|
33
|
+
@events = {}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def started(event)
|
|
37
|
+
begin_instrumentation(event)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def succeeded(event)
|
|
41
|
+
end_instrumentation(event)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def failed(event)
|
|
45
|
+
end_instrumentation(event)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# For logging
|
|
49
|
+
def config
|
|
50
|
+
Skylight.config
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def begin_instrumentation(event)
|
|
56
|
+
return unless COMMANDS.include?(event.command_name.to_sym)
|
|
57
|
+
|
|
58
|
+
command_name = COMMAND_NAMES[event.command_name] || event.command_name.to_s
|
|
59
|
+
|
|
60
|
+
title = "#{event.database_name}.#{command_name}"
|
|
61
|
+
|
|
62
|
+
command = event.command
|
|
63
|
+
|
|
64
|
+
# Not sure if this will always exist
|
|
65
|
+
# Delete so the description will be less redundant
|
|
66
|
+
if (target = command[event.command_name])
|
|
67
|
+
title << " #{target}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
payload = {}
|
|
71
|
+
|
|
72
|
+
# Ruby Hashes are ordered based on insertion so do the most important ones first
|
|
73
|
+
|
|
74
|
+
add_value("key".freeze, command, payload)
|
|
75
|
+
add_bound("query".freeze, command, payload)
|
|
76
|
+
add_bound("filter".freeze, command, payload)
|
|
77
|
+
add_value("sort".freeze, command, payload)
|
|
78
|
+
|
|
79
|
+
add_bound("update".freeze, command, payload) if command_name == "findAndModify".freeze
|
|
80
|
+
|
|
81
|
+
add_value("remove".freeze, command, payload)
|
|
82
|
+
add_value("new".freeze, command, payload)
|
|
83
|
+
|
|
84
|
+
if (updates = command["updates".freeze])
|
|
85
|
+
# AFAICT the gem generally just sends one item in the updates array
|
|
86
|
+
update = updates[0]
|
|
87
|
+
update_payload = {}
|
|
88
|
+
add_bound("q".freeze, update, update_payload)
|
|
89
|
+
add_bound("u".freeze, update, update_payload)
|
|
90
|
+
add_value("multi".freeze, update, update_payload)
|
|
91
|
+
add_value("upsert".freeze, update, update_payload)
|
|
92
|
+
|
|
93
|
+
payload["updates".freeze] = [update_payload]
|
|
94
|
+
|
|
95
|
+
payload["updates".freeze] << "..." if updates.length > 1
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if (deletes = command["deletes".freeze])
|
|
99
|
+
# AFAICT the gem generally just sends one item in the updates array
|
|
100
|
+
delete = deletes[0]
|
|
101
|
+
delete_payload = {}
|
|
102
|
+
add_bound("q".freeze, delete, delete_payload)
|
|
103
|
+
add_value("limit".freeze, delete, delete_payload)
|
|
104
|
+
|
|
105
|
+
payload["deletes".freeze] = [delete_payload]
|
|
106
|
+
|
|
107
|
+
payload["deletes".freeze] << "..." if deletes.length > 1
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
if (pipeline = command["pipeline".freeze])
|
|
111
|
+
payload["pipeline".freeze] = pipeline.map { |segment| extract_binds(segment) }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# We're ignoring documents from insert because they could have completely inconsistent
|
|
115
|
+
# format which would make it hard to merge.
|
|
116
|
+
|
|
117
|
+
opts = {
|
|
118
|
+
category: CAT,
|
|
119
|
+
title: title,
|
|
120
|
+
description: payload.empty? ? nil : payload.to_json,
|
|
121
|
+
meta: {
|
|
122
|
+
database: event.database_name
|
|
123
|
+
},
|
|
124
|
+
internal: true
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@events[event.operation_id] = Skylight.instrument(opts)
|
|
128
|
+
rescue Exception => e
|
|
129
|
+
error "failed to begin instrumentation for Mongo; msg=%s", e.message
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def end_instrumentation(event)
|
|
133
|
+
if (original_event = @events.delete(event.operation_id))
|
|
134
|
+
meta = {}
|
|
135
|
+
if event.is_a?(::Mongo::Monitoring::Event::CommandFailed)
|
|
136
|
+
meta[:exception] = ["CommandFailed", event.message]
|
|
137
|
+
end
|
|
138
|
+
Skylight.done(original_event, meta)
|
|
139
|
+
end
|
|
140
|
+
rescue Exception => e
|
|
141
|
+
error "failed to end instrumentation for Mongo; msg=%s", e.message
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def add_value(key, command, payload)
|
|
145
|
+
if command.key?(key)
|
|
146
|
+
value = command[key]
|
|
147
|
+
payload[key] = value
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def add_bound(key, command, payload)
|
|
152
|
+
if (value = command[key])
|
|
153
|
+
payload[key] = extract_binds(value)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def extract_binds(hash)
|
|
158
|
+
ret = {}
|
|
159
|
+
|
|
160
|
+
hash.each { |k, v| ret[k] = v.is_a?(Hash) ? extract_binds(v) : "?".freeze }
|
|
161
|
+
|
|
162
|
+
ret
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
register(:mongo, "Mongo", "mongo", Mongo::Probe.new)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Older versions of the mongoid uses the moped under-the-hood, while newer
|
|
2
|
+
# verions uses the official driver. It used to be that the the mongoid probe
|
|
3
|
+
# exists to detect and enable either one of those underlying probes, but at
|
|
4
|
+
# this point we no longer support moped, so this is now just an alias for the
|
|
5
|
+
# mongo probe.
|
|
6
|
+
require_relative "mongo"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require "skylight/formatters/http"
|
|
2
|
+
|
|
3
|
+
module Skylight
|
|
4
|
+
module Probes
|
|
5
|
+
module NetHTTP
|
|
6
|
+
module Instrumentation
|
|
7
|
+
def request(req, *)
|
|
8
|
+
return super if !started? || Probes::NetHTTP::Probe.disabled?
|
|
9
|
+
|
|
10
|
+
method = req.method
|
|
11
|
+
|
|
12
|
+
# req['host'] also includes special handling for default ports
|
|
13
|
+
host, port = req["host"] ? req["host"].split(":") : nil
|
|
14
|
+
|
|
15
|
+
# If we're connected with a persistent socket
|
|
16
|
+
host ||= address
|
|
17
|
+
|
|
18
|
+
path = req.path
|
|
19
|
+
scheme = use_ssl? ? "https" : "http"
|
|
20
|
+
|
|
21
|
+
# Contained in the path
|
|
22
|
+
query = nil
|
|
23
|
+
|
|
24
|
+
opts = Formatters::HTTP.build_opts(method, scheme, host, port, path, query)
|
|
25
|
+
|
|
26
|
+
Skylight.instrument(opts) { super }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Probe for instrumenting Net::HTTP requests. Works by monkeypatching the default Net::HTTP#request method.
|
|
31
|
+
class Probe
|
|
32
|
+
DISABLED_KEY = :__skylight_net_http_disabled
|
|
33
|
+
|
|
34
|
+
def self.disable
|
|
35
|
+
state_was = Thread.current[DISABLED_KEY]
|
|
36
|
+
Thread.current[DISABLED_KEY] = true
|
|
37
|
+
yield
|
|
38
|
+
ensure
|
|
39
|
+
Thread.current[DISABLED_KEY] = state_was
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.disabled?
|
|
43
|
+
!!Thread.current[DISABLED_KEY]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def install
|
|
47
|
+
Net::HTTP.prepend(Instrumentation)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
register(:net_http, "Net::HTTP", "net/http", NetHTTP::Probe.new)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Probes
|
|
3
|
+
module Rack
|
|
4
|
+
module Builder
|
|
5
|
+
module Instrumentation
|
|
6
|
+
def use(middleware, *args, &block)
|
|
7
|
+
if @map
|
|
8
|
+
mapping = @map
|
|
9
|
+
@map = nil
|
|
10
|
+
@use << proc { |app| generate_map(app, mapping) }
|
|
11
|
+
end
|
|
12
|
+
@use << proc do |app|
|
|
13
|
+
middleware
|
|
14
|
+
.new(app, *args, &block)
|
|
15
|
+
.tap do |middleware_instance|
|
|
16
|
+
Skylight::Probes::Middleware::Instrumentation.sk_instrument_middleware(middleware_instance)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Probe
|
|
24
|
+
def install
|
|
25
|
+
if defined?(::Rack.release) && Gem::Version.new(::Rack.release) >= ::Gem::Version.new("1.4") &&
|
|
26
|
+
defined?(::Rack::Builder)
|
|
27
|
+
require "skylight/probes/middleware"
|
|
28
|
+
::Rack::Builder.prepend(Instrumentation)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
register(:rack_builder, "Rack::Builder", "rack/builder", Skylight::Probes::Rack::Builder::Probe.new)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Probes
|
|
3
|
+
module Redis
|
|
4
|
+
# Unfortunately, because of the nature of pipelining, there's no way for us to
|
|
5
|
+
# give a time breakdown on the individual items.
|
|
6
|
+
|
|
7
|
+
PIPELINED_OPTS = { category: "db.redis.pipelined".freeze, title: "PIPELINE".freeze, internal: true }.freeze
|
|
8
|
+
|
|
9
|
+
MULTI_OPTS = { category: "db.redis.multi".freeze, title: "MULTI".freeze, internal: true }.freeze
|
|
10
|
+
|
|
11
|
+
module ClientInstrumentation
|
|
12
|
+
def call(command, *)
|
|
13
|
+
command_name = command[0]
|
|
14
|
+
|
|
15
|
+
return super if command_name == :auth
|
|
16
|
+
|
|
17
|
+
opts = { category: "db.redis.command", title: command_name.upcase.to_s, internal: true }
|
|
18
|
+
|
|
19
|
+
Skylight.instrument(opts) { super }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module ClientInstrumentationV5
|
|
24
|
+
def call_v(command)
|
|
25
|
+
command_name = command[0]
|
|
26
|
+
|
|
27
|
+
return super if command_name == :auth
|
|
28
|
+
|
|
29
|
+
opts = { category: "db.redis.command", title: command_name.upcase.to_s, internal: true }
|
|
30
|
+
|
|
31
|
+
Skylight.instrument(opts) { super }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
module Instrumentation
|
|
36
|
+
def pipelined(*)
|
|
37
|
+
Skylight.instrument(PIPELINED_OPTS) { super }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def multi(*)
|
|
41
|
+
Skylight.instrument(MULTI_OPTS) { super }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class Probe
|
|
46
|
+
def install
|
|
47
|
+
version = defined?(::Redis::VERSION) ? Gem::Version.new(::Redis::VERSION) : nil
|
|
48
|
+
|
|
49
|
+
if !version || version < Gem::Version.new("3.0.0")
|
|
50
|
+
Skylight.error "The installed version of Redis doesn't support Middlewares. " \
|
|
51
|
+
"At least version 3.0.0 is required."
|
|
52
|
+
return
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if ::Redis::Client.method_defined?(:call_v)
|
|
56
|
+
::Redis::Client.prepend(ClientInstrumentationV5)
|
|
57
|
+
else
|
|
58
|
+
::Redis::Client.prepend(ClientInstrumentation)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
::Redis.prepend(Instrumentation)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
register(:redis, "Redis", "redis", Redis::Probe.new)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Supports 3.12.0+
|
|
2
|
+
module Skylight
|
|
3
|
+
module Probes
|
|
4
|
+
module Sequel
|
|
5
|
+
class Probe
|
|
6
|
+
def install
|
|
7
|
+
require "sequel/database/logging"
|
|
8
|
+
|
|
9
|
+
method_name = ::Sequel::Database.method_defined?(:log_connection_yield) ? "log_connection_yield" : "log_yield"
|
|
10
|
+
|
|
11
|
+
mod =
|
|
12
|
+
Module.new do
|
|
13
|
+
define_method method_name do |sql, *args, &block|
|
|
14
|
+
super(sql, *args) do
|
|
15
|
+
::ActiveSupport::Notifications.instrument("sql.sequel", sql: sql, name: "SQL", binds: args) do
|
|
16
|
+
block.call
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
::Sequel::Database.prepend(mod)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
register(:sequel, "Sequel", "sequel", Sequel::Probe.new)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Probes
|
|
3
|
+
module Sinatra
|
|
4
|
+
module ClassInstrumentation
|
|
5
|
+
def compile!(verb, path, *)
|
|
6
|
+
super.tap do |_, _, keys_or_wrapper, wrapper|
|
|
7
|
+
wrapper ||= keys_or_wrapper
|
|
8
|
+
|
|
9
|
+
# Deal with the situation where the path is a regex, and the default behavior
|
|
10
|
+
# of Ruby stringification produces an unreadable mess
|
|
11
|
+
if path.is_a?(Regexp)
|
|
12
|
+
human_readable = "<sk-regex>%r{#{path.source}}</sk-regex>"
|
|
13
|
+
wrapper.instance_variable_set(:@route_name, "#{verb} #{human_readable}")
|
|
14
|
+
else
|
|
15
|
+
wrapper.instance_variable_set(:@route_name, "#{verb} #{path}")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module Instrumentation
|
|
22
|
+
def dispatch!(*)
|
|
23
|
+
super.tap do
|
|
24
|
+
if (trace = Skylight.instrumenter&.current_trace) && (route = env["sinatra.route"])
|
|
25
|
+
# Include the app's mount point (if available)
|
|
26
|
+
script_name = trace.instrumenter.config.sinatra_route_prefixes? && env["SCRIPT_NAME"]
|
|
27
|
+
|
|
28
|
+
trace.endpoint =
|
|
29
|
+
if script_name && !script_name.empty?
|
|
30
|
+
verb, path = route.split(" ", 2)
|
|
31
|
+
"#{verb} [#{script_name}]#{path}"
|
|
32
|
+
else
|
|
33
|
+
route
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def compile_template(engine, data, options, *)
|
|
40
|
+
super.tap do |template|
|
|
41
|
+
if defined?(::Tilt::Template) && template.is_a?(::Tilt::Template)
|
|
42
|
+
# Pass along a useful "virtual path" to Tilt. The Tilt probe will handle
|
|
43
|
+
# instrumenting correctly.
|
|
44
|
+
virtual_path = data.is_a?(Symbol) ? data.to_s : "Inline template (#{engine})"
|
|
45
|
+
template.instance_variable_set(:@__sky_virtual_path, virtual_path)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class Probe
|
|
52
|
+
def install
|
|
53
|
+
if ::Sinatra::VERSION < "1.4.0"
|
|
54
|
+
Skylight.error "Sinatra must be version 1.4.0 or greater."
|
|
55
|
+
return
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
::Sinatra::Base.singleton_class.prepend(ClassInstrumentation)
|
|
59
|
+
::Sinatra::Base.prepend(Instrumentation)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
register(:sinatra, "Sinatra::Base", "sinatra/base", Sinatra::Probe.new)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
module Skylight
|
|
2
2
|
module Probes
|
|
3
3
|
module Sinatra
|
|
4
|
-
|
|
5
|
-
def
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
module Instrumentation
|
|
5
|
+
def setup_default_middleware(builder)
|
|
6
|
+
builder.use Skylight::Middleware
|
|
7
|
+
super
|
|
8
|
+
end
|
|
9
|
+
end
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
end
|
|
13
|
-
end
|
|
11
|
+
class AddMiddlewareProbe
|
|
12
|
+
def install
|
|
13
|
+
::Sinatra::Base.singleton_class.prepend(Instrumentation)
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
register(:sinatra_add_middleware, "Sinatra::Base", "sinatra/base", Sinatra::AddMiddlewareProbe.new)
|
|
19
19
|
end
|
|
20
20
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Should support 0.2+, though not tested against older versions
|
|
2
|
+
module Skylight
|
|
3
|
+
module Probes
|
|
4
|
+
module Tilt
|
|
5
|
+
module Instrumentation
|
|
6
|
+
def render(*args, &block)
|
|
7
|
+
opts = {
|
|
8
|
+
category: "view.render.template",
|
|
9
|
+
title: @__sky_virtual_path || options[:sky_virtual_path] || basename || "Unknown template name"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
Skylight.instrument(opts) { super(*args, &block) }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Probe
|
|
17
|
+
def install
|
|
18
|
+
::Tilt::Template.prepend(Instrumentation)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
register(:tilt, "Tilt::Template", "tilt/template", Tilt::Probe.new)
|
|
24
|
+
end
|
|
25
|
+
end
|