skylight-core 4.1.2 → 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/skylight/core/config.rb +6 -0
- data/lib/skylight/core/instrumentable.rb +43 -3
- data/lib/skylight/core/instrumenter.rb +97 -19
- data/lib/skylight/core/middleware.rb +20 -6
- data/lib/skylight/core/normalizers/active_job/perform.rb +2 -2
- data/lib/skylight/core/normalizers/grape/endpoint.rb +2 -1
- data/lib/skylight/core/normalizers/graphql/base.rb +131 -0
- data/lib/skylight/core/normalizers.rb +1 -0
- data/lib/skylight/core/probes/action_controller.rb +16 -11
- data/lib/skylight/core/probes/active_job.rb +1 -1
- data/lib/skylight/core/probes/delayed_job.rb +1 -1
- data/lib/skylight/core/probes/graphql.rb +41 -0
- data/lib/skylight/core/probes/middleware.rb +1 -1
- data/lib/skylight/core/probes/net_http.rb +2 -1
- data/lib/skylight/core/probes/sinatra.rb +12 -2
- data/lib/skylight/core/sidekiq.rb +6 -1
- data/lib/skylight/core/subscriber.rb +6 -8
- data/lib/skylight/core/trace.rb +52 -8
- data/lib/skylight/core/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b967f11a876f41f4337a88fce73301993cf3160f862186b64a7d03a931b5ccb6
|
4
|
+
data.tar.gz: b33c7c1ecbfab7545ea7efdbb691371bc35a0f90ef786c8fb8a60e3db9f7a8a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0285603cf5bdd82f8cfa8c9e0f3c54569b8e46051ccd16926e14498e024e5b92c9432b150422285625eee6b359aa46c036e483bda9a0cd7cf1dfd94edcc0d530'
|
7
|
+
data.tar.gz: 27fdc7636b44ddaece0b205e425a6546b5ef39ad0671a1719983b8133825940bded7c711abf5631eab83326c79fbd20adb0925c41aa934155670487cc5c4bb7a
|
data/lib/skylight/core/config.rb
CHANGED
@@ -36,6 +36,7 @@ module Skylight::Core
|
|
36
36
|
# == Instrumenter ==
|
37
37
|
"ENABLE_SEGMENTS" => :enable_segments,
|
38
38
|
"ENABLE_SIDEKIQ" => :enable_sidekiq,
|
39
|
+
"SINATRA_ROUTE_PREFIXES" => :sinatra_route_prefixes,
|
39
40
|
|
40
41
|
# == User config settings ==
|
41
42
|
"USER_CONFIG_PATH" => :user_config_path,
|
@@ -54,6 +55,7 @@ module Skylight::Core
|
|
54
55
|
log_sql_parse_errors: true,
|
55
56
|
enable_segments: true,
|
56
57
|
enable_sidekiq: false,
|
58
|
+
sinatra_route_prefixes: false,
|
57
59
|
'heroku.dyno_info_path': "/etc/heroku/dyno"
|
58
60
|
}
|
59
61
|
end
|
@@ -411,6 +413,10 @@ module Skylight::Core
|
|
411
413
|
!!get(:enable_sidekiq)
|
412
414
|
end
|
413
415
|
|
416
|
+
def sinatra_route_prefixes?
|
417
|
+
!!get(:sinatra_route_prefixes)
|
418
|
+
end
|
419
|
+
|
414
420
|
def user_config
|
415
421
|
@user_config ||= UserConfig.new(self)
|
416
422
|
end
|
@@ -92,18 +92,24 @@ module Skylight
|
|
92
92
|
end
|
93
93
|
|
94
94
|
# Start a trace
|
95
|
-
def trace(endpoint = nil, cat = nil, title = nil, meta: nil, segment: nil)
|
95
|
+
def trace(endpoint = nil, cat = nil, title = nil, meta: nil, segment: nil, component: nil)
|
96
96
|
unless instrumenter
|
97
97
|
return yield if block_given?
|
98
98
|
return
|
99
99
|
end
|
100
100
|
|
101
|
+
if instrumenter.poisoned?
|
102
|
+
spawn_shutdown_thread!
|
103
|
+
return yield if block_given?
|
104
|
+
return
|
105
|
+
end
|
106
|
+
|
101
107
|
cat ||= DEFAULT_CATEGORY
|
102
108
|
|
103
109
|
if block_given?
|
104
|
-
instrumenter.trace(endpoint, cat, title, nil, meta: meta, segment: segment) { |tr| yield tr }
|
110
|
+
instrumenter.trace(endpoint, cat, title, nil, meta: meta, segment: segment, component: component) { |tr| yield tr }
|
105
111
|
else
|
106
|
-
instrumenter.trace(endpoint, cat, title, nil, meta: meta, segment: segment)
|
112
|
+
instrumenter.trace(endpoint, cat, title, nil, meta: meta, segment: segment, component: component)
|
107
113
|
end
|
108
114
|
end
|
109
115
|
|
@@ -132,6 +138,32 @@ module Skylight
|
|
132
138
|
instrumenter.instrument(category, title, desc, meta, &block)
|
133
139
|
end
|
134
140
|
|
141
|
+
def mute
|
142
|
+
unless instrumenter
|
143
|
+
return yield if block_given?
|
144
|
+
return
|
145
|
+
end
|
146
|
+
|
147
|
+
instrumenter.mute do
|
148
|
+
yield if block_given?
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def unmute
|
153
|
+
unless instrumenter
|
154
|
+
return yield if block_given?
|
155
|
+
return
|
156
|
+
end
|
157
|
+
|
158
|
+
instrumenter.unmute do
|
159
|
+
yield if block_given?
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def muted?
|
164
|
+
instrumenter&.muted?
|
165
|
+
end
|
166
|
+
|
135
167
|
def span_correlation_header(span)
|
136
168
|
return unless instrumenter
|
137
169
|
instrumenter.span_correlation_header(span)
|
@@ -162,6 +194,14 @@ module Skylight
|
|
162
194
|
return unless instrumenter
|
163
195
|
instrumenter.config
|
164
196
|
end
|
197
|
+
|
198
|
+
# Runs the shutdown procedure in the background.
|
199
|
+
# This should do little more than unsubscribe from all ActiveSupport::Notifications
|
200
|
+
def spawn_shutdown_thread!
|
201
|
+
@shutdown_thread || const_get(:LOCK).synchronize do
|
202
|
+
@shutdown_thread ||= Thread.new { @instrumenter&.shutdown }
|
203
|
+
end
|
204
|
+
end
|
165
205
|
end
|
166
206
|
end
|
167
207
|
end
|
@@ -13,6 +13,7 @@ module Skylight::Core
|
|
13
13
|
class TraceInfo
|
14
14
|
def initialize(key = KEY)
|
15
15
|
@key = key
|
16
|
+
@muted_key = "#{key}_muted"
|
16
17
|
end
|
17
18
|
|
18
19
|
def current
|
@@ -22,9 +23,20 @@ module Skylight::Core
|
|
22
23
|
def current=(trace)
|
23
24
|
Thread.current[@key] = trace
|
24
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
|
25
37
|
end
|
26
38
|
|
27
|
-
attr_reader :uuid, :config, :gc
|
39
|
+
attr_reader :uuid, :config, :gc
|
28
40
|
|
29
41
|
def self.trace_class
|
30
42
|
Trace
|
@@ -51,6 +63,7 @@ module Skylight::Core
|
|
51
63
|
|
52
64
|
key = "#{KEY}_#{self.class.trace_class.name}".gsub(/\W/, "_")
|
53
65
|
@trace_info = @config[:trace_info] || TraceInfo.new(key)
|
66
|
+
@mutex = Mutex.new
|
54
67
|
end
|
55
68
|
|
56
69
|
def log_context
|
@@ -86,6 +99,45 @@ module Skylight::Core
|
|
86
99
|
true
|
87
100
|
end
|
88
101
|
|
102
|
+
def muted=(val)
|
103
|
+
@trace_info.muted = val
|
104
|
+
end
|
105
|
+
|
106
|
+
def muted?
|
107
|
+
@trace_info.muted?
|
108
|
+
end
|
109
|
+
|
110
|
+
def mute
|
111
|
+
old_muted = muted?
|
112
|
+
self.muted = true
|
113
|
+
yield if block_given?
|
114
|
+
ensure
|
115
|
+
self.muted = old_muted
|
116
|
+
end
|
117
|
+
|
118
|
+
def unmute
|
119
|
+
old_muted = muted?
|
120
|
+
self.muted = false
|
121
|
+
yield if block_given?
|
122
|
+
ensure
|
123
|
+
self.muted = old_muted
|
124
|
+
end
|
125
|
+
|
126
|
+
def silence_warnings(context)
|
127
|
+
@warnings_silenced || @mutex.synchronize do
|
128
|
+
@warnings_silenced ||= {}
|
129
|
+
end
|
130
|
+
|
131
|
+
@warnings_silenced[context] = true
|
132
|
+
end
|
133
|
+
|
134
|
+
def warnings_silenced?(context)
|
135
|
+
@warnings_silenced && @warnings_silenced[context]
|
136
|
+
end
|
137
|
+
|
138
|
+
alias_method :disable, :mute
|
139
|
+
alias_method :disabled?, :muted?
|
140
|
+
|
89
141
|
def start!
|
90
142
|
# We do this here since we can't report these issues via Gem install without stopping install entirely.
|
91
143
|
check_install!
|
@@ -120,7 +172,7 @@ module Skylight::Core
|
|
120
172
|
native_stop
|
121
173
|
end
|
122
174
|
|
123
|
-
def trace(endpoint, cat, title = nil, desc = nil, meta: nil, segment: nil)
|
175
|
+
def trace(endpoint, cat, title = nil, desc = nil, meta: nil, segment: nil, component: nil)
|
124
176
|
# If a trace is already in progress, continue with that one
|
125
177
|
if (trace = @trace_info.current)
|
126
178
|
return yield(trace) if block_given?
|
@@ -128,7 +180,7 @@ module Skylight::Core
|
|
128
180
|
end
|
129
181
|
|
130
182
|
begin
|
131
|
-
trace = self.class.trace_class.new(self, endpoint, Util::Clock.nanos, cat, title, desc, meta: meta, segment: segment)
|
183
|
+
trace = self.class.trace_class.new(self, endpoint, Util::Clock.nanos, cat, title, desc, meta: meta, segment: segment, component: component)
|
132
184
|
rescue Exception => e
|
133
185
|
log_error e.message
|
134
186
|
t { e.backtrace.join("\n") }
|
@@ -147,17 +199,6 @@ module Skylight::Core
|
|
147
199
|
end
|
148
200
|
end
|
149
201
|
|
150
|
-
def disable
|
151
|
-
@disabled = true
|
152
|
-
yield
|
153
|
-
ensure
|
154
|
-
@disabled = false
|
155
|
-
end
|
156
|
-
|
157
|
-
def disabled?
|
158
|
-
defined?(@disabled) && @disabled
|
159
|
-
end
|
160
|
-
|
161
202
|
def self.match?(string, regex)
|
162
203
|
@scanner ||= StringScanner.new("")
|
163
204
|
@scanner.string = string
|
@@ -171,6 +212,11 @@ module Skylight::Core
|
|
171
212
|
def instrument(cat, title = nil, desc = nil, meta = nil)
|
172
213
|
raise ArgumentError, "cat is required" unless cat
|
173
214
|
|
215
|
+
if muted?
|
216
|
+
return yield if block_given?
|
217
|
+
return
|
218
|
+
end
|
219
|
+
|
174
220
|
unless (trace = @trace_info.current)
|
175
221
|
return yield if block_given?
|
176
222
|
return
|
@@ -214,6 +260,14 @@ module Skylight::Core
|
|
214
260
|
trace.broken!
|
215
261
|
end
|
216
262
|
|
263
|
+
def poison!
|
264
|
+
@poisoned = true
|
265
|
+
end
|
266
|
+
|
267
|
+
def poisoned?
|
268
|
+
@poisoned
|
269
|
+
end
|
270
|
+
|
217
271
|
def done(span, meta = nil)
|
218
272
|
return unless (trace = @trace_info.current)
|
219
273
|
trace.done(span, meta)
|
@@ -244,12 +298,16 @@ module Skylight::Core
|
|
244
298
|
native_submit_trace(trace)
|
245
299
|
true
|
246
300
|
rescue => e
|
247
|
-
|
248
|
-
t { "BACKTRACE:\n#{e.backtrace.join("\n")}" }
|
249
|
-
false
|
301
|
+
handle_instrumenter_error(trace, e)
|
250
302
|
end
|
251
303
|
end
|
252
304
|
|
305
|
+
def handle_instrumenter_error(trace, e)
|
306
|
+
warn "failed to submit trace to worker; trace=%s, err=%s", trace.uuid, e
|
307
|
+
t { "BACKTRACE:\n#{e.backtrace.join("\n")}" }
|
308
|
+
false
|
309
|
+
end
|
310
|
+
|
253
311
|
def ignore?(trace)
|
254
312
|
config.ignored_endpoints.include?(trace.endpoint)
|
255
313
|
end
|
@@ -259,9 +317,29 @@ module Skylight::Core
|
|
259
317
|
[nil, sql]
|
260
318
|
end
|
261
319
|
|
320
|
+
# Because GraphQL can return multiple results, each of which
|
321
|
+
# may have their own success/error states, we need to set the
|
322
|
+
# skylight segment as follows:
|
323
|
+
#
|
324
|
+
# - when all queries have errors: "error"
|
325
|
+
# - when some queries have errors: "<rendered format>+error"
|
326
|
+
# - when no queries have errors: "<rendered format>"
|
327
|
+
#
|
328
|
+
# <rendered format> will be determined by the Rails controller as usual.
|
329
|
+
# See Instrumenter#finalize_endpoint_segment for the actual segment/error assignment.
|
262
330
|
def finalize_endpoint_segment(trace)
|
263
|
-
return unless trace.segment
|
264
|
-
|
331
|
+
return unless (segment = trace.segment)
|
332
|
+
|
333
|
+
segment = case trace.compound_response_error_status
|
334
|
+
when :all
|
335
|
+
"error"
|
336
|
+
when :partial
|
337
|
+
"#{segment}+error"
|
338
|
+
else
|
339
|
+
segment
|
340
|
+
end
|
341
|
+
|
342
|
+
trace.endpoint += "<sk-segment>#{segment}</sk-segment>"
|
265
343
|
end
|
266
344
|
end
|
267
345
|
end
|
@@ -44,17 +44,31 @@ module Skylight::Core
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
def self.with_after_close(resp, &block)
|
47
|
+
def self.with_after_close(resp, debug_identifier: "unknown", &block)
|
48
48
|
# Responses should be arrays but in some situations they aren't
|
49
49
|
# e.g. https://github.com/ruby-grape/grape/issues/1041
|
50
|
-
# The safest approach seems to be to rely on implicit destructuring
|
51
|
-
# since that is currently what Rack::Lint does.
|
52
50
|
# See also https://github.com/rack/rack/issues/1239
|
53
|
-
status, headers, body = resp
|
54
51
|
|
52
|
+
unless resp.respond_to?(:to_ary)
|
53
|
+
if resp.respond_to?(:to_a)
|
54
|
+
log_target.warn("Rack response from \"#{debug_identifier}\" cannot be implicitly converted to an array. This is in violation of "\
|
55
|
+
"the Rack SPEC and will raise an error in future versions.")
|
56
|
+
resp = resp.to_a
|
57
|
+
else
|
58
|
+
log_target.error("Rack response from \"#{debug_identifier}\" cannot be converted to an array. This is in violation of the Rack SPEC "\
|
59
|
+
"and may cause problems with Skylight operation.")
|
60
|
+
return resp
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
status, headers, body = resp
|
55
65
|
[status, headers, BodyProxy.new(body, &block)]
|
56
66
|
end
|
57
67
|
|
68
|
+
def self.log_target
|
69
|
+
@log_target ||= (Skylight::Core::Fanout.registered.first&.config || Logger.new(STDERR))
|
70
|
+
end
|
71
|
+
|
58
72
|
include Util::Logging
|
59
73
|
|
60
74
|
# For Util::Logging
|
@@ -78,13 +92,13 @@ module Skylight::Core
|
|
78
92
|
else
|
79
93
|
begin
|
80
94
|
t { "middleware beginning trace" }
|
81
|
-
trace = instrumentable.trace(endpoint_name(env), "app.rack.request", nil, meta: endpoint_meta(env))
|
95
|
+
trace = instrumentable.trace(endpoint_name(env), "app.rack.request", nil, meta: endpoint_meta(env), component: :web)
|
82
96
|
t { "middleware began trace=#{trace ? trace.uuid : nil}" }
|
83
97
|
|
84
98
|
resp = @app.call(env)
|
85
99
|
|
86
100
|
if trace
|
87
|
-
Middleware.with_after_close(resp) { trace.submit }
|
101
|
+
Middleware.with_after_close(resp, debug_identifier: "Rack App: #{@app.class}") { trace.submit }
|
88
102
|
else
|
89
103
|
resp
|
90
104
|
end
|
@@ -4,12 +4,12 @@ module Skylight::Core
|
|
4
4
|
class Perform < Normalizer
|
5
5
|
register "perform.active_job"
|
6
6
|
|
7
|
-
DELIVERY_JOB =
|
7
|
+
DELIVERY_JOB = /\AActionMailer::(Mail)?DeliveryJob\Z/.freeze
|
8
8
|
DELAYED_JOB_WRAPPER = "ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper".freeze
|
9
9
|
|
10
10
|
def self.normalize_title(job_instance)
|
11
11
|
job_instance.class.name.to_s.tap do |str|
|
12
|
-
if str
|
12
|
+
if str.match(DELIVERY_JOB)
|
13
13
|
mailer_class, mailer_method, * = job_instance.arguments
|
14
14
|
return ["#{mailer_class}##{mailer_method}", str]
|
15
15
|
end
|
@@ -25,7 +25,8 @@ module Skylight::Core
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def get_namespace(endpoint)
|
28
|
-
|
28
|
+
# slice off preceding slash for data continuity
|
29
|
+
::Grape::Namespace.joined_space_path(endpoint.namespace_stackable(:namespace)).to_s[1..-1]
|
29
30
|
end
|
30
31
|
end
|
31
32
|
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/inflector"
|
4
|
+
|
5
|
+
module Skylight::Core::Normalizers::GraphQL
|
6
|
+
# Some AS::N events in GraphQL are not super useful.
|
7
|
+
# We are purposefully ignoring the following keys (and you probably shouldn't add them):
|
8
|
+
# - "graphql.analyze_multiplex"
|
9
|
+
# - "graphql.execute_field" (very frequently called)
|
10
|
+
# - "graphql.execute_field_lazy"
|
11
|
+
|
12
|
+
class Base < Skylight::Core::Normalizers::Normalizer
|
13
|
+
ANONYMOUS = "[anonymous]".freeze
|
14
|
+
CAT = "app.graphql".freeze
|
15
|
+
|
16
|
+
if defined?(::GraphQL::VERSION) && Gem::Version.new(::GraphQL::VERSION) >= Gem::Version.new("1.10")
|
17
|
+
def self.register_graphql
|
18
|
+
register("#{key}.graphql")
|
19
|
+
end
|
20
|
+
else
|
21
|
+
def self.register_graphql
|
22
|
+
register("graphql.#{key}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.inherited(klass)
|
27
|
+
klass.const_set(
|
28
|
+
:KEY,
|
29
|
+
ActiveSupport::Inflector.underscore(
|
30
|
+
ActiveSupport::Inflector.demodulize(klass.name)
|
31
|
+
).freeze
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.key
|
36
|
+
self::KEY
|
37
|
+
end
|
38
|
+
|
39
|
+
def normalize(_trace, _name, _payload)
|
40
|
+
[CAT, "graphql.#{key}", nil]
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def key
|
46
|
+
self.class.key
|
47
|
+
end
|
48
|
+
|
49
|
+
def extract_query_name(query)
|
50
|
+
query&.context&.[](:skylight_endpoint) ||
|
51
|
+
query&.operation_name ||
|
52
|
+
ANONYMOUS
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Lex < Base
|
57
|
+
register_graphql
|
58
|
+
end
|
59
|
+
|
60
|
+
class Parse < Base
|
61
|
+
register_graphql
|
62
|
+
end
|
63
|
+
|
64
|
+
class Validate < Base
|
65
|
+
register_graphql
|
66
|
+
end
|
67
|
+
|
68
|
+
class ExecuteMultiplex < Base
|
69
|
+
register_graphql
|
70
|
+
|
71
|
+
def normalize_after(trace, _span, _name, payload)
|
72
|
+
# This is in normalize_after because the queries may not have
|
73
|
+
# an assigned operation name before they are executed.
|
74
|
+
# For example, if you send a single query with a defined operation name, e.g.:
|
75
|
+
# ```graphql
|
76
|
+
# query MyNamedQuery { user(id: 1) { name } }
|
77
|
+
# ```
|
78
|
+
# ... but do _not_ send the operationName request param, the GraphQL docs[1]
|
79
|
+
# specify that the executor should use the operation name from the definition.
|
80
|
+
#
|
81
|
+
# In graphql-ruby's case, the calculation of the operation name is lazy, and
|
82
|
+
# has not been done yet at the point where execute_multiplex starts.
|
83
|
+
# [1] https://graphql.org/learn/serving-over-http/#post-request
|
84
|
+
queries, has_errors = payload[:multiplex].queries.each_with_object([Set.new, Set.new]) do |query, (names, errors)|
|
85
|
+
names << extract_query_name(query)
|
86
|
+
errors << query.static_errors.any?
|
87
|
+
end
|
88
|
+
|
89
|
+
trace.endpoint = "graphql:#{queries.sort.join('+')}"
|
90
|
+
trace.compound_response_error_status = if has_errors.all?
|
91
|
+
:all
|
92
|
+
elsif !has_errors.none?
|
93
|
+
:partial
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class AnalyzeQuery < Base
|
99
|
+
register_graphql
|
100
|
+
end
|
101
|
+
|
102
|
+
class ExecuteQuery < Base
|
103
|
+
register_graphql
|
104
|
+
|
105
|
+
def normalize(trace, _name, payload)
|
106
|
+
query_name = extract_query_name(payload[:query])
|
107
|
+
|
108
|
+
if query_name == ANONYMOUS
|
109
|
+
meta = { mute_children: true }
|
110
|
+
end
|
111
|
+
|
112
|
+
# This is probably always overriden by execute_multiplex#normalize_after,
|
113
|
+
# but in the case of a single query, it will be the same value anyway.
|
114
|
+
trace.endpoint = "graphql:#{query_name}"
|
115
|
+
|
116
|
+
[CAT, "graphql.#{key}: #{query_name}", nil, meta]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class ExecuteQueryLazy < ExecuteQuery
|
121
|
+
register_graphql
|
122
|
+
|
123
|
+
def normalize(trace, _name, payload)
|
124
|
+
if payload[:query]
|
125
|
+
super
|
126
|
+
elsif payload[:multiplex]
|
127
|
+
[CAT, "graphql.#{key}.multiplex", nil]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -9,19 +9,24 @@ module Skylight::Core
|
|
9
9
|
def append_info_to_payload(payload)
|
10
10
|
append_info_to_payload_without_sk(payload)
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
12
|
+
payload[:sk_rendered_format] = sk_rendered_mime.try(:ref)
|
13
|
+
payload[:sk_variant] = request.respond_to?(:variant) ? request.variant : nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def sk_rendered_mime
|
17
|
+
if respond_to?(:media_type)
|
18
|
+
mt = media_type
|
19
|
+
return mt && Mime::Type.lookup(mt)
|
21
20
|
end
|
22
21
|
|
23
|
-
|
24
|
-
|
22
|
+
if content_type.is_a?(Mime::Type)
|
23
|
+
content_type
|
24
|
+
elsif content_type.respond_to?(:to_s)
|
25
|
+
type_str = content_type.to_s.split(';').first
|
26
|
+
Mime::Type.lookup(type_str) unless type_str.blank?
|
27
|
+
elsif respond_to?(:rendered_format) && rendered_format
|
28
|
+
rendered_format
|
29
|
+
end
|
25
30
|
end
|
26
31
|
end
|
27
32
|
end
|
@@ -9,7 +9,7 @@ module Skylight::Core
|
|
9
9
|
alias execute_without_sk execute
|
10
10
|
|
11
11
|
def execute(*args)
|
12
|
-
Skylight.trace(TITLE, "app.job.execute") do |trace|
|
12
|
+
Skylight.trace(TITLE, "app.job.execute", component: :worker) do |trace|
|
13
13
|
# See normalizers/active_job/perform for endpoint/segment assignment
|
14
14
|
begin
|
15
15
|
execute_without_sk(*args)
|
@@ -24,7 +24,7 @@ module Skylight::Core
|
|
24
24
|
UNKNOWN
|
25
25
|
end
|
26
26
|
|
27
|
-
Skylight.trace(handler_name, "app.delayed_job.worker", "Delayed::Worker#run", segment: job.queue) do
|
27
|
+
Skylight.trace(handler_name, "app.delayed_job.worker", "Delayed::Worker#run", component: :worker, segment: job.queue) do
|
28
28
|
run_without_sk(job, *args)
|
29
29
|
end
|
30
30
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Skylight::Core
|
4
|
+
module Probes
|
5
|
+
module GraphQL
|
6
|
+
module Instrumentation
|
7
|
+
def initialize(*, **)
|
8
|
+
super
|
9
|
+
|
10
|
+
return unless defined?(@tracers)
|
11
|
+
|
12
|
+
unless @tracers.include?(::GraphQL::Tracing::ActiveSupportNotificationsTracing)
|
13
|
+
@tracers << ::GraphQL::Tracing::ActiveSupportNotificationsTracing
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Probe
|
19
|
+
def install
|
20
|
+
tracing_klass_name = "::GraphQL::Tracing::ActiveSupportNotificationsTracing"
|
21
|
+
klasses_to_probe = %w(
|
22
|
+
::GraphQL::Execution::Multiplex
|
23
|
+
::GraphQL::Query
|
24
|
+
)
|
25
|
+
|
26
|
+
return unless ([tracing_klass_name] + klasses_to_probe).all?(&method(:safe_constantize))
|
27
|
+
|
28
|
+
klasses_to_probe.each do |klass_name|
|
29
|
+
safe_constantize(klass_name).prepend(Instrumentation)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def safe_constantize(klass_name)
|
34
|
+
Skylight::Core::Util::Inflector.safe_constantize(klass_name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
register(:graphql, "GraphQL", "graphql", GraphQL::Probe.new)
|
40
|
+
end
|
41
|
+
end
|
@@ -33,7 +33,7 @@ module Skylight::Core
|
|
33
33
|
spans = Skylight::Core::Fanout.instrument(title: name, category: "#{category}")
|
34
34
|
resp = call_without_sk(*args, &block)
|
35
35
|
|
36
|
-
proxied_response = Skylight::Core::Middleware.with_after_close(resp) do
|
36
|
+
proxied_response = Skylight::Core::Middleware.with_after_close(resp, debug_identifier: "Middleware: #{name}") do
|
37
37
|
Skylight::Core::Fanout.done(spans)
|
38
38
|
end
|
39
39
|
rescue Exception => err
|
@@ -8,10 +8,11 @@ module Skylight::Core
|
|
8
8
|
DISABLED_KEY = :__skylight_net_http_disabled
|
9
9
|
|
10
10
|
def self.disable
|
11
|
+
state_was = Thread.current[DISABLED_KEY]
|
11
12
|
Thread.current[DISABLED_KEY] = true
|
12
13
|
yield
|
13
14
|
ensure
|
14
|
-
Thread.current[DISABLED_KEY] =
|
15
|
+
Thread.current[DISABLED_KEY] = state_was
|
15
16
|
end
|
16
17
|
|
17
18
|
def self.disabled?
|
@@ -36,8 +36,18 @@ module Skylight::Core
|
|
36
36
|
dispatch_without_sk!(*args, &block).tap do
|
37
37
|
Skylight::Core::Fanout.each_trace do |trace|
|
38
38
|
# Set the endpoint name to the route name
|
39
|
-
route = env["sinatra.route"]
|
40
|
-
|
39
|
+
if (route = env["sinatra.route"])
|
40
|
+
# Include the app's mount point (if available)
|
41
|
+
script_name = trace.instrumenter.config.sinatra_route_prefixes? && env["SCRIPT_NAME"]
|
42
|
+
|
43
|
+
trace.endpoint =
|
44
|
+
if script_name && !script_name.empty?
|
45
|
+
verb, path = route.split(" ", 2)
|
46
|
+
"#{verb} [#{script_name}]#{path}"
|
47
|
+
else
|
48
|
+
route
|
49
|
+
end
|
50
|
+
end
|
41
51
|
end
|
42
52
|
end
|
43
53
|
end
|
@@ -2,6 +2,11 @@ module Skylight
|
|
2
2
|
module Core
|
3
3
|
module Sidekiq
|
4
4
|
def self.add_middleware(instrumentable)
|
5
|
+
unless defined?(::Sidekiq)
|
6
|
+
instrumentable.warn "Skylight for Sidekiq is active, but Sidekiq is not defined."
|
7
|
+
return
|
8
|
+
end
|
9
|
+
|
5
10
|
::Sidekiq.configure_server do |sidekiq_config|
|
6
11
|
instrumentable.debug "Adding Sidekiq Middleware"
|
7
12
|
|
@@ -22,7 +27,7 @@ module Skylight
|
|
22
27
|
def call(_worker, job, queue)
|
23
28
|
t { "Sidekiq middleware beginning trace" }
|
24
29
|
title = job["wrapped"] || job["class"]
|
25
|
-
@instrumentable.trace(title, "app.sidekiq.worker", title, segment: queue) do |trace|
|
30
|
+
@instrumentable.trace(title, "app.sidekiq.worker", title, segment: queue, component: :worker) do |trace|
|
26
31
|
begin
|
27
32
|
yield
|
28
33
|
rescue Exception # includes Sidekiq::Shutdown
|
@@ -50,14 +50,12 @@ module Skylight::Core
|
|
50
50
|
|
51
51
|
while (curr = trace.notifications.pop)
|
52
52
|
next unless curr.name == name
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
trace.done(curr.span, meta) if curr.span
|
60
|
-
end
|
53
|
+
|
54
|
+
meta = {}
|
55
|
+
meta[:exception] = payload[:exception] if payload[:exception]
|
56
|
+
meta[:exception_object] = payload[:exception_object] if payload[:exception_object]
|
57
|
+
trace.done(curr.span, meta) if curr.span
|
58
|
+
normalize_after(trace, curr.span, name, payload)
|
61
59
|
return
|
62
60
|
end
|
63
61
|
rescue Exception => e
|
data/lib/skylight/core/trace.rb
CHANGED
@@ -6,14 +6,14 @@ module Skylight::Core
|
|
6
6
|
|
7
7
|
include Util::Logging
|
8
8
|
|
9
|
-
attr_reader :instrumenter, :endpoint, :notifications, :meta
|
10
|
-
attr_accessor :uuid
|
9
|
+
attr_reader :instrumenter, :endpoint, :segment, :notifications, :meta
|
10
|
+
attr_accessor :uuid
|
11
11
|
|
12
|
-
def self.new(instrumenter, endpoint, start, cat, title = nil, desc = nil, meta: nil, segment: nil)
|
12
|
+
def self.new(instrumenter, endpoint, start, cat, title = nil, desc = nil, meta: nil, segment: nil, component: nil)
|
13
13
|
uuid = SecureRandom.uuid
|
14
14
|
inst = native_new(normalize_time(start), uuid, endpoint, meta)
|
15
15
|
inst.uuid = uuid
|
16
|
-
inst.send(:initialize, instrumenter, cat, title, desc, meta)
|
16
|
+
inst.send(:initialize, instrumenter, cat, title, desc, meta, component: component)
|
17
17
|
inst.endpoint = endpoint
|
18
18
|
inst.segment = segment
|
19
19
|
inst
|
@@ -26,7 +26,7 @@ module Skylight::Core
|
|
26
26
|
(time.to_i / 100_000).to_i
|
27
27
|
end
|
28
28
|
|
29
|
-
def initialize(instrumenter, cat, title, desc, meta)
|
29
|
+
def initialize(instrumenter, cat, title, desc, meta, **)
|
30
30
|
raise ArgumentError, "instrumenter is required" unless instrumenter
|
31
31
|
|
32
32
|
@instrumenter = instrumenter
|
@@ -51,14 +51,32 @@ module Skylight::Core
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def endpoint=(value)
|
54
|
+
if muted?
|
55
|
+
maybe_warn(:endpoint_set_muted, "tried to set endpoint name while muted")
|
56
|
+
return
|
57
|
+
end
|
54
58
|
@endpoint = value
|
55
59
|
native_set_endpoint(value)
|
56
60
|
end
|
57
61
|
|
62
|
+
def segment=(value)
|
63
|
+
if muted?
|
64
|
+
maybe_warn(:segment_set_muted, "tried to set segment name while muted")
|
65
|
+
return
|
66
|
+
end
|
67
|
+
@segment = value
|
68
|
+
end
|
69
|
+
|
70
|
+
attr_accessor :compound_response_error_status
|
71
|
+
|
58
72
|
def config
|
59
73
|
@instrumenter.config
|
60
74
|
end
|
61
75
|
|
76
|
+
def muted?
|
77
|
+
!!@child_instrumentation_muted_by || @instrumenter.muted?
|
78
|
+
end
|
79
|
+
|
62
80
|
def broken?
|
63
81
|
!!@broken
|
64
82
|
end
|
@@ -69,7 +87,7 @@ module Skylight::Core
|
|
69
87
|
end
|
70
88
|
|
71
89
|
def record(cat, title = nil, desc = nil)
|
72
|
-
return if broken?
|
90
|
+
return if muted? || broken?
|
73
91
|
|
74
92
|
title.freeze if title.is_a?(String)
|
75
93
|
desc.freeze if desc.is_a?(String)
|
@@ -87,6 +105,7 @@ module Skylight::Core
|
|
87
105
|
end
|
88
106
|
|
89
107
|
def instrument(cat, title = nil, desc = nil, meta = nil)
|
108
|
+
return if muted?
|
90
109
|
return if broken?
|
91
110
|
t { "instrument: #{cat}, #{title}" }
|
92
111
|
|
@@ -119,10 +138,9 @@ module Skylight::Core
|
|
119
138
|
# `span` will be `nil` if we failed to start instrumenting, such as in
|
120
139
|
# the case of too many spans in a request.
|
121
140
|
return unless span
|
122
|
-
|
123
141
|
return if broken?
|
124
142
|
|
125
|
-
if meta
|
143
|
+
if meta&.[](:defer)
|
126
144
|
deferred_spans[span] ||= (Util::Clock.nanos - gc_time)
|
127
145
|
return
|
128
146
|
end
|
@@ -199,6 +217,8 @@ module Skylight::Core
|
|
199
217
|
def start(time, cat, title, desc, meta, opts = {})
|
200
218
|
time = self.class.normalize_time(time) unless opts[:normalize] == false
|
201
219
|
|
220
|
+
mute_children = meta&.delete(:mute_children)
|
221
|
+
|
202
222
|
sp = native_start_span(time, cat.to_s)
|
203
223
|
native_span_set_title(sp, title.to_s) if title
|
204
224
|
native_span_set_description(sp, desc.to_s) if desc
|
@@ -208,9 +228,18 @@ module Skylight::Core
|
|
208
228
|
@spans << sp
|
209
229
|
t { "started span: #{sp} - #{cat}, #{title}" }
|
210
230
|
|
231
|
+
if mute_children
|
232
|
+
t { "muting child instrumentation for span=#{sp}" }
|
233
|
+
mute_child_instrumentation(sp)
|
234
|
+
end
|
235
|
+
|
211
236
|
sp
|
212
237
|
end
|
213
238
|
|
239
|
+
def mute_child_instrumentation(span)
|
240
|
+
@child_instrumentation_muted_by = span
|
241
|
+
end
|
242
|
+
|
214
243
|
# Middleware spans that were interrupted by a throw/catch should be cached here.
|
215
244
|
# keys: span ids
|
216
245
|
# values: nsec timestamp at which the span was cached here.
|
@@ -237,6 +266,10 @@ module Skylight::Core
|
|
237
266
|
def normalized_stop(span, time)
|
238
267
|
time = self.class.normalize_time(time)
|
239
268
|
native_stop_span(span, time)
|
269
|
+
|
270
|
+
if @child_instrumentation_muted_by == span
|
271
|
+
@child_instrumentation_muted_by = nil # restart instrumenting
|
272
|
+
end
|
240
273
|
end
|
241
274
|
|
242
275
|
# Originally extracted from `stop`.
|
@@ -271,5 +304,16 @@ module Skylight::Core
|
|
271
304
|
@gc.update
|
272
305
|
@gc.time
|
273
306
|
end
|
307
|
+
|
308
|
+
def maybe_warn(context, msg)
|
309
|
+
return if warnings_silenced?(context)
|
310
|
+
instrumenter.silence_warnings(context)
|
311
|
+
|
312
|
+
warn(msg)
|
313
|
+
end
|
314
|
+
|
315
|
+
def warnings_silenced?(context)
|
316
|
+
instrumenter.warnings_silenced?(context)
|
317
|
+
end
|
274
318
|
end
|
275
319
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: skylight-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tilde, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-04-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -204,6 +204,7 @@ files:
|
|
204
204
|
- lib/skylight/core/normalizers/grape/format_response.rb
|
205
205
|
- lib/skylight/core/normalizers/graphiti/render.rb
|
206
206
|
- lib/skylight/core/normalizers/graphiti/resolve.rb
|
207
|
+
- lib/skylight/core/normalizers/graphql/base.rb
|
207
208
|
- lib/skylight/core/normalizers/moped/query.rb
|
208
209
|
- lib/skylight/core/normalizers/render.rb
|
209
210
|
- lib/skylight/core/normalizers/sequel/sql.rb
|
@@ -223,6 +224,7 @@ files:
|
|
223
224
|
- lib/skylight/core/probes/excon/middleware.rb
|
224
225
|
- lib/skylight/core/probes/faraday.rb
|
225
226
|
- lib/skylight/core/probes/grape.rb
|
227
|
+
- lib/skylight/core/probes/graphql.rb
|
226
228
|
- lib/skylight/core/probes/httpclient.rb
|
227
229
|
- lib/skylight/core/probes/middleware.rb
|
228
230
|
- lib/skylight/core/probes/mongo.rb
|