skylight-core 4.1.2 → 4.3.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/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
|