skylight 4.3.2 → 5.1.1
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 +399 -336
- data/CLA.md +1 -1
- data/CONTRIBUTING.md +2 -8
- data/LICENSE.md +7 -17
- data/README.md +1 -1
- data/ext/extconf.rb +45 -56
- data/ext/libskylight.yml +10 -6
- data/ext/skylight_native.c +22 -99
- data/lib/skylight.rb +201 -14
- data/lib/skylight/api.rb +32 -21
- data/lib/skylight/cli.rb +48 -46
- data/lib/skylight/cli/doctor.rb +62 -63
- data/lib/skylight/cli/helpers.rb +19 -19
- data/lib/skylight/cli/merger.rb +142 -138
- data/lib/skylight/config.rb +634 -199
- data/lib/skylight/deprecation.rb +17 -0
- data/lib/skylight/errors.rb +23 -9
- data/lib/skylight/extensions.rb +95 -0
- data/lib/skylight/extensions/source_location.rb +291 -0
- data/lib/skylight/formatters/http.rb +18 -0
- data/lib/skylight/gc.rb +99 -0
- data/lib/skylight/helpers.rb +81 -36
- data/lib/skylight/instrumenter.rb +336 -18
- data/lib/skylight/middleware.rb +134 -1
- data/lib/skylight/native.rb +60 -12
- data/lib/skylight/native_ext_fetcher.rb +13 -14
- data/lib/skylight/normalizers.rb +157 -0
- 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 +90 -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 +12 -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 +45 -0
- data/lib/skylight/probes.rb +173 -0
- data/lib/skylight/probes/action_controller.rb +52 -0
- data/lib/skylight/probes/action_dispatch.rb +2 -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_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/delayed_job.rb +144 -0
- data/lib/skylight/probes/elasticsearch.rb +36 -0
- data/lib/skylight/probes/excon.rb +25 -0
- data/lib/skylight/probes/excon/middleware.rb +65 -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 +156 -0
- data/lib/skylight/probes/mongoid.rb +13 -0
- data/lib/skylight/probes/net_http.rb +54 -0
- data/lib/skylight/probes/redis.rb +51 -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/railtie.rb +157 -27
- data/lib/skylight/sidekiq.rb +47 -0
- data/lib/skylight/subscriber.rb +108 -0
- data/lib/skylight/test.rb +151 -0
- data/lib/skylight/trace.rb +325 -22
- data/lib/skylight/user_config.rb +58 -0
- data/lib/skylight/util.rb +12 -0
- data/lib/skylight/util/allocation_free.rb +26 -0
- data/lib/skylight/util/clock.rb +57 -0
- data/lib/skylight/util/component.rb +22 -22
- data/lib/skylight/util/deploy.rb +16 -21
- data/lib/skylight/util/gzip.rb +20 -0
- data/lib/skylight/util/http.rb +106 -113
- 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 +1 -5
- data/lib/skylight/util/ssl.rb +1 -25
- 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
- metadata +126 -13
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Skylight
|
|
2
|
+
module Formatters
|
|
3
|
+
module HTTP
|
|
4
|
+
# Build instrumentation options for HTTP queries
|
|
5
|
+
#
|
|
6
|
+
# @param [String] method HTTP method, e.g. get, post
|
|
7
|
+
# @param [String] scheme HTTP scheme, e.g. http, https
|
|
8
|
+
# @param [String] host Request host, e.g. example.com
|
|
9
|
+
# @param [String, Integer] port Request port
|
|
10
|
+
# @param [String] path Request path
|
|
11
|
+
# @param [String] query Request query string
|
|
12
|
+
# @return [Hash] a hash containing `:category`, `:title`, and `:annotations`
|
|
13
|
+
def self.build_opts(method, _scheme, host, _port, _path, _query)
|
|
14
|
+
{ category: "api.http.#{method.downcase}", title: "#{method.upcase} #{host}", internal: true }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/skylight/gc.rb
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
require "skylight/util/logging"
|
|
2
|
+
|
|
3
|
+
module Skylight
|
|
4
|
+
# @api private
|
|
5
|
+
class GC
|
|
6
|
+
METHODS = %i[enable total_time].freeze
|
|
7
|
+
TH_KEY = :SK_GC_CURR_WINDOW
|
|
8
|
+
MAX_COUNT = 1000
|
|
9
|
+
MAX_TIME = 30_000_000
|
|
10
|
+
|
|
11
|
+
include Util::Logging
|
|
12
|
+
|
|
13
|
+
attr_reader :config
|
|
14
|
+
|
|
15
|
+
def initialize(config, profiler)
|
|
16
|
+
@listeners = []
|
|
17
|
+
@config = config
|
|
18
|
+
@lock = Mutex.new
|
|
19
|
+
@time = 0
|
|
20
|
+
|
|
21
|
+
if METHODS.all? { |m| profiler.respond_to?(m) }
|
|
22
|
+
@profiler = profiler
|
|
23
|
+
@time = @profiler.total_time
|
|
24
|
+
else
|
|
25
|
+
debug "disabling GC profiling"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def enable
|
|
30
|
+
@profiler&.enable
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Total time in microseconds for GC over entire process lifetime
|
|
34
|
+
def total_time
|
|
35
|
+
@profiler ? @profiler.total_time : nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def track
|
|
39
|
+
if @profiler
|
|
40
|
+
win = Window.new(self)
|
|
41
|
+
|
|
42
|
+
@lock.synchronize do
|
|
43
|
+
__update
|
|
44
|
+
@listeners << win
|
|
45
|
+
|
|
46
|
+
# Cleanup any listeners that might have leaked
|
|
47
|
+
@listeners.shift until @listeners[0].time < MAX_TIME
|
|
48
|
+
|
|
49
|
+
@listeners.shift if @listeners.length > MAX_COUNT
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
win
|
|
53
|
+
else
|
|
54
|
+
Window.new(nil)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def release(win)
|
|
59
|
+
@lock.synchronize { @listeners.delete(win) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def update
|
|
63
|
+
@lock.synchronize { __update }
|
|
64
|
+
|
|
65
|
+
nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def __update
|
|
71
|
+
time = @profiler.total_time
|
|
72
|
+
diff = time - @time
|
|
73
|
+
@time = time
|
|
74
|
+
|
|
75
|
+
@listeners.each { |l| l.add(diff) } if diff > 0
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class Window
|
|
79
|
+
attr_reader :time
|
|
80
|
+
|
|
81
|
+
def initialize(global)
|
|
82
|
+
@global = global
|
|
83
|
+
@time = 0
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def update
|
|
87
|
+
@global&.update
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def add(time)
|
|
91
|
+
@time += time
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def release
|
|
95
|
+
@global&.release(self)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
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,47 +121,94 @@ 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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
133
|
+
HAS_ARGUMENT_FORWARDING = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
|
|
134
|
+
|
|
135
|
+
def __sk_instrument_method_on(klass, name, title, **opts)
|
|
136
|
+
category = (opts[:category] || "app.method").to_s
|
|
137
|
+
title = (opts[:title] || title).to_s
|
|
138
|
+
desc = opts[:description].to_s if opts[:description]
|
|
139
|
+
|
|
140
|
+
# NOTE: The source location logic happens before we have have a config so we can'
|
|
141
|
+
# check if source locations are enabled. However, it only happens once so the potential impact
|
|
142
|
+
# should be minimal. This would more appropriately belong to Extensions::SourceLocation,
|
|
143
|
+
# but as that is a runtime concern, and this happens at compile time, there isn't currently
|
|
144
|
+
# a clean way to turn this on and off. The absence of the extension will cause the
|
|
145
|
+
# source_file and source_line to be removed from the trace span before it is submitted.
|
|
146
|
+
source_file, source_line = klass.instance_method(name).source_location
|
|
137
147
|
|
|
138
|
-
|
|
139
|
-
|
|
148
|
+
# We should strongly prefer using the new argument-forwarding syntax (...) where available.
|
|
149
|
+
# In Ruby 2.7, the following are known to be syntax errors:
|
|
150
|
+
#
|
|
151
|
+
# - mixing positional arguments with argument forwarding (e.g., send(:method_name, ...))
|
|
152
|
+
# - calling a setter method with multiple arguments, unless dispatched via send or public_send.
|
|
153
|
+
#
|
|
154
|
+
# So it is possible, though not recommended, to define setter methods that take multiple arguments,
|
|
155
|
+
# keywords, and/or blocks. Unfortunately, this means that for setters, we still need to explicitly
|
|
156
|
+
# forward the different argument types.
|
|
157
|
+
is_setter_method = name.to_s.end_with?("=")
|
|
158
|
+
|
|
159
|
+
arg_string =
|
|
160
|
+
if HAS_ARGUMENT_FORWARDING
|
|
161
|
+
is_setter_method ? "*args, **kwargs, &blk" : "..."
|
|
162
|
+
else
|
|
163
|
+
"*args, &blk"
|
|
164
|
+
end
|
|
140
165
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
166
|
+
original_method_dispatch =
|
|
167
|
+
if is_setter_method
|
|
168
|
+
"self.send(:before_instrument_#{name}, #{arg_string})"
|
|
169
|
+
else
|
|
170
|
+
"before_instrument_#{name}(#{arg_string})"
|
|
171
|
+
end
|
|
146
172
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
173
|
+
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
174
|
+
alias_method :"before_instrument_#{name}", :"#{name}" # alias_method :"before_instrument_process", :"process"
|
|
175
|
+
def #{name}(#{arg_string}) # def process(*args, **kwargs, &blk)
|
|
176
|
+
span = Skylight.instrument( # span = Skylight.instrument(
|
|
177
|
+
category: :"#{category}", # category: :"app.method",
|
|
178
|
+
title: #{title.inspect}, # title: "process",
|
|
179
|
+
description: #{desc.inspect}, # description: "Process data",
|
|
180
|
+
source_file: #{source_file.inspect}, # source_file: "myapp/lib/processor.rb",
|
|
181
|
+
source_line: #{source_line.inspect}) # source_line: 123)
|
|
182
|
+
#
|
|
183
|
+
meta = {} # meta = {}
|
|
184
|
+
#
|
|
185
|
+
begin # begin
|
|
186
|
+
#{original_method_dispatch} # self.before_instrument_process(...)
|
|
187
|
+
rescue Exception => e # rescue Exception => e
|
|
188
|
+
meta[:exception_object] = e # meta[:exception_object] = e
|
|
189
|
+
raise e # raise e
|
|
190
|
+
ensure # ensure
|
|
191
|
+
Skylight.done(span, meta) if span # Skylight.done(span, meta) if span
|
|
192
|
+
end # end
|
|
193
|
+
end # end
|
|
194
|
+
#
|
|
195
|
+
if protected_method_defined?(:"before_instrument_#{name}") # if protected_method_defined?(:"before_instrument_process")
|
|
196
|
+
protected :"#{name}" # protected :"process"
|
|
197
|
+
elsif private_method_defined?(:"before_instrument_#{name}") # elsif private_method_defined?(:"before_instrument_process")
|
|
198
|
+
private :"#{name}" # private :"process"
|
|
199
|
+
end # end
|
|
157
200
|
RUBY
|
|
158
|
-
|
|
201
|
+
end
|
|
159
202
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
203
|
+
if respond_to?(:singleton_class)
|
|
204
|
+
alias __sk_singleton_class singleton_class
|
|
205
|
+
else
|
|
206
|
+
def __sk_singleton_class
|
|
207
|
+
class << self
|
|
208
|
+
self
|
|
165
209
|
end
|
|
166
210
|
end
|
|
211
|
+
end
|
|
167
212
|
end
|
|
168
213
|
|
|
169
214
|
# @api private
|
|
@@ -1,36 +1,354 @@
|
|
|
1
|
+
require "strscan"
|
|
2
|
+
require "securerandom"
|
|
3
|
+
require "skylight/util/logging"
|
|
4
|
+
require "skylight/extensions"
|
|
5
|
+
|
|
1
6
|
module Skylight
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
7
|
+
# @api private
|
|
8
|
+
class Instrumenter
|
|
9
|
+
KEY = :__skylight_current_trace
|
|
10
|
+
|
|
11
|
+
include Util::Logging
|
|
12
|
+
|
|
13
|
+
class TraceInfo
|
|
14
|
+
def initialize(key = KEY)
|
|
15
|
+
@key = key
|
|
16
|
+
@muted_key = "#{key}_muted"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def current
|
|
20
|
+
Thread.current[@key]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def current=(trace)
|
|
24
|
+
Thread.current[@key] = trace
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# NOTE: This should only be set by the instrumenter, and only
|
|
28
|
+
# in the context of a `mute` block. Do not try to turn this
|
|
29
|
+
# flag on and off directly.
|
|
30
|
+
def muted=(val)
|
|
31
|
+
Thread.current[@muted_key] = val
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def muted?
|
|
35
|
+
!!Thread.current[@muted_key]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
attr_reader :uuid, :config, :gc, :extensions
|
|
40
|
+
|
|
41
|
+
def self.native_new(_uuid, _config_env)
|
|
42
|
+
raise "not implemented"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.new(config)
|
|
46
|
+
config.validate!
|
|
47
|
+
|
|
48
|
+
uuid = SecureRandom.uuid
|
|
49
|
+
inst = native_new(uuid, config.to_native_env)
|
|
50
|
+
inst.send(:initialize, uuid, config)
|
|
51
|
+
inst
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def initialize(uuid, config)
|
|
55
|
+
@uuid = uuid
|
|
56
|
+
@gc = config.gc
|
|
57
|
+
@config = config
|
|
58
|
+
@subscriber = Skylight::Subscriber.new(config, self)
|
|
59
|
+
|
|
60
|
+
@trace_info = @config[:trace_info] || TraceInfo.new(KEY)
|
|
61
|
+
@mutex = Mutex.new
|
|
62
|
+
@extensions = Skylight::Extensions::Collection.new(@config)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def enable_extension!(name)
|
|
66
|
+
@mutex.synchronize { extensions.enable!(name) }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def disable_extension!(name)
|
|
70
|
+
@mutex.synchronize { extensions.disable!(name) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def extension_enabled?(name)
|
|
74
|
+
extensions.enabled?(name)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def log_context
|
|
78
|
+
@log_context ||= { inst: uuid }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def native_start
|
|
82
|
+
raise "not implemented"
|
|
5
83
|
end
|
|
6
84
|
|
|
7
|
-
def
|
|
85
|
+
def native_stop
|
|
86
|
+
raise "not implemented"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def native_track_desc(_endpoint, _description)
|
|
90
|
+
raise "not implemented"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def native_submit_trace(_trace)
|
|
94
|
+
raise "not implemented"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def current_trace
|
|
98
|
+
@trace_info.current
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def current_trace=(trace)
|
|
102
|
+
t { "setting current_trace=#{trace ? trace.uuid : "nil"}; thread=#{Thread.current.object_id}" }
|
|
103
|
+
@trace_info.current = trace
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def validate_installation
|
|
8
107
|
# Warn if there was an error installing Skylight.
|
|
9
108
|
|
|
10
|
-
if defined?(Skylight.check_install_errors)
|
|
11
|
-
Skylight.check_install_errors(config)
|
|
12
|
-
end
|
|
109
|
+
Skylight.check_install_errors(config) if defined?(Skylight.check_install_errors)
|
|
13
110
|
|
|
14
111
|
if !Skylight.native? && defined?(Skylight.warn_skylight_native_missing)
|
|
15
112
|
Skylight.warn_skylight_native_missing(config)
|
|
16
|
-
return
|
|
113
|
+
return false
|
|
17
114
|
end
|
|
115
|
+
|
|
116
|
+
true
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def muted=(val)
|
|
120
|
+
@trace_info.muted = val
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def muted?
|
|
124
|
+
@trace_info.muted?
|
|
18
125
|
end
|
|
19
126
|
|
|
20
|
-
def
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
127
|
+
def mute
|
|
128
|
+
old_muted = muted?
|
|
129
|
+
self.muted = true
|
|
130
|
+
yield if block_given?
|
|
131
|
+
ensure
|
|
132
|
+
self.muted = old_muted
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def unmute
|
|
136
|
+
old_muted = muted?
|
|
137
|
+
self.muted = false
|
|
138
|
+
yield if block_given?
|
|
139
|
+
ensure
|
|
140
|
+
self.muted = old_muted
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def silence_warnings(context)
|
|
144
|
+
@warnings_silenced || @mutex.synchronize { @warnings_silenced ||= {} }
|
|
145
|
+
|
|
146
|
+
@warnings_silenced[context] = true
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def warnings_silenced?(context)
|
|
150
|
+
@warnings_silenced && @warnings_silenced[context]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
alias disable mute
|
|
154
|
+
alias disabled? muted?
|
|
155
|
+
|
|
156
|
+
def start!
|
|
157
|
+
# We do this here since we can't report these issues via Gem install without stopping install entirely.
|
|
158
|
+
return unless validate_installation
|
|
159
|
+
|
|
160
|
+
t { "starting instrumenter" }
|
|
161
|
+
|
|
162
|
+
unless config.validate_with_server
|
|
163
|
+
log_error "invalid config"
|
|
164
|
+
return
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
t { "starting native instrumenter" }
|
|
168
|
+
unless native_start
|
|
169
|
+
warn "failed to start instrumenter"
|
|
170
|
+
return
|
|
27
171
|
end
|
|
172
|
+
|
|
173
|
+
enable_extension!(:source_location) if @config.enable_source_locations?
|
|
174
|
+
config.gc.enable
|
|
175
|
+
@subscriber.register!
|
|
176
|
+
|
|
177
|
+
ActiveSupport::Notifications.instrument("started_instrumenter.skylight", instrumenter: self)
|
|
178
|
+
|
|
179
|
+
self
|
|
180
|
+
rescue Exception => e
|
|
181
|
+
log_error "failed to start instrumenter; msg=%s; config=%s", e.message, config.inspect
|
|
182
|
+
t { e.backtrace.join("\n") }
|
|
28
183
|
nil
|
|
29
184
|
end
|
|
30
185
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
186
|
+
def shutdown
|
|
187
|
+
@subscriber.unregister!
|
|
188
|
+
native_stop
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def trace(endpoint, cat, title = nil, desc = nil, meta: nil, segment: nil, component: nil)
|
|
192
|
+
# If a trace is already in progress, continue with that one
|
|
193
|
+
if (trace = @trace_info.current)
|
|
194
|
+
return yield(trace) if block_given?
|
|
195
|
+
|
|
196
|
+
return trace
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
begin
|
|
200
|
+
meta ||= {}
|
|
201
|
+
extensions.process_trace_meta(meta)
|
|
202
|
+
trace =
|
|
203
|
+
Trace.new(
|
|
204
|
+
self,
|
|
205
|
+
endpoint,
|
|
206
|
+
Skylight::Util::Clock.nanos,
|
|
207
|
+
cat,
|
|
208
|
+
title,
|
|
209
|
+
desc,
|
|
210
|
+
meta: meta,
|
|
211
|
+
segment: segment,
|
|
212
|
+
component: component
|
|
213
|
+
)
|
|
214
|
+
rescue Exception => e
|
|
215
|
+
log_error e.message
|
|
216
|
+
t { e.backtrace.join("\n") }
|
|
217
|
+
return
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
@trace_info.current = trace
|
|
221
|
+
return trace unless block_given?
|
|
222
|
+
|
|
223
|
+
begin
|
|
224
|
+
yield trace
|
|
225
|
+
ensure
|
|
226
|
+
@trace_info.current = nil
|
|
227
|
+
t { "instrumenter submitting trace; trace=#{trace.uuid}" }
|
|
228
|
+
trace.submit
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def instrument(cat, title = nil, desc = nil, meta = nil)
|
|
233
|
+
raise ArgumentError, "cat is required" unless cat
|
|
234
|
+
|
|
235
|
+
if muted?
|
|
236
|
+
return yield if block_given?
|
|
237
|
+
|
|
238
|
+
return
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
unless (trace = @trace_info.current)
|
|
242
|
+
return yield if block_given?
|
|
243
|
+
|
|
244
|
+
return
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
cat = cat.to_s
|
|
248
|
+
|
|
249
|
+
unless Skylight::CATEGORY_REGEX.match?(cat)
|
|
250
|
+
warn "invalid skylight instrumentation category; trace=%s; value=%s", trace.uuid, cat
|
|
251
|
+
return yield if block_given?
|
|
252
|
+
|
|
253
|
+
return
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
cat = "other.#{cat}" unless Skylight::TIER_REGEX.match?(cat)
|
|
257
|
+
|
|
258
|
+
unless (sp = trace.instrument(cat, title, desc, meta))
|
|
259
|
+
return yield if block_given?
|
|
260
|
+
|
|
261
|
+
return
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
return sp unless block_given?
|
|
265
|
+
|
|
266
|
+
begin
|
|
267
|
+
yield sp
|
|
268
|
+
rescue Exception => e
|
|
269
|
+
meta ||= {}
|
|
270
|
+
meta[:exception] = [e.class.name, e.message]
|
|
271
|
+
meta[:exception_object] = e
|
|
272
|
+
raise e
|
|
273
|
+
ensure
|
|
274
|
+
trace.done(sp, meta)
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def broken!
|
|
279
|
+
return unless (trace = @trace_info.current)
|
|
280
|
+
|
|
281
|
+
trace.broken!
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def poison!
|
|
285
|
+
@poisoned = true
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def poisoned?
|
|
289
|
+
@poisoned
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def done(span, meta = nil)
|
|
293
|
+
return unless (trace = @trace_info.current)
|
|
294
|
+
|
|
295
|
+
trace.done(span, meta)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def process(trace)
|
|
299
|
+
t { fmt "processing trace=#{trace.uuid}" }
|
|
300
|
+
|
|
301
|
+
if ignore?(trace)
|
|
302
|
+
t { fmt "ignoring trace=#{trace.uuid}" }
|
|
303
|
+
return false
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
begin
|
|
307
|
+
finalize_endpoint_segment(trace)
|
|
308
|
+
native_submit_trace(trace)
|
|
309
|
+
true
|
|
310
|
+
rescue StandardError => e
|
|
311
|
+
handle_instrumenter_error(trace, e)
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def handle_instrumenter_error(trace, err)
|
|
316
|
+
poison! if err.is_a?(Skylight::InstrumenterUnrecoverableError)
|
|
317
|
+
|
|
318
|
+
warn "failed to submit trace to worker; trace=%s, err=%s", trace.uuid, err
|
|
319
|
+
t { "BACKTRACE:\n#{err.backtrace.join("\n")}" }
|
|
320
|
+
|
|
321
|
+
false
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def ignore?(trace)
|
|
325
|
+
config.ignored_endpoints.include?(trace.endpoint)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Because GraphQL can return multiple results, each of which
|
|
329
|
+
# may have their own success/error states, we need to set the
|
|
330
|
+
# skylight segment as follows:
|
|
331
|
+
#
|
|
332
|
+
# - when all queries have errors: "error"
|
|
333
|
+
# - when some queries have errors: "<rendered format>+error"
|
|
334
|
+
# - when no queries have errors: "<rendered format>"
|
|
335
|
+
#
|
|
336
|
+
# <rendered format> will be determined by the Rails controller as usual.
|
|
337
|
+
# See Instrumenter#finalize_endpoint_segment for the actual segment/error assignment.
|
|
338
|
+
def finalize_endpoint_segment(trace)
|
|
339
|
+
return unless (segment = trace.segment)
|
|
340
|
+
|
|
341
|
+
segment =
|
|
342
|
+
case trace.compound_response_error_status
|
|
343
|
+
when :all
|
|
344
|
+
"error"
|
|
345
|
+
when :partial
|
|
346
|
+
"#{segment}+error"
|
|
347
|
+
else
|
|
348
|
+
segment
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
trace.endpoint += "<sk-segment>#{segment}</sk-segment>"
|
|
34
352
|
end
|
|
35
353
|
end
|
|
36
354
|
end
|