skylight 5.0.0.beta → 5.0.0
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 +26 -6
- data/CONTRIBUTING.md +1 -1
- data/ext/extconf.rb +2 -2
- data/ext/libskylight.yml +7 -5
- data/lib/skylight.rb +9 -2
- data/lib/skylight/api.rb +3 -0
- data/lib/skylight/cli/doctor.rb +11 -13
- data/lib/skylight/config.rb +25 -32
- data/lib/skylight/deprecation.rb +3 -1
- data/lib/skylight/errors.rb +4 -4
- data/lib/skylight/extensions.rb +8 -0
- data/lib/skylight/extensions/source_location.rb +123 -81
- data/lib/skylight/formatters/http.rb +2 -1
- data/lib/skylight/helpers.rb +44 -35
- data/lib/skylight/instrumenter.rb +3 -2
- data/lib/skylight/middleware.rb +4 -4
- data/lib/skylight/native.rb +1 -1
- data/lib/skylight/native_ext_fetcher.rb +2 -2
- data/lib/skylight/normalizers.rb +6 -4
- data/lib/skylight/normalizers/action_controller/process_action.rb +1 -1
- data/lib/skylight/normalizers/action_dispatch/route_set.rb +1 -1
- data/lib/skylight/normalizers/active_job/perform.rb +5 -0
- data/lib/skylight/normalizers/graphql/base.rb +1 -0
- data/lib/skylight/normalizers/render.rb +1 -1
- data/lib/skylight/normalizers/shrine.rb +34 -0
- data/lib/skylight/normalizers/sql.rb +3 -2
- data/lib/skylight/probes.rb +38 -10
- data/lib/skylight/probes/active_job.rb +4 -6
- data/lib/skylight/probes/active_job_enqueue.rb +18 -14
- data/lib/skylight/probes/active_model_serializers.rb +2 -6
- data/lib/skylight/probes/delayed_job.rb +112 -25
- data/lib/skylight/probes/elasticsearch.rb +1 -1
- data/lib/skylight/probes/excon/middleware.rb +4 -4
- data/lib/skylight/probes/middleware.rb +2 -1
- data/lib/skylight/probes/mongo.rb +2 -1
- data/lib/skylight/probes/net_http.rb +0 -1
- data/lib/skylight/probes/redis.rb +6 -3
- data/lib/skylight/railtie.rb +1 -1
- data/lib/skylight/sidekiq.rb +12 -7
- data/lib/skylight/subscriber.rb +1 -1
- data/lib/skylight/trace.rb +10 -4
- data/lib/skylight/util/deploy.rb +3 -6
- data/lib/skylight/util/instrumenter_method.rb +11 -11
- data/lib/skylight/util/logging.rb +6 -6
- data/lib/skylight/util/lru_cache.rb +1 -3
- data/lib/skylight/util/platform.rb +1 -1
- data/lib/skylight/version.rb +1 -1
- metadata +27 -13
- data/lib/skylight/fanout.rb +0 -0
@@ -12,7 +12,8 @@ module Skylight
|
|
12
12
|
# @return [Hash] a hash containing `:category`, `:title`, and `:annotations`
|
13
13
|
def self.build_opts(method, _scheme, host, _port, _path, _query)
|
14
14
|
{ category: "api.http.#{method.downcase}",
|
15
|
-
title: "#{method.upcase} #{host}"
|
15
|
+
title: "#{method.upcase} #{host}",
|
16
|
+
internal: true }
|
16
17
|
end
|
17
18
|
end
|
18
19
|
end
|
data/lib/skylight/helpers.rb
CHANGED
@@ -14,7 +14,7 @@ module Skylight
|
|
14
14
|
|
15
15
|
if (opts = @__sk_instrument_next_method)
|
16
16
|
@__sk_instrument_next_method = nil
|
17
|
-
instrument_method(name, opts)
|
17
|
+
instrument_method(name, **opts)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -24,7 +24,7 @@ module Skylight
|
|
24
24
|
|
25
25
|
if (opts = @__sk_instrument_next_method)
|
26
26
|
@__sk_instrument_next_method = nil
|
27
|
-
instrument_class_method(name, opts)
|
27
|
+
instrument_class_method(name, **opts)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -77,14 +77,12 @@ module Skylight
|
|
77
77
|
# do_expensive_stuff
|
78
78
|
# end
|
79
79
|
# end
|
80
|
-
def instrument_method(*args)
|
81
|
-
opts = args.pop if args.last.is_a?(Hash)
|
82
|
-
|
80
|
+
def instrument_method(*args, **opts)
|
83
81
|
if (name = args.pop)
|
84
82
|
title = "#{self}##{name}"
|
85
|
-
__sk_instrument_method_on(self, name, title, opts
|
83
|
+
__sk_instrument_method_on(self, name, title, **opts)
|
86
84
|
else
|
87
|
-
@__sk_instrument_next_method = opts
|
85
|
+
@__sk_instrument_next_method = opts
|
88
86
|
end
|
89
87
|
end
|
90
88
|
|
@@ -123,14 +121,18 @@ module Skylight
|
|
123
121
|
#
|
124
122
|
# instrument_class_method :my_method, title: 'Expensive work'
|
125
123
|
# end
|
126
|
-
def instrument_class_method(name, opts
|
124
|
+
def instrument_class_method(name, **opts)
|
125
|
+
# NOTE: If the class is defined anonymously and then assigned to a variable this code
|
126
|
+
# will not be aware of the updated name.
|
127
127
|
title = "#{self}.#{name}"
|
128
|
-
__sk_instrument_method_on(__sk_singleton_class, name, title, opts
|
128
|
+
__sk_instrument_method_on(__sk_singleton_class, name, title, **opts)
|
129
129
|
end
|
130
130
|
|
131
131
|
private
|
132
132
|
|
133
|
-
|
133
|
+
HAS_KW_ARGUMENT_FORWARDING = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
|
134
|
+
|
135
|
+
def __sk_instrument_method_on(klass, name, title, **opts)
|
134
136
|
category = (opts[:category] || "app.method").to_s
|
135
137
|
title = (opts[:title] || title).to_s
|
136
138
|
desc = opts[:description].to_s if opts[:description]
|
@@ -143,33 +145,40 @@ module Skylight
|
|
143
145
|
# source_file and source_line to be removed from the trace span before it is submitted.
|
144
146
|
source_file, source_line = klass.instance_method(name).source_location
|
145
147
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
category: :"#{category}",
|
152
|
-
title: #{title.inspect},
|
153
|
-
description: #{desc.inspect},
|
154
|
-
source_file: #{source_file.inspect},
|
155
|
-
source_line: #{source_line.inspect})
|
156
|
-
|
157
|
-
meta = {}
|
158
|
-
begin
|
159
|
-
send(:before_instrument_#{name}, *args, &blk)
|
160
|
-
rescue Exception => e
|
161
|
-
meta[:exception_object] = e
|
162
|
-
raise e
|
163
|
-
ensure
|
164
|
-
Skylight.done(span, meta) if span
|
165
|
-
end
|
148
|
+
arg_string =
|
149
|
+
if HAS_KW_ARGUMENT_FORWARDING
|
150
|
+
"*args, **kwargs, &blk"
|
151
|
+
else
|
152
|
+
"*args, &blk"
|
166
153
|
end
|
167
154
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
155
|
+
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
156
|
+
alias_method :"before_instrument_#{name}", :"#{name}" # alias_method :"before_instrument_process", :"process"
|
157
|
+
def #{name}(#{arg_string}) # def process(*args, **kwargs, &blk)
|
158
|
+
span = Skylight.instrument( # span = Skylight.instrument(
|
159
|
+
category: :"#{category}", # category: :"app.method",
|
160
|
+
title: #{title.inspect}, # title: "process",
|
161
|
+
description: #{desc.inspect}, # description: "Process data",
|
162
|
+
source_file: #{source_file.inspect}, # source_file: "myapp/lib/processor.rb",
|
163
|
+
source_line: #{source_line.inspect}) # source_line: 123)
|
164
|
+
#
|
165
|
+
meta = {} # meta = {}
|
166
|
+
#
|
167
|
+
begin # begin
|
168
|
+
send(:before_instrument_#{name}, #{arg_string}) # send(:before_instrument_process, *args, **kwargs, &blk)
|
169
|
+
rescue Exception => e # rescue Exception => e
|
170
|
+
meta[:exception_object] = e # meta[:exception_object] = e
|
171
|
+
raise e # raise e
|
172
|
+
ensure # ensure
|
173
|
+
Skylight.done(span, meta) if span # Skylight.done(span, meta) if span
|
174
|
+
end # end
|
175
|
+
end # end
|
176
|
+
#
|
177
|
+
if protected_method_defined?(:"before_instrument_#{name}") # if protected_method_defined?(:"before_instrument_process")
|
178
|
+
protected :"#{name}" # protected :"process"
|
179
|
+
elsif private_method_defined?(:"before_instrument_#{name}") # elsif private_method_defined?(:"before_instrument_process")
|
180
|
+
private :"#{name}" # private :"process"
|
181
|
+
end # end
|
173
182
|
RUBY
|
174
183
|
end
|
175
184
|
|
@@ -60,8 +60,6 @@ module Skylight
|
|
60
60
|
@trace_info = @config[:trace_info] || TraceInfo.new(KEY)
|
61
61
|
@mutex = Mutex.new
|
62
62
|
@extensions = Skylight::Extensions::Collection.new(@config)
|
63
|
-
|
64
|
-
enable_extension!(:source_location) if @config.enable_source_locations?
|
65
63
|
end
|
66
64
|
|
67
65
|
def enable_extension!(name)
|
@@ -176,6 +174,7 @@ module Skylight
|
|
176
174
|
return
|
177
175
|
end
|
178
176
|
|
177
|
+
enable_extension!(:source_location) if @config.enable_source_locations?
|
179
178
|
config.gc.enable
|
180
179
|
@subscriber.register!
|
181
180
|
|
@@ -202,6 +201,8 @@ module Skylight
|
|
202
201
|
end
|
203
202
|
|
204
203
|
begin
|
204
|
+
meta ||= {}
|
205
|
+
extensions.process_trace_meta(meta)
|
205
206
|
trace = Trace.new(self, endpoint, Skylight::Util::Clock.nanos, cat, title, desc,
|
206
207
|
meta: meta, segment: segment, component: component)
|
207
208
|
rescue Exception => e
|
data/lib/skylight/middleware.rb
CHANGED
@@ -10,10 +10,10 @@ module Skylight
|
|
10
10
|
@closed = false
|
11
11
|
end
|
12
12
|
|
13
|
-
def respond_to_missing?(
|
14
|
-
return false if
|
13
|
+
def respond_to_missing?(name, include_all = false)
|
14
|
+
return false if name.to_s !~ /^to_ary$/
|
15
15
|
|
16
|
-
@body.respond_to?(
|
16
|
+
@body.respond_to?(name, include_all)
|
17
17
|
end
|
18
18
|
|
19
19
|
def close
|
@@ -117,7 +117,7 @@ module Skylight
|
|
117
117
|
end
|
118
118
|
|
119
119
|
def endpoint_meta(_env)
|
120
|
-
|
120
|
+
{ source_location: Trace::SYNTHETIC }
|
121
121
|
end
|
122
122
|
|
123
123
|
# Request ID code based on ActionDispatch::RequestId
|
data/lib/skylight/native.rb
CHANGED
@@ -101,7 +101,7 @@ module Skylight
|
|
101
101
|
|
102
102
|
# @api private
|
103
103
|
def self.check_install_errors(config)
|
104
|
-
#
|
104
|
+
# NOTE: An unsupported arch doesn't count as an error.
|
105
105
|
install_log = File.expand_path("../../ext/install.log", __dir__)
|
106
106
|
|
107
107
|
if File.exist?(install_log) && File.read(install_log) =~ /ERROR/
|
@@ -23,7 +23,7 @@ module Skylight
|
|
23
23
|
# @param opts [Hash]
|
24
24
|
def self.fetch(**args)
|
25
25
|
args[:source] ||= BASE_URL
|
26
|
-
args[:logger] ||= Logger.new(
|
26
|
+
args[:logger] ||= Logger.new($stdout)
|
27
27
|
new(**args).fetch
|
28
28
|
end
|
29
29
|
|
@@ -35,7 +35,7 @@ module Skylight
|
|
35
35
|
# @param required [Boolean] whether the download is required to be successful
|
36
36
|
# @param platform
|
37
37
|
# @param log [Logger]
|
38
|
-
def initialize(source:, target:, version:, checksum:, arch:, required: false, platform: nil
|
38
|
+
def initialize(source:, target:, version:, checksum:, arch:, logger:, required: false, platform: nil)
|
39
39
|
raise "source required" unless source
|
40
40
|
raise "target required" unless target
|
41
41
|
raise "checksum required" unless checksum
|
data/lib/skylight/normalizers.rb
CHANGED
@@ -20,7 +20,7 @@ module Skylight
|
|
20
20
|
matches = registry.select { |n, _| n =~ /(^|\.)#{name}$/ }
|
21
21
|
raise ArgumentError, "no normalizers match #{name}" if matches.empty?
|
22
22
|
|
23
|
-
matches.
|
23
|
+
matches.each_value { |v| v[1] = enabled }
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -69,7 +69,8 @@ module Skylight
|
|
69
69
|
return cat if cat == :skip
|
70
70
|
|
71
71
|
meta ||= {}
|
72
|
-
|
72
|
+
cache_key = ret.hash
|
73
|
+
process_meta(trace, name, payload, meta, cache_key: cache_key)
|
73
74
|
|
74
75
|
[cat, title, desc, meta]
|
75
76
|
end
|
@@ -78,7 +79,7 @@ module Skylight
|
|
78
79
|
|
79
80
|
private
|
80
81
|
|
81
|
-
def process_meta(trace,
|
82
|
+
def process_meta(trace, _name, payload, meta, cache_key: nil)
|
82
83
|
trace.instrumenter.extensions.process_normalizer_meta(
|
83
84
|
payload,
|
84
85
|
meta,
|
@@ -144,7 +145,8 @@ module Skylight
|
|
144
145
|
graphiti/resolve
|
145
146
|
graphiti/render
|
146
147
|
graphql/base
|
147
|
-
sequel/sql
|
148
|
+
sequel/sql
|
149
|
+
shrine].each do |file|
|
148
150
|
require "skylight/normalizers/#{file}"
|
149
151
|
end
|
150
152
|
end
|
@@ -38,7 +38,7 @@ module Skylight
|
|
38
38
|
|
39
39
|
def process_meta_options(payload)
|
40
40
|
# provide hints to override default source_location behavior
|
41
|
-
super.merge(
|
41
|
+
super.merge(source_location_hint: [:instance_method, payload[:controller], payload[:action]])
|
42
42
|
end
|
43
43
|
|
44
44
|
def segment_from_payload(payload)
|
@@ -19,7 +19,7 @@ module Skylight
|
|
19
19
|
|
20
20
|
def process_meta_options(_payload)
|
21
21
|
# provide hints to override default source_location behavior
|
22
|
-
super.merge(
|
22
|
+
super.merge(source_location_hint: [:own_instance_method, router_class_name, "call"])
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
@@ -36,6 +36,11 @@ module Skylight
|
|
36
36
|
|
37
37
|
private
|
38
38
|
|
39
|
+
def process_meta_options(payload)
|
40
|
+
# provide hints to override default source_location behavior
|
41
|
+
super.merge(source_location_hint: [:instance_method, payload[:job].class.to_s, "perform"])
|
42
|
+
end
|
43
|
+
|
39
44
|
def normalize_adapter_name(adapter)
|
40
45
|
adapter_string = adapter.is_a?(Class) ? adapter.to_s : adapter.class.to_s
|
41
46
|
adapter_string[/ActiveJob::QueueAdapters::(\w+)Adapter/, 1].underscore
|
@@ -40,7 +40,7 @@ module Skylight
|
|
40
40
|
# Matches a Gem Version or 12-digit hex (sha)
|
41
41
|
# that is preceeded by a `-` and followed by `/`
|
42
42
|
# Also matches 'app/views/' if it exists
|
43
|
-
%r{-(?:#{Gem::Version::VERSION_PATTERN}|[0-9a-f]{12})
|
43
|
+
%r{-(?:#{Gem::Version::VERSION_PATTERN}|[0-9a-f]{12})/(?:app/views/)*},
|
44
44
|
": ".freeze
|
45
45
|
)
|
46
46
|
else
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Skylight
|
2
|
+
module Normalizers
|
3
|
+
class Shrine < Normalizer
|
4
|
+
TITLES = {
|
5
|
+
"upload.shrine" => "Upload",
|
6
|
+
"download.shrine" => "Download",
|
7
|
+
"open.shrine" => "Open",
|
8
|
+
"exists.shrine" => "Exists",
|
9
|
+
"delete.shrine" => "Delete",
|
10
|
+
"metadata.shrine" => "Metadata",
|
11
|
+
"mime_type.shrine" => "MIME Type",
|
12
|
+
"image_dimensions.shrine" => "Image Dimensions",
|
13
|
+
"signature.shrine" => "Signature",
|
14
|
+
"extension.shrine" => "Extension",
|
15
|
+
"derivation.shrine" => "Derivation",
|
16
|
+
"derivatives.shrine" => "Derivatives",
|
17
|
+
"data_uri.shrine" => "Data URI",
|
18
|
+
"remote_url.shrine" => "Remote URL"
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
TITLES.each_key do |key|
|
22
|
+
register key
|
23
|
+
end
|
24
|
+
|
25
|
+
def normalize(_trace, name, _payload)
|
26
|
+
title = ["Shrine", TITLES[name]].join(" ")
|
27
|
+
|
28
|
+
cat = "app.#{name.split('.').reverse.join('.')}"
|
29
|
+
|
30
|
+
[cat, title, nil]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -14,7 +14,7 @@ module Skylight
|
|
14
14
|
# @option payload [String] [:name] The SQL operation
|
15
15
|
# @option payload [Hash] [:binds] The bound parameters
|
16
16
|
# @return [Array]
|
17
|
-
def normalize(
|
17
|
+
def normalize(_trace, name, payload)
|
18
18
|
case payload[:name]
|
19
19
|
when "SCHEMA", "CACHE"
|
20
20
|
return :skip
|
@@ -29,7 +29,8 @@ module Skylight
|
|
29
29
|
|
30
30
|
unless sql.valid_encoding?
|
31
31
|
if config[:log_sql_parse_errors]
|
32
|
-
config.logger.error "[#{Skylight::SqlLexError.formatted_code}] Unable to extract binds from non-UTF-8
|
32
|
+
config.logger.error "[#{Skylight::SqlLexError.formatted_code}] Unable to extract binds from non-UTF-8 " \
|
33
|
+
"query. " \
|
33
34
|
"encoding=#{payload[:sql].encoding.name} " \
|
34
35
|
"sql=#{payload[:sql].inspect} "
|
35
36
|
end
|
data/lib/skylight/probes.rb
CHANGED
@@ -16,11 +16,41 @@ module Skylight
|
|
16
16
|
|
17
17
|
def install
|
18
18
|
probe.install
|
19
|
+
rescue StandardError, LoadError => e
|
20
|
+
log_install_exception(e)
|
19
21
|
end
|
20
22
|
|
21
23
|
def constant_available?
|
22
24
|
Skylight::Probes.constant_available?(const_name)
|
23
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
|
+
gems =
|
36
|
+
begin
|
37
|
+
Bundler.locked_gems.dependencies.map { |d| [d.name, d.requirement.to_s] }
|
38
|
+
rescue # rubocop:disable Lint/SuppressedException
|
39
|
+
end
|
40
|
+
|
41
|
+
error = "[SKYLIGHT] [#{Skylight::VERSION}] Encountered an error while installing the " \
|
42
|
+
"probe for #{const_name}. Please notify support@skylight.io with the debugging " \
|
43
|
+
"information below. It's recommended that you disable this probe until the " \
|
44
|
+
"issue is resolved." \
|
45
|
+
"\n\nERROR: #{description}\n\n#{backtrace}\n\n"
|
46
|
+
|
47
|
+
if gems
|
48
|
+
gems_string = gems.map { |g| " #{g[0]} #{g[1]}" }.join("\n")
|
49
|
+
error << "GEMS:\n\n#{gems_string}\n\n"
|
50
|
+
end
|
51
|
+
|
52
|
+
$stderr.puts(error)
|
53
|
+
end
|
24
54
|
end
|
25
55
|
|
26
56
|
class << self
|
@@ -99,12 +129,12 @@ module Skylight
|
|
99
129
|
def require_hook(require_path)
|
100
130
|
each_by_require_path(require_path) do |registration|
|
101
131
|
# Double check constant is available
|
102
|
-
|
103
|
-
install_probe(registration)
|
132
|
+
next unless registration.constant_available?
|
104
133
|
|
105
|
-
|
106
|
-
|
107
|
-
|
134
|
+
install_probe(registration)
|
135
|
+
|
136
|
+
# Don't need this to be called again
|
137
|
+
unregister_require_hook(registration)
|
108
138
|
end
|
109
139
|
end
|
110
140
|
|
@@ -143,11 +173,9 @@ module Kernel
|
|
143
173
|
|
144
174
|
def require(name)
|
145
175
|
require_without_sk(name).tap do
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
warn("[SKYLIGHT] Rescued exception in require hook", e)
|
150
|
-
end
|
176
|
+
Skylight::Probes.require_hook(name)
|
177
|
+
rescue Exception => e
|
178
|
+
warn("[SKYLIGHT] Rescued exception in require hook", e)
|
151
179
|
end
|
152
180
|
end
|
153
181
|
end
|
@@ -7,12 +7,10 @@ module Skylight
|
|
7
7
|
def execute(*)
|
8
8
|
Skylight.trace(TITLE, "app.job.execute", component: :worker) do |trace|
|
9
9
|
# See normalizers/active_job/perform for endpoint/segment assignment
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
raise
|
15
|
-
end
|
10
|
+
super
|
11
|
+
rescue Exception
|
12
|
+
trace.segment = "error" if trace
|
13
|
+
raise
|
16
14
|
end
|
17
15
|
end
|
18
16
|
end
|