Class: Skylight::Core::Instrumenter Private

Inherits:
Object
  • Object
show all
Includes:
Util::Logging
Defined in:
lib/skylight/core/instrumenter.rb

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Defined Under Namespace

Classes: TraceInfo

Constant Summary collapse

KEY =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

:__skylight_current_trace
TOO_MANY_UNIQUES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

"<too many unique descriptions>".freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util::Logging

#config_for_logging, #debug, #error, #fmt, #info, #log, #log_env_prefix, #raise_on_error?, #t, #trace?, #warn

Constructor Details

#initialize(uuid, config) ⇒ Instrumenter

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Instrumenter.



58
59
60
61
62
63
64
65
66
67
# File 'lib/skylight/core/instrumenter.rb', line 58

def initialize(uuid, config)
  @uuid = uuid
  @gc = config.gc
  @config = config
  @subscriber = Subscriber.new(config, self)

  key = "#{KEY}_#{self.class.trace_class.name}".gsub(/\W/, "_")
  @trace_info = @config[:trace_info] || TraceInfo.new(key)
  @mutex = Mutex.new
end

Instance Attribute Details

#configObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



39
40
41
# File 'lib/skylight/core/instrumenter.rb', line 39

def config
  @config
end

#gcObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



39
40
41
# File 'lib/skylight/core/instrumenter.rb', line 39

def gc
  @gc
end

#uuidObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



39
40
41
# File 'lib/skylight/core/instrumenter.rb', line 39

def uuid
  @uuid
end

Class Method Details

.match?(string, regex) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


202
203
204
205
206
# File 'lib/skylight/core/instrumenter.rb', line 202

def self.match?(string, regex)
  @scanner ||= StringScanner.new("")
  @scanner.string = string
  @scanner.match?(regex)
end

.native_new(_uuid, _config_env) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



45
46
47
# File 'lib/skylight/core/instrumenter.rb', line 45

def self.native_new(_uuid, _config_env)
  raise "not implemented"
end

.new(config) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



49
50
51
52
53
54
55
56
# File 'lib/skylight/core/instrumenter.rb', line 49

def self.new(config)
  config.validate!

  uuid = SecureRandom.uuid
  inst = native_new(uuid, config.to_native_env)
  inst.send(:initialize, uuid, config)
  inst
end

.trace_classObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



41
42
43
# File 'lib/skylight/core/instrumenter.rb', line 41

def self.trace_class
  Trace
end

Instance Method Details

#broken!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



258
259
260
261
# File 'lib/skylight/core/instrumenter.rb', line 258

def broken!
  return unless (trace = @trace_info.current)
  trace.broken!
end

#check_install!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



98
99
100
# File 'lib/skylight/core/instrumenter.rb', line 98

def check_install!
  true
end

#current_traceObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



89
90
91
# File 'lib/skylight/core/instrumenter.rb', line 89

def current_trace
  @trace_info.current
end

#current_trace=(trace) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



93
94
95
96
# File 'lib/skylight/core/instrumenter.rb', line 93

def current_trace=(trace)
  t { "setting current_trace=#{trace ? trace.uuid : 'nil'}; thread=#{Thread.current.object_id}" }
  @trace_info.current = trace
end

#done(span, meta = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



271
272
273
274
# File 'lib/skylight/core/instrumenter.rb', line 271

def done(span, meta = nil)
  return unless (trace = @trace_info.current)
  trace.done(span, meta)
end

#finalize_endpoint_segment(trace) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Because GraphQL can return multiple results, each of which may have their own success/error states, we need to set the skylight segment as follows:

  • when all queries have errors: “error”

  • when some queries have errors: “<rendered format>+error”

  • when no queries have errors: “<rendered format>”

<rendered format> will be determined by the Rails controller as usual. See Instrumenter#finalize_endpoint_segment for the actual segment/error assignment.



330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/skylight/core/instrumenter.rb', line 330

def finalize_endpoint_segment(trace)
  return unless (segment = trace.segment)

  segment = case trace.compound_response_error_status
            when :all
              "error"
            when :partial
              "#{segment}+error"
            else
              segment
            end

  trace.endpoint += "<sk-segment>#{segment}</sk-segment>"
end

#handle_instrumenter_error(trace, e) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



305
306
307
308
309
# File 'lib/skylight/core/instrumenter.rb', line 305

def handle_instrumenter_error(trace, e)
  warn "failed to submit trace to worker; trace=%s, err=%s", trace.uuid, e
  t { "BACKTRACE:\n#{e.backtrace.join("\n")}" }
  false
end

#ignore?(trace) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


311
312
313
# File 'lib/skylight/core/instrumenter.rb', line 311

def ignore?(trace)
  config.ignored_endpoints.include?(trace.endpoint)
end

#instrument(cat, title = nil, desc = nil, meta = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Raises:

  • (ArgumentError)


212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/skylight/core/instrumenter.rb', line 212

def instrument(cat, title = nil, desc = nil, meta = nil)
  raise ArgumentError, "cat is required" unless cat

  if muted?
    return yield if block_given?
    return
  end

  unless (trace = @trace_info.current)
    return yield if block_given?
    return
  end

  cat = cat.to_s

  unless match?(cat, Skylight::CATEGORY_REGEX)
    warn "invalid skylight instrumentation category; trace=%s; value=%s", trace.uuid, cat
    return yield if block_given?
    return
  end

  cat = "other.#{cat}" unless match?(cat, Skylight::TIER_REGEX)

  unless (sp = trace.instrument(cat, title, desc, meta))
    return yield if block_given?
    return
  end

  return sp unless block_given?

  meta = {}
  begin
    yield sp
  rescue Exception => e
    meta = { exception: [e.class.name, e.message], exception_object: e }
    raise e
  ensure
    trace.done(sp, meta)
  end
end

#limited_description(description) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



276
277
278
279
280
281
282
283
284
285
286
# File 'lib/skylight/core/instrumenter.rb', line 276

def limited_description(description)
  endpoint = @trace_info.current.endpoint

  if description
    if native_track_desc(endpoint, description)
      description
    else
      TOO_MANY_UNIQUES
    end
  end
end

#log_contextObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



69
70
71
# File 'lib/skylight/core/instrumenter.rb', line 69

def log_context
  @log_context ||= { inst: uuid }
end

#match?(string, regex) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


208
209
210
# File 'lib/skylight/core/instrumenter.rb', line 208

def match?(string, regex)
  self.class.match?(string, regex)
end

#muteObject Also known as: disable

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



110
111
112
113
114
115
116
# File 'lib/skylight/core/instrumenter.rb', line 110

def mute
  old_muted = muted?
  self.muted = true
  yield if block_given?
ensure
  self.muted = old_muted
end

#muted=(val) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



102
103
104
# File 'lib/skylight/core/instrumenter.rb', line 102

def muted=(val)
  @trace_info.muted = val
end

#muted?Boolean Also known as: disabled?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


106
107
108
# File 'lib/skylight/core/instrumenter.rb', line 106

def muted?
  @trace_info.muted?
end

#native_startObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



73
74
75
# File 'lib/skylight/core/instrumenter.rb', line 73

def native_start
  raise "not implemented"
end

#native_stopObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



77
78
79
# File 'lib/skylight/core/instrumenter.rb', line 77

def native_stop
  raise "not implemented"
end

#native_submit_trace(_trace) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



85
86
87
# File 'lib/skylight/core/instrumenter.rb', line 85

def native_submit_trace(_trace)
  raise "not implemented"
end

#native_track_desc(_endpoint, _description) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



81
82
83
# File 'lib/skylight/core/instrumenter.rb', line 81

def native_track_desc(_endpoint, _description)
  raise "not implemented"
end

#poison!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



263
264
265
# File 'lib/skylight/core/instrumenter.rb', line 263

def poison!
  @poisoned = true
end

#poisoned?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


267
268
269
# File 'lib/skylight/core/instrumenter.rb', line 267

def poisoned?
  @poisoned
end

#process(trace) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/skylight/core/instrumenter.rb', line 288

def process(trace)
  t { fmt "processing trace=#{trace.uuid}" }

  if ignore?(trace)
    t { fmt "ignoring trace=#{trace.uuid}" }
    return false
  end

  begin
    finalize_endpoint_segment(trace)
    native_submit_trace(trace)
    true
  rescue => e
    handle_instrumenter_error(trace, e)
  end
end

#process_sql(sql) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Return [title, sql]



316
317
318
# File 'lib/skylight/core/instrumenter.rb', line 316

def process_sql(sql)
  [nil, sql]
end

#shutdownObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



170
171
172
173
# File 'lib/skylight/core/instrumenter.rb', line 170

def shutdown
  @subscriber.unregister!
  native_stop
end

#silence_warnings(context) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



126
127
128
129
130
131
132
# File 'lib/skylight/core/instrumenter.rb', line 126

def silence_warnings(context)
  @warnings_silenced || @mutex.synchronize do
    @warnings_silenced ||= {}
  end

  @warnings_silenced[context] = true
end

#span_correlation_header(span) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



253
254
255
256
# File 'lib/skylight/core/instrumenter.rb', line 253

def span_correlation_header(span)
  return unless (trace = @trace_info.current)
  trace.span_correlation_header(span)
end

#start!Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/skylight/core/instrumenter.rb', line 141

def start!
  # We do this here since we can't report these issues via Gem install without stopping install entirely.
  check_install!

  t { "starting instrumenter" }

  unless config.validate_with_server
    log_error "invalid config"
    return
  end

  t { "starting native instrumenter" }
  unless native_start
    warn "failed to start instrumenter"
    return
  end

  config.gc.enable
  @subscriber.register!

  ActiveSupport::Notifications.instrument("started_instrumenter.skylight", instrumenter: self)

  self
rescue Exception => e
  log_error "failed to start instrumenter; msg=%s; config=%s", e.message, config.inspect
  t { e.backtrace.join("\n") }
  nil
end

#trace(endpoint, cat, title = nil, desc = nil, meta: nil, segment: nil, component: nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/skylight/core/instrumenter.rb', line 175

def trace(endpoint, cat, title = nil, desc = nil, meta: nil, segment: nil, component: nil)
  # If a trace is already in progress, continue with that one
  if (trace = @trace_info.current)
    return yield(trace) if block_given?
    return trace
  end

  begin
    trace = self.class.trace_class.new(self, endpoint, Util::Clock.nanos, cat, title, desc, meta: meta, segment: segment, component: component)
  rescue Exception => e
    log_error e.message
    t { e.backtrace.join("\n") }
    return
  end

  @trace_info.current = trace
  return trace unless block_given?

  begin
    yield trace
  ensure
    @trace_info.current = nil
    t { "instrumenter submitting trace; trace=#{trace.uuid}" }
    trace.submit
  end
end

#unmuteObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



118
119
120
121
122
123
124
# File 'lib/skylight/core/instrumenter.rb', line 118

def unmute
  old_muted = muted?
  self.muted = false
  yield if block_given?
ensure
  self.muted = old_muted
end

#warnings_silenced?(context) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


134
135
136
# File 'lib/skylight/core/instrumenter.rb', line 134

def warnings_silenced?(context)
  @warnings_silenced && @warnings_silenced[context]
end