skylight 4.3.2 → 5.0.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 +35 -3
- data/CONTRIBUTING.md +2 -8
- data/ext/extconf.rb +6 -5
- data/ext/libskylight.yml +7 -6
- data/ext/skylight_native.c +22 -99
- data/lib/skylight.rb +211 -14
- data/lib/skylight/api.rb +10 -3
- data/lib/skylight/cli.rb +4 -3
- data/lib/skylight/cli/doctor.rb +13 -14
- data/lib/skylight/cli/merger.rb +6 -4
- data/lib/skylight/config.rb +597 -127
- data/lib/skylight/deprecation.rb +17 -0
- data/lib/skylight/errors.rb +21 -6
- data/lib/skylight/extensions.rb +107 -0
- data/lib/skylight/extensions/source_location.rb +291 -0
- data/lib/skylight/formatters/http.rb +20 -0
- data/lib/skylight/gc.rb +109 -0
- data/lib/skylight/helpers.rb +69 -26
- data/lib/skylight/instrumenter.rb +326 -15
- data/lib/skylight/middleware.rb +138 -1
- data/lib/skylight/native.rb +52 -2
- data/lib/skylight/native_ext_fetcher.rb +4 -3
- data/lib/skylight/normalizers.rb +153 -0
- data/lib/skylight/normalizers/action_controller/process_action.rb +69 -0
- data/lib/skylight/normalizers/action_controller/send_file.rb +50 -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 +86 -0
- data/lib/skylight/normalizers/active_model_serializers/render.rb +28 -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 +30 -0
- data/lib/skylight/normalizers/active_support/cache.rb +22 -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 +46 -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 +32 -0
- data/lib/skylight/normalizers/elasticsearch/request.rb +20 -0
- data/lib/skylight/normalizers/faraday/request.rb +40 -0
- data/lib/skylight/normalizers/grape/endpoint.rb +34 -0
- data/lib/skylight/normalizers/grape/endpoint_render.rb +25 -0
- data/lib/skylight/normalizers/grape/endpoint_run.rb +41 -0
- data/lib/skylight/normalizers/grape/endpoint_run_filters.rb +22 -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 +132 -0
- data/lib/skylight/normalizers/render.rb +81 -0
- data/lib/skylight/normalizers/sequel/sql.rb +12 -0
- data/lib/skylight/normalizers/shrine.rb +34 -0
- data/lib/skylight/normalizers/sql.rb +45 -0
- data/lib/skylight/probes.rb +181 -0
- data/lib/skylight/probes/action_controller.rb +48 -0
- data/lib/skylight/probes/action_dispatch.rb +2 -0
- data/lib/skylight/probes/action_dispatch/request_id.rb +29 -0
- data/lib/skylight/probes/action_dispatch/routing/route_set.rb +28 -0
- data/lib/skylight/probes/action_view.rb +43 -0
- data/lib/skylight/probes/active_job.rb +27 -0
- data/lib/skylight/probes/active_job_enqueue.rb +41 -0
- data/lib/skylight/probes/active_model_serializers.rb +50 -0
- data/lib/skylight/probes/delayed_job.rb +149 -0
- data/lib/skylight/probes/elasticsearch.rb +38 -0
- data/lib/skylight/probes/excon.rb +25 -0
- data/lib/skylight/probes/excon/middleware.rb +66 -0
- data/lib/skylight/probes/faraday.rb +23 -0
- data/lib/skylight/probes/graphql.rb +43 -0
- data/lib/skylight/probes/httpclient.rb +44 -0
- data/lib/skylight/probes/middleware.rb +126 -0
- data/lib/skylight/probes/mongo.rb +164 -0
- data/lib/skylight/probes/mongoid.rb +13 -0
- data/lib/skylight/probes/net_http.rb +54 -0
- data/lib/skylight/probes/redis.rb +63 -0
- data/lib/skylight/probes/sequel.rb +33 -0
- data/lib/skylight/probes/sinatra.rb +63 -0
- data/lib/skylight/probes/sinatra_add_middleware.rb +10 -10
- data/lib/skylight/probes/tilt.rb +27 -0
- data/lib/skylight/railtie.rb +162 -18
- data/lib/skylight/sidekiq.rb +48 -0
- data/lib/skylight/subscriber.rb +110 -0
- data/lib/skylight/test.rb +146 -0
- data/lib/skylight/trace.rb +307 -10
- data/lib/skylight/user_config.rb +61 -0
- data/lib/skylight/util.rb +12 -0
- data/lib/skylight/util/allocation_free.rb +26 -0
- data/lib/skylight/util/clock.rb +56 -0
- data/lib/skylight/util/component.rb +5 -2
- data/lib/skylight/util/deploy.rb +7 -10
- data/lib/skylight/util/gzip.rb +20 -0
- data/lib/skylight/util/http.rb +4 -10
- data/lib/skylight/util/instrumenter_method.rb +26 -0
- data/lib/skylight/util/logging.rb +138 -0
- data/lib/skylight/util/lru_cache.rb +40 -0
- data/lib/skylight/util/platform.rb +1 -1
- data/lib/skylight/vendor/cli/thor/rake_compat.rb +1 -1
- data/lib/skylight/version.rb +5 -1
- data/lib/skylight/vm/gc.rb +68 -0
- metadata +126 -13
|
@@ -0,0 +1,20 @@
|
|
|
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}",
|
|
15
|
+
title: "#{method.upcase} #{host}",
|
|
16
|
+
internal: true }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/skylight/gc.rb
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
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
|
+
if @listeners.length > MAX_COUNT
|
|
50
|
+
@listeners.shift
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
win
|
|
55
|
+
else
|
|
56
|
+
Window.new(nil)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def release(win)
|
|
61
|
+
@lock.synchronize do
|
|
62
|
+
@listeners.delete(win)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def update
|
|
67
|
+
@lock.synchronize do
|
|
68
|
+
__update
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def __update
|
|
77
|
+
time = @profiler.total_time
|
|
78
|
+
diff = time - @time
|
|
79
|
+
@time = time
|
|
80
|
+
|
|
81
|
+
if diff > 0
|
|
82
|
+
@listeners.each do |l|
|
|
83
|
+
l.add(diff)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class Window
|
|
89
|
+
attr_reader :time
|
|
90
|
+
|
|
91
|
+
def initialize(global)
|
|
92
|
+
@global = global
|
|
93
|
+
@time = 0
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def update
|
|
97
|
+
@global&.update
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def add(time)
|
|
101
|
+
@time += time
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def release
|
|
105
|
+
@global&.release(self)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
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,37 +121,82 @@ 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_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]
|
|
137
139
|
|
|
138
|
-
|
|
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
|
|
140
147
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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?("=")
|
|
146
158
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
raise e
|
|
153
|
-
ensure
|
|
154
|
-
Skylight.done(span, meta) if span
|
|
155
|
-
end
|
|
159
|
+
arg_string =
|
|
160
|
+
if HAS_ARGUMENT_FORWARDING
|
|
161
|
+
is_setter_method ? "*args, **kwargs, &blk" : "..."
|
|
162
|
+
else
|
|
163
|
+
"*args, &blk"
|
|
156
164
|
end
|
|
165
|
+
|
|
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
|
|
172
|
+
|
|
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
|
|
|
@@ -1,10 +1,109 @@
|
|
|
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
109
|
if defined?(Skylight.check_install_errors)
|
|
@@ -13,24 +112,236 @@ module Skylight
|
|
|
13
112
|
|
|
14
113
|
if !Skylight.native? && defined?(Skylight.warn_skylight_native_missing)
|
|
15
114
|
Skylight.warn_skylight_native_missing(config)
|
|
16
|
-
return
|
|
115
|
+
return false
|
|
17
116
|
end
|
|
117
|
+
|
|
118
|
+
true
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def muted=(val)
|
|
122
|
+
@trace_info.muted = val
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def muted?
|
|
126
|
+
@trace_info.muted?
|
|
18
127
|
end
|
|
19
128
|
|
|
20
|
-
def
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
129
|
+
def mute
|
|
130
|
+
old_muted = muted?
|
|
131
|
+
self.muted = true
|
|
132
|
+
yield if block_given?
|
|
133
|
+
ensure
|
|
134
|
+
self.muted = old_muted
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def unmute
|
|
138
|
+
old_muted = muted?
|
|
139
|
+
self.muted = false
|
|
140
|
+
yield if block_given?
|
|
141
|
+
ensure
|
|
142
|
+
self.muted = old_muted
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def silence_warnings(context)
|
|
146
|
+
@warnings_silenced || @mutex.synchronize do
|
|
147
|
+
@warnings_silenced ||= {}
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
@warnings_silenced[context] = true
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def warnings_silenced?(context)
|
|
154
|
+
@warnings_silenced && @warnings_silenced[context]
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
alias disable mute
|
|
158
|
+
alias disabled? muted?
|
|
159
|
+
|
|
160
|
+
def start!
|
|
161
|
+
# We do this here since we can't report these issues via Gem install without stopping install entirely.
|
|
162
|
+
return unless validate_installation
|
|
163
|
+
|
|
164
|
+
t { "starting instrumenter" }
|
|
165
|
+
|
|
166
|
+
unless config.validate_with_server
|
|
167
|
+
log_error "invalid config"
|
|
168
|
+
return
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
t { "starting native instrumenter" }
|
|
172
|
+
unless native_start
|
|
173
|
+
warn "failed to start instrumenter"
|
|
174
|
+
return
|
|
27
175
|
end
|
|
176
|
+
|
|
177
|
+
enable_extension!(:source_location) if @config.enable_source_locations?
|
|
178
|
+
config.gc.enable
|
|
179
|
+
@subscriber.register!
|
|
180
|
+
|
|
181
|
+
ActiveSupport::Notifications.instrument("started_instrumenter.skylight", instrumenter: self)
|
|
182
|
+
|
|
183
|
+
self
|
|
184
|
+
rescue Exception => e
|
|
185
|
+
log_error "failed to start instrumenter; msg=%s; config=%s", e.message, config.inspect
|
|
186
|
+
t { e.backtrace.join("\n") }
|
|
28
187
|
nil
|
|
29
188
|
end
|
|
30
189
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
190
|
+
def shutdown
|
|
191
|
+
@subscriber.unregister!
|
|
192
|
+
native_stop
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def trace(endpoint, cat, title = nil, desc = nil, meta: nil, segment: nil, component: nil)
|
|
196
|
+
# If a trace is already in progress, continue with that one
|
|
197
|
+
if (trace = @trace_info.current)
|
|
198
|
+
return yield(trace) if block_given?
|
|
199
|
+
|
|
200
|
+
return trace
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
begin
|
|
204
|
+
meta ||= {}
|
|
205
|
+
extensions.process_trace_meta(meta)
|
|
206
|
+
trace = Trace.new(self, endpoint, Skylight::Util::Clock.nanos, cat, title, desc,
|
|
207
|
+
meta: meta, segment: segment, component: component)
|
|
208
|
+
rescue Exception => e
|
|
209
|
+
log_error e.message
|
|
210
|
+
t { e.backtrace.join("\n") }
|
|
211
|
+
return
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
@trace_info.current = trace
|
|
215
|
+
return trace unless block_given?
|
|
216
|
+
|
|
217
|
+
begin
|
|
218
|
+
yield trace
|
|
219
|
+
ensure
|
|
220
|
+
@trace_info.current = nil
|
|
221
|
+
t { "instrumenter submitting trace; trace=#{trace.uuid}" }
|
|
222
|
+
trace.submit
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def instrument(cat, title = nil, desc = nil, meta = nil)
|
|
227
|
+
raise ArgumentError, "cat is required" unless cat
|
|
228
|
+
|
|
229
|
+
if muted?
|
|
230
|
+
return yield if block_given?
|
|
231
|
+
|
|
232
|
+
return
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
unless (trace = @trace_info.current)
|
|
236
|
+
return yield if block_given?
|
|
237
|
+
|
|
238
|
+
return
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
cat = cat.to_s
|
|
242
|
+
|
|
243
|
+
unless Skylight::CATEGORY_REGEX.match?(cat)
|
|
244
|
+
warn "invalid skylight instrumentation category; trace=%s; value=%s", trace.uuid, cat
|
|
245
|
+
return yield if block_given?
|
|
246
|
+
|
|
247
|
+
return
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
cat = "other.#{cat}" unless Skylight::TIER_REGEX.match?(cat)
|
|
251
|
+
|
|
252
|
+
unless (sp = trace.instrument(cat, title, desc, meta))
|
|
253
|
+
return yield if block_given?
|
|
254
|
+
|
|
255
|
+
return
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
return sp unless block_given?
|
|
259
|
+
|
|
260
|
+
begin
|
|
261
|
+
yield sp
|
|
262
|
+
rescue Exception => e
|
|
263
|
+
meta ||= {}
|
|
264
|
+
meta[:exception] = [e.class.name, e.message]
|
|
265
|
+
meta[:exception_object] = e
|
|
266
|
+
raise e
|
|
267
|
+
ensure
|
|
268
|
+
trace.done(sp, meta)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def broken!
|
|
273
|
+
return unless (trace = @trace_info.current)
|
|
274
|
+
|
|
275
|
+
trace.broken!
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def poison!
|
|
279
|
+
@poisoned = true
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def poisoned?
|
|
283
|
+
@poisoned
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def done(span, meta = nil)
|
|
287
|
+
return unless (trace = @trace_info.current)
|
|
288
|
+
|
|
289
|
+
trace.done(span, meta)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def process(trace)
|
|
293
|
+
t { fmt "processing trace=#{trace.uuid}" }
|
|
294
|
+
|
|
295
|
+
if ignore?(trace)
|
|
296
|
+
t { fmt "ignoring trace=#{trace.uuid}" }
|
|
297
|
+
return false
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
begin
|
|
301
|
+
finalize_endpoint_segment(trace)
|
|
302
|
+
native_submit_trace(trace)
|
|
303
|
+
true
|
|
304
|
+
rescue => e
|
|
305
|
+
handle_instrumenter_error(trace, e)
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def handle_instrumenter_error(trace, err)
|
|
310
|
+
poison! if err.is_a?(Skylight::InstrumenterUnrecoverableError)
|
|
311
|
+
|
|
312
|
+
warn "failed to submit trace to worker; trace=%s, err=%s", trace.uuid, err
|
|
313
|
+
t { "BACKTRACE:\n#{err.backtrace.join("\n")}" }
|
|
314
|
+
|
|
315
|
+
false
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def ignore?(trace)
|
|
319
|
+
config.ignored_endpoints.include?(trace.endpoint)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Because GraphQL can return multiple results, each of which
|
|
323
|
+
# may have their own success/error states, we need to set the
|
|
324
|
+
# skylight segment as follows:
|
|
325
|
+
#
|
|
326
|
+
# - when all queries have errors: "error"
|
|
327
|
+
# - when some queries have errors: "<rendered format>+error"
|
|
328
|
+
# - when no queries have errors: "<rendered format>"
|
|
329
|
+
#
|
|
330
|
+
# <rendered format> will be determined by the Rails controller as usual.
|
|
331
|
+
# See Instrumenter#finalize_endpoint_segment for the actual segment/error assignment.
|
|
332
|
+
def finalize_endpoint_segment(trace)
|
|
333
|
+
return unless (segment = trace.segment)
|
|
334
|
+
|
|
335
|
+
segment = case trace.compound_response_error_status
|
|
336
|
+
when :all
|
|
337
|
+
"error"
|
|
338
|
+
when :partial
|
|
339
|
+
"#{segment}+error"
|
|
340
|
+
else
|
|
341
|
+
segment
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
trace.endpoint += "<sk-segment>#{segment}</sk-segment>"
|
|
34
345
|
end
|
|
35
346
|
end
|
|
36
347
|
end
|