Class: Skylight::Instrumenter Private

Inherits:
Object
  • Object
show all
Includes:
Util::Logging
Defined in:
lib/skylight/instrumenter.rb,
lib/skylight/native.rb,
ext/skylight_native.c

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
LOCK =

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.

Mutex.new
DESC_LOCK =

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.

Mutex.new
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>"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util::Logging

#debug, #error, #info, #log, #t, trace?, #warn

Constructor Details

#initialize(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.



84
85
86
87
88
89
90
91
# File 'lib/skylight/instrumenter.rb', line 84

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

  @trace_info = @config[:trace_info] || TraceInfo.new
  @descriptions = Hash.new { |h,k| h[k] = {} }
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.



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

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.



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

def gc
  @gc
end

#trace_infoObject (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.



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

def trace_info
  @trace_info
end

Class Method Details

.instanceObject

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.



26
27
28
# File 'lib/skylight/instrumenter.rb', line 26

def self.instance
  @instance
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)


181
182
183
184
# File 'lib/skylight/instrumenter.rb', line 181

def self.match?(string, regex)
  @scanner.string = string
  @scanner.match?(regex)
end

.native_new(rb_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.

class Skylight::Instrumenter



152
153
154
# File 'ext/skylight_native.c', line 152

def self.native_new(*args)
  allocate
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.



74
75
76
77
78
79
80
81
82
# File 'lib/skylight/instrumenter.rb', line 74

def self.new(config)
  config ||= {}
  config = Config.load(config) unless config.is_a?(Config)
  config.validate!

  inst = native_new(config.to_env)
  inst.send(:initialize, config)
  inst
end

.start!(config = 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.

Do start

Parameters:

  • config (Config) (defaults to: nil)

    The config



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/skylight/instrumenter.rb', line 32

def self.start!(config = nil)
  return @instance if @instance

  LOCK.synchronize do
    return @instance if @instance
    @instance = new(config).start!
  end
rescue => e
  message = sprintf("[SKYLIGHT] [#{Skylight::VERSION}] Unable to start Instrumenter; msg=%s; class=%s", e.message, e.class)
  if config && config.respond_to?(:logger)
    config.logger.warn message
  else
    warn message
  end
  false
end

.stop!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
57
58
# File 'lib/skylight/instrumenter.rb', line 49

def self.stop!
  LOCK.synchronize do
    return unless @instance
    # This is only really helpful for getting specs to pass.
    @instance.current_trace = nil

    @instance.shutdown
    @instance = nil
  end
end

Instance Method Details

#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.



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

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.



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

def current_trace=(trace)
  @trace_info.current = trace
end

#disableObject

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.



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

def disable
  @disabled = true
  yield
ensure
  @disabled = false
end

#disabled?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)


176
177
178
# File 'lib/skylight/instrumenter.rb', line 176

def disabled?
  @disabled
end

#done(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.



190
191
192
193
# File 'lib/skylight/instrumenter.rb', line 190

def done(span)
  return unless trace = @trace_info.current
  trace.done(span)
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)


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

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

#instrument(cat, title = nil, desc = nil, annot = 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)


195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/skylight/instrumenter.rb', line 195

def instrument(cat, title=nil, desc=nil, annot=nil)
  raise ArgumentError, 'cat is required' unless cat

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

  cat = cat.to_s

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

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

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

  return sp unless block_given?

  begin
    yield sp
  ensure
    trace.done(sp)
  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.



227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/skylight/instrumenter.rb', line 227

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

  DESC_LOCK.synchronize do
    set = @descriptions[endpoint]

    if set.size >= 100
      return TOO_MANY_UNIQUES
    end

    set[description] = true
    description
  end
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)


186
187
188
# File 'lib/skylight/instrumenter.rb', line 186

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

#native_startObject



200
201
202
203
204
205
206
207
# File 'ext/skylight_native.c', line 200

static VALUE
instrumenter_start(VALUE self) {
  sky_instrumenter_t* instrumenter;

  My_Struct(instrumenter, sky_instrumenter_t, no_instrumenter_msg);

  return (VALUE) WITHOUT_GVL(instrumenter_start_nogvl, instrumenter);
}

#native_stopObject



209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'ext/skylight_native.c', line 209

static VALUE
instrumenter_stop(VALUE self) {
  sky_instrumenter_t* instrumenter;

  My_Struct(instrumenter, sky_instrumenter_t, no_instrumenter_msg);

  CHECK_FFI(
      sky_instrumenter_stop(instrumenter),
      "native Instrumenter#stop failed");

  sky_deactivate_memprof();

  return Qnil;
}

#native_submit_trace(rb_trace) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'ext/skylight_native.c', line 224

static VALUE
instrumenter_submit_trace(VALUE self, VALUE rb_trace) {
  sky_instrumenter_t* instrumenter;
  sky_trace_t* trace;

  My_Struct(instrumenter, sky_instrumenter_t, no_instrumenter_msg);
  Transfer_Struct(trace, rb_trace, sky_trace_t, consumed_trace_msg);

  CHECK_FFI(
      sky_instrumenter_submit_trace(instrumenter, trace),
      "native Instrumenter#submit_trace failed");

  return Qnil;
}

#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.



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/skylight/instrumenter.rb', line 242

def process(trace)
  t { fmt "processing trace" }

  if ignore?(trace)
    t { fmt "ignoring trace" }
    return false
  end

  begin
    native_submit_trace(trace)
    true
  rescue => e
    warn "failed to submit trace to worker; err=%s", e
    false
  end
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.



136
137
138
139
# File 'lib/skylight/instrumenter.rb', line 136

def shutdown
  @subscriber.unregister!
  native_stop
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.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/skylight/instrumenter.rb', line 101

def start!
  # Warn if there was an error installing Skylight.
  # We do this here since we can't report these issues via Gem install without stopping install entirely.
  Skylight.check_install_errors(config)

  unless Skylight.native?
    Skylight.warn_skylight_native_missing(config)
    return
  end

  t { "starting instrumenter" }
  @config.validate!

  unless validate_authentication
    warn "invalid authentication token"
    return
  end

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

  @config.gc.enable
  @subscriber.register!

  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, annot = 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.



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
# File 'lib/skylight/instrumenter.rb', line 141

def trace(endpoint, cat, title=nil, desc=nil, annot=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 = Trace.new(self, endpoint, Util::Clock.nanos, cat, title, desc, annot)
  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 { "submitting trace" }
    trace.submit
  end
end

#validate_authenticationObject

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.

Validates that the provided authentication token is valid. This is done by issuing a request for a session token and checking the response



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/skylight/instrumenter.rb', line 265

def validate_authentication
  # If a session token is specified, don't bother attempting to validate
  if config[:session_token]
    debug "using pre-generated session token"
    true
  else
    api = Api.new(config)
    api.authentication = config[:authentication]

    case res = api.validate_authentication
    when :ok
      true
    when :invalid
      false
    when :unknown
      warn "unable to validate authentication token"
      true
    else
      error "[BUG] unexpected validate_token result; res=%s", res
      true
    end
  end
end