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,172 @@
|
|
|
1
|
+
require "pathname"
|
|
2
|
+
require "active_support/inflector"
|
|
3
|
+
|
|
4
|
+
module Skylight
|
|
5
|
+
# @api private
|
|
6
|
+
module Probes
|
|
7
|
+
class ProbeRegistration
|
|
8
|
+
attr_reader :name, :const_name, :require_paths, :probe
|
|
9
|
+
|
|
10
|
+
def initialize(name, const_name, require_paths, probe)
|
|
11
|
+
@name = name
|
|
12
|
+
@const_name = const_name
|
|
13
|
+
@require_paths = Array(require_paths)
|
|
14
|
+
@probe = probe
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def install
|
|
18
|
+
probe.install
|
|
19
|
+
rescue StandardError, LoadError => e
|
|
20
|
+
log_install_exception(e)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def constant_available?
|
|
24
|
+
Skylight::Probes.constant_available?(const_name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def log_install_exception(err)
|
|
30
|
+
description = err.class.to_s
|
|
31
|
+
description << ": #{err.message}" unless err.message.empty?
|
|
32
|
+
|
|
33
|
+
backtrace = err.backtrace.map { |l| " #{l}" }.join("\n")
|
|
34
|
+
|
|
35
|
+
# rubocop:disable Lint/SuppressedException
|
|
36
|
+
gems =
|
|
37
|
+
begin
|
|
38
|
+
Bundler.locked_gems.dependencies.map { |d| [d.name, d.requirement.to_s] }
|
|
39
|
+
rescue StandardError
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# rubocop:enable Lint/SuppressedException
|
|
43
|
+
|
|
44
|
+
error =
|
|
45
|
+
"[SKYLIGHT] [#{Skylight::VERSION}] Encountered an error while installing the " \
|
|
46
|
+
"probe for #{const_name}. Please notify support@skylight.io with the debugging " \
|
|
47
|
+
"information below. It's recommended that you disable this probe until the " \
|
|
48
|
+
"issue is resolved." \
|
|
49
|
+
"\n\nERROR: #{description}\n\n#{backtrace}\n\n"
|
|
50
|
+
|
|
51
|
+
if gems
|
|
52
|
+
gems_string = gems.map { |g| " #{g[0]} #{g[1]}" }.join("\n")
|
|
53
|
+
error << "GEMS:\n\n#{gems_string}\n\n"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
$stderr.puts(error)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class << self
|
|
61
|
+
def constant_available?(const_name)
|
|
62
|
+
::ActiveSupport::Inflector.safe_constantize(const_name).present?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def install!
|
|
66
|
+
pending = registered.values - installed.values
|
|
67
|
+
|
|
68
|
+
pending.each do |registration|
|
|
69
|
+
registration.constant_available? ? install_probe(registration) : register_require_hook(registration)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def install_probe(registration)
|
|
74
|
+
return if installed.key?(registration.name)
|
|
75
|
+
|
|
76
|
+
installed[registration.name] = registration
|
|
77
|
+
registration.install
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def add_path(path)
|
|
81
|
+
Dir.glob("**/*.rb", base: path) do |f|
|
|
82
|
+
name = Pathname.new(f).sub_ext("").to_s
|
|
83
|
+
full_path = File.expand_path(f, path)
|
|
84
|
+
|
|
85
|
+
raise "duplicate probe name: #{name}; original=#{available[name]}; new=#{full_path}" if available.key?(name)
|
|
86
|
+
|
|
87
|
+
available[name] = full_path
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def available
|
|
92
|
+
@available ||= {}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def probe(*probes)
|
|
96
|
+
unknown = probes.map(&:to_s) - available.keys
|
|
97
|
+
raise ArgumentError, "unknown probes: #{unknown.join(", ")}" unless unknown.empty?
|
|
98
|
+
|
|
99
|
+
probes.each { |p| require available[p.to_s] }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def registered
|
|
103
|
+
@registered ||= {}
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def require_hooks
|
|
107
|
+
@require_hooks ||= {}
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def installed
|
|
111
|
+
@installed ||= {}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def register(name, *args)
|
|
115
|
+
raise "already registered: #{name}" if registered.key?(name)
|
|
116
|
+
|
|
117
|
+
registered[name] = ProbeRegistration.new(name, *args)
|
|
118
|
+
|
|
119
|
+
true
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def require_hook(require_path)
|
|
123
|
+
each_by_require_path(require_path) do |registration|
|
|
124
|
+
# Double check constant is available
|
|
125
|
+
next unless registration.constant_available?
|
|
126
|
+
|
|
127
|
+
install_probe(registration)
|
|
128
|
+
|
|
129
|
+
# Don't need this to be called again
|
|
130
|
+
unregister_require_hook(registration)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def register_require_hook(registration)
|
|
135
|
+
registration.require_paths.each do |p|
|
|
136
|
+
require_hooks[p] ||= []
|
|
137
|
+
require_hooks[p] << registration
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def unregister_require_hook(registration)
|
|
142
|
+
registration.require_paths.each do |p|
|
|
143
|
+
require_hooks[p].delete(registration)
|
|
144
|
+
require_hooks.delete(p) if require_hooks[p].empty?
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def each_by_require_path(require_path)
|
|
149
|
+
return unless require_hooks.key?(require_path)
|
|
150
|
+
|
|
151
|
+
# dup because we may be mutating the array
|
|
152
|
+
require_hooks[require_path].dup.each { |registration| yield registration }
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
add_path(File.expand_path("./probes", __dir__))
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# @api private
|
|
161
|
+
module Kernel
|
|
162
|
+
# Unfortunately, we can't use prepend here, in part because RubyGems changes require with an alias
|
|
163
|
+
alias require_without_sk require
|
|
164
|
+
|
|
165
|
+
def require(name)
|
|
166
|
+
require_without_sk(name).tap do
|
|
167
|
+
Skylight::Probes.require_hook(name)
|
|
168
|
+
rescue Exception => e
|
|
169
|
+
warn("[SKYLIGHT] Rescued exception in require hook", e)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
data/lib/skylight/railtie.rb
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "skylight"
|
|
2
|
+
require "rails"
|
|
2
3
|
|
|
3
4
|
module Skylight
|
|
5
|
+
# @api private
|
|
4
6
|
class Railtie < Rails::Railtie
|
|
5
|
-
include Skylight::Core::Railtie
|
|
6
|
-
|
|
7
|
-
def self.config_class; Skylight::Config end
|
|
8
|
-
def self.middleware_class; Skylight::Middleware end
|
|
9
|
-
|
|
10
7
|
config.skylight = ActiveSupport::OrderedOptions.new
|
|
11
8
|
|
|
12
9
|
# The environments in which skylight should be enabled
|
|
13
|
-
config.skylight.environments = [
|
|
10
|
+
config.skylight.environments = ["production"]
|
|
14
11
|
|
|
15
12
|
# The path to the configuration file
|
|
16
13
|
config.skylight.config_path = "config/skylight.yml"
|
|
@@ -19,29 +16,189 @@ module Skylight
|
|
|
19
16
|
# net_http, action_controller, action_dispatch, action_view, and middleware are on by default
|
|
20
17
|
# See https://www.skylight.io/support/getting-more-from-skylight#available-instrumentation-options
|
|
21
18
|
# for a full list.
|
|
22
|
-
config.skylight.probes = [
|
|
19
|
+
config.skylight.probes = %w[
|
|
20
|
+
net_http
|
|
21
|
+
action_controller
|
|
22
|
+
action_dispatch
|
|
23
|
+
action_view
|
|
24
|
+
middleware
|
|
25
|
+
active_job_enqueue
|
|
26
|
+
active_record_async
|
|
27
|
+
]
|
|
23
28
|
|
|
24
29
|
# The position in the middleware stack to place Skylight
|
|
25
30
|
# Default is first, but can be `{ after: Middleware::Name }` or `{ before: Middleware::Name }`
|
|
26
31
|
config.skylight.middleware_position = 0
|
|
27
32
|
|
|
28
|
-
initializer
|
|
33
|
+
initializer "skylight.configure" do |app|
|
|
29
34
|
run_initializer(app)
|
|
30
35
|
end
|
|
31
36
|
|
|
32
37
|
private
|
|
33
38
|
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
# We must have an opt-in signal
|
|
40
|
+
def show_worker_activation_warning(sk_config)
|
|
41
|
+
reasons = []
|
|
42
|
+
reasons << "the 'active_job' probe is enabled" if sk_rails_config.probes.include?("active_job")
|
|
43
|
+
reasons << "the 'delayed_job' probe is enabled" if sk_rails_config.probes.include?("delayed_job")
|
|
44
|
+
reasons << "SKYLIGHT_ENABLE_SIDEKIQ is set" if sk_config.enable_sidekiq?
|
|
45
|
+
|
|
46
|
+
return if reasons.empty?
|
|
47
|
+
|
|
48
|
+
sk_config.logger.warn("Activating Skylight for Background Jobs because #{reasons.to_sentence}")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def log_prefix
|
|
52
|
+
"[SKYLIGHT] [#{Skylight::VERSION}]"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def development_warning
|
|
56
|
+
"#{log_prefix} Running Skylight in development mode. No data will be reported until you deploy your app.\n" \
|
|
57
|
+
"(To disable this message for all local apps, run `skylight disable_dev_warning`.)"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def run_initializer(app)
|
|
61
|
+
# Load probes even when agent is inactive to catch probe related bugs sooner
|
|
62
|
+
load_probes
|
|
63
|
+
|
|
64
|
+
config = load_skylight_config(app)
|
|
65
|
+
|
|
66
|
+
if activate?(config)
|
|
67
|
+
if config
|
|
68
|
+
if Skylight.start!(config)
|
|
69
|
+
set_middleware_position(app, config)
|
|
70
|
+
Rails.logger.info "#{log_prefix} Skylight agent enabled"
|
|
71
|
+
else
|
|
72
|
+
Rails.logger.info "#{log_prefix} Unable to start, see the Skylight logs for more details"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
elsif Rails.env.development?
|
|
76
|
+
log_warning config, development_warning unless config.user_config.disable_dev_warning?
|
|
77
|
+
elsif !Rails.env.test?
|
|
78
|
+
unless config.user_config.disable_env_warning?
|
|
79
|
+
log_warning config,
|
|
80
|
+
"#{log_prefix} You are running in the #{Rails.env} environment but haven't added it " \
|
|
81
|
+
"to config.skylight.environments, so no data will be sent to Skylight servers."
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
rescue Skylight::ConfigError => e
|
|
85
|
+
Rails.logger.error "#{log_prefix} #{e.message}; disabling Skylight agent"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def log_warning(config, msg)
|
|
89
|
+
config ? config.alert_logger.warn(msg) : Rails.logger.warn(msg)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def existent_paths(paths)
|
|
93
|
+
paths.respond_to?(:existent) ? paths.existent : paths.select { |f| File.exist?(f) }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def load_skylight_config(app)
|
|
97
|
+
path = config_path(app)
|
|
98
|
+
path = nil unless File.exist?(path)
|
|
99
|
+
|
|
100
|
+
unless (tmp = app.config.paths["tmp"].first)
|
|
101
|
+
Rails.logger.error "#{log_prefix} tmp directory missing from rails configuration"
|
|
102
|
+
return nil
|
|
36
103
|
end
|
|
37
104
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
105
|
+
config = Config.load(file: path, priority_key: Rails.env.to_s)
|
|
106
|
+
config[:root] = Rails.root
|
|
107
|
+
|
|
108
|
+
configure_logging(config, app)
|
|
109
|
+
|
|
110
|
+
config[:"daemon.sockdir_path"] ||= tmp
|
|
111
|
+
config[:"normalizers.render.view_paths"] = existent_paths(app.config.paths["app/views"]) + [Rails.root.to_s]
|
|
112
|
+
|
|
113
|
+
config[:env] ||= Rails.env.to_s if config[:report_rails_env]
|
|
114
|
+
|
|
115
|
+
config
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def configure_logging(config, app)
|
|
119
|
+
if (logger = sk_rails_config(app).logger)
|
|
120
|
+
config.logger = logger
|
|
121
|
+
else
|
|
122
|
+
# Configure the log file destination
|
|
123
|
+
if (log_file = sk_rails_config(app).log_file)
|
|
124
|
+
config["log_file"] = log_file
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
if (native_log_file = sk_rails_config(app).native_log_file)
|
|
128
|
+
config["native_log_file"] = native_log_file
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
config["log_file"] = File.join(Rails.root, "log/skylight.log") if !config.key?("log_file") && !config.on_heroku?
|
|
132
|
+
|
|
133
|
+
# Configure the log level
|
|
134
|
+
if (level = sk_rails_config(app).log_level)
|
|
135
|
+
config["log_level"] = level
|
|
136
|
+
elsif !config.key?("log_level")
|
|
137
|
+
if (level = app.config.log_level)
|
|
138
|
+
config["log_level"] = level
|
|
42
139
|
end
|
|
43
140
|
end
|
|
44
141
|
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def config_path(app)
|
|
145
|
+
File.expand_path(sk_rails_config.config_path, app.root)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def environments
|
|
149
|
+
Array(sk_rails_config.environments).map { |e| e&.to_s }.compact
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def activate?(sk_config)
|
|
153
|
+
return false unless sk_config
|
|
154
|
+
|
|
155
|
+
key = "SKYLIGHT_ENABLED"
|
|
156
|
+
activate = ENV.key?(key) ? ENV.fetch(key, nil) !~ /^false$/i : environments.include?(Rails.env.to_s)
|
|
157
|
+
|
|
158
|
+
show_worker_activation_warning(sk_config) if activate
|
|
159
|
+
|
|
160
|
+
activate
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def load_probes
|
|
164
|
+
probes = sk_rails_config.probes || []
|
|
165
|
+
Skylight::Probes.probe(*probes)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def middleware_position
|
|
169
|
+
if sk_rails_config.middleware_position.is_a?(Hash)
|
|
170
|
+
sk_rails_config.middleware_position.symbolize_keys
|
|
171
|
+
else
|
|
172
|
+
sk_rails_config.middleware_position
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def insert_middleware(app, config)
|
|
177
|
+
if middleware_position.key?(:after)
|
|
178
|
+
app.middleware.insert_after(middleware_position[:after], Skylight::Middleware, config: config)
|
|
179
|
+
elsif middleware_position.key?(:before)
|
|
180
|
+
app.middleware.insert_before(middleware_position[:before], Skylight::Middleware, config: config)
|
|
181
|
+
else
|
|
182
|
+
raise "The middleware position you have set is invalid. Please be sure " \
|
|
183
|
+
"`config.skylight.middleware_position` is set up correctly."
|
|
184
|
+
end
|
|
185
|
+
end
|
|
45
186
|
|
|
187
|
+
def set_middleware_position(app, config)
|
|
188
|
+
if middleware_position.is_a?(Integer)
|
|
189
|
+
app.middleware.insert middleware_position, Skylight::Middleware, config: config
|
|
190
|
+
elsif middleware_position.is_a?(Hash) && middleware_position.keys.count == 1
|
|
191
|
+
insert_middleware(app, config)
|
|
192
|
+
elsif middleware_position.nil?
|
|
193
|
+
app.middleware.insert 0, Skylight::Middleware, config: config
|
|
194
|
+
else
|
|
195
|
+
raise "The middleware position you have set is invalid. Please be sure " \
|
|
196
|
+
"`config.skylight.middleware_position` is set up correctly."
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def sk_rails_config(target = self)
|
|
201
|
+
target.config.skylight
|
|
202
|
+
end
|
|
46
203
|
end
|
|
47
204
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Sidekiq
|
|
3
|
+
def self.add_middleware
|
|
4
|
+
unless defined?(::Sidekiq)
|
|
5
|
+
Skylight.warn "Skylight for Sidekiq is active, but Sidekiq is not defined."
|
|
6
|
+
return
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
::Sidekiq.configure_server do |sidekiq_config|
|
|
10
|
+
Skylight.debug "Adding Sidekiq Middleware"
|
|
11
|
+
|
|
12
|
+
sidekiq_config.server_middleware do |chain|
|
|
13
|
+
# Put it at the front
|
|
14
|
+
chain.prepend ServerMiddleware
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class ServerMiddleware
|
|
20
|
+
include Util::Logging
|
|
21
|
+
|
|
22
|
+
def call(worker, job, queue)
|
|
23
|
+
t { "Sidekiq middleware beginning trace" }
|
|
24
|
+
title = job["display_class"] || job["wrapped"] || job["class"]
|
|
25
|
+
|
|
26
|
+
# TODO: Using hints here would be ideal but requires further refactoring
|
|
27
|
+
meta =
|
|
28
|
+
if (source_location = worker.method(:perform).source_location)
|
|
29
|
+
{ source_file: source_location[0], source_line: source_location[1] }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Skylight.trace(title, "app.sidekiq.worker", title, meta: meta, segment: queue, component: :worker) do |trace|
|
|
33
|
+
yield
|
|
34
|
+
rescue Exception # includes Sidekiq::Shutdown
|
|
35
|
+
trace.segment = "error" if trace
|
|
36
|
+
raise
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
ActiveSupport::Notifications.subscribe(
|
|
42
|
+
"started_instrumenter.skylight"
|
|
43
|
+
) do |_name, _started, _finished, _unique_id, payload|
|
|
44
|
+
add_middleware if payload[:instrumenter].config.enable_sidekiq?
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
data/lib/skylight/sinatra.rb
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require
|
|
2
|
-
Skylight.probe(:sinatra_add_middleware, :sinatra, :tilt, :sequel)
|
|
1
|
+
require "skylight"
|
|
2
|
+
Skylight.probe(:sinatra_add_middleware, :sinatra, :tilt, :sequel, :rack_builder)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
# @api private
|
|
3
|
+
class Subscriber
|
|
4
|
+
include Util::Logging
|
|
5
|
+
|
|
6
|
+
attr_reader :config, :normalizers
|
|
7
|
+
|
|
8
|
+
def initialize(config, instrumenter)
|
|
9
|
+
@config = config
|
|
10
|
+
@normalizers = Normalizers.build(config)
|
|
11
|
+
@instrumenter = instrumenter
|
|
12
|
+
@subscribers = []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def register!
|
|
16
|
+
unregister!
|
|
17
|
+
@normalizers.each_key { |key| @subscribers << ActiveSupport::Notifications.subscribe(key, self) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def unregister!
|
|
21
|
+
ActiveSupport::Notifications.unsubscribe @subscribers.shift until @subscribers.empty?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
#
|
|
25
|
+
#
|
|
26
|
+
# ===== ActiveSupport::Notifications API
|
|
27
|
+
#
|
|
28
|
+
#
|
|
29
|
+
|
|
30
|
+
class Notification
|
|
31
|
+
attr_reader :name, :span
|
|
32
|
+
|
|
33
|
+
def initialize(name, span)
|
|
34
|
+
@name = name
|
|
35
|
+
@span = span
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# cargo-culted from Rails's ConnectionAdapter
|
|
40
|
+
EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
|
|
41
|
+
EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze # :nodoc:
|
|
42
|
+
private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
|
|
43
|
+
def with_trace(trace, &block) # :nodoc:
|
|
44
|
+
Thread.handle_interrupt(EXCEPTION_NEVER) do
|
|
45
|
+
previous_trace = @trace
|
|
46
|
+
@trace = trace
|
|
47
|
+
Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
|
|
48
|
+
ensure
|
|
49
|
+
@trace = previous_trace
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def start(name, _id, payload)
|
|
54
|
+
return if @instrumenter.disabled?
|
|
55
|
+
return unless (trace = current_trace)
|
|
56
|
+
|
|
57
|
+
_start(trace, name, payload)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def finish(name, _id, payload)
|
|
61
|
+
return if @instrumenter.disabled?
|
|
62
|
+
return unless (trace = current_trace)
|
|
63
|
+
|
|
64
|
+
while (curr = trace.notifications.pop)
|
|
65
|
+
next unless curr.name == name
|
|
66
|
+
|
|
67
|
+
meta = {}
|
|
68
|
+
meta[:exception] = payload[:exception] if payload[:exception]
|
|
69
|
+
meta[:exception_object] = payload[:exception_object] if payload[:exception_object]
|
|
70
|
+
trace.done(curr.span, meta) if curr.span
|
|
71
|
+
normalize_after(trace, curr.span, name, payload)
|
|
72
|
+
return
|
|
73
|
+
end
|
|
74
|
+
rescue Exception => e
|
|
75
|
+
error "Subscriber#finish error; msg=%s", e.message
|
|
76
|
+
debug "trace=%s", trace.inspect
|
|
77
|
+
debug "in: name=%s", name.inspect
|
|
78
|
+
debug "in: payload=%s", payload.inspect
|
|
79
|
+
t { e.backtrace.join("\n") }
|
|
80
|
+
nil
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def publish(name, *args)
|
|
84
|
+
# Ignored for now because nothing in rails uses it
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def publish_event(event)
|
|
88
|
+
# Ignored for now because only ActiveRecord::FutureResult uses it and we handle that with probes
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def current_trace
|
|
94
|
+
@trace || @instrumenter.current_trace
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def normalize(*args)
|
|
98
|
+
@normalizers.normalize(*args)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def normalize_after(*args)
|
|
102
|
+
@normalizers.normalize_after(*args)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def _start(trace, name, payload)
|
|
106
|
+
result = normalize(trace, name, payload)
|
|
107
|
+
|
|
108
|
+
unless result == :skip
|
|
109
|
+
case result.size
|
|
110
|
+
when 3, 4
|
|
111
|
+
cat, title, desc, meta = result
|
|
112
|
+
else
|
|
113
|
+
raise "Invalid normalizer result: #{result.inspect}"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
span = trace.instrument(cat, title, desc, meta)
|
|
117
|
+
end
|
|
118
|
+
rescue Exception => e
|
|
119
|
+
error "Subscriber#start error; msg=%s", e.message
|
|
120
|
+
debug "trace=%s", trace.inspect
|
|
121
|
+
debug "in: name=%s", name.inspect
|
|
122
|
+
debug "in: payload=%s", payload.inspect
|
|
123
|
+
debug "out: cat=%s, title=%s, desc=%s", cat.inspect, name.inspect, desc.inspect
|
|
124
|
+
t { e.backtrace.join("\n") }
|
|
125
|
+
nil
|
|
126
|
+
ensure
|
|
127
|
+
trace.notifications << Notification.new(name, span)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|