Class: Instana::Tracer

Inherits:
Object
  • Object
show all
Extended by:
ThreadLocal
Defined in:
lib/instana/tracer.rb

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ThreadLocal

thread_local

Class Method Details

.method_missing(method, *args, &block) ⇒ Object



14
15
16
17
18
19
20
# File 'lib/instana/tracer.rb', line 14

def method_missing(method, *args, &block)
  if ::Instana.tracer.respond_to?(method)
    ::Instana.tracer.send(method, *args, &block)
  else
    super
  end
end

Instance Method Details

#clear!Object

Used in the test suite, this resets the tracer to non-tracing state.



413
414
415
# File 'lib/instana/tracer.rb', line 413

def clear!
  self.current_trace = nil
end

#contextSpanContext

Retrieve the current context of the tracer.

Returns:



362
363
364
365
# File 'lib/instana/tracer.rb', line 362

def context
  return nil unless tracing?
  self.current_trace.current_span.context
end

#current_spanObject

Helper method to retrieve the currently active span for the active trace.



401
402
403
# File 'lib/instana/tracer.rb', line 401

def current_span
  self.current_trace ? self.current_trace.current_span : nil
end

#current_span_name?(candidate) ⇒ Boolean

Indicates if the name of the current span matches <candidate>

Returns:

  • (Boolean)


407
408
409
# File 'lib/instana/tracer.rb', line 407

def current_span_name?(candidate)
  self.current_trace &&  self.current_trace.current_span.name == candidate.to_sym
end

#extract(format, carrier) ⇒ SpanContext

Extract a span from a carrier



314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/instana/tracer.rb', line 314

def extract(format, carrier)
  case format
  when OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY
    ::Instana.logger.debug 'Unsupported extract format'
  when OpenTracing::FORMAT_RACK
    ::Instana::SpanContext.new(::Instana::Util.header_to_id(carrier['HTTP_X_INSTANA_T']),
                                 ::Instana::Util.header_to_id(carrier['HTTP_X_INSTANA_S']))
  else
    ::Instana.logger.debug 'Unknown inject format'
    nil
  end
end

#inject(span_context, format, carrier) ⇒ Object

Inject a span into the given carrier

Parameters:



295
296
297
298
299
300
301
302
303
304
305
# File 'lib/instana/tracer.rb', line 295

def inject(span_context, format, carrier)
  case format
  when OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY
    ::Instana.logger.debug 'Unsupported inject format'
  when OpenTracing::FORMAT_RACK
    carrier['X-Instana-T'] = ::Instana::Util.id_to_header(span_context.trace_id)
    carrier['X-Instana-S'] = ::Instana::Util.id_to_header(span_context.span_id)
  else
    ::Instana.logger.debug 'Unknown inject format'
  end
end

#log_async_entry(name, kvs) ⇒ Hash

Starts a new asynchronous span on the current trace.

Parameters:

  • name (String)

    the name of the span to create

  • kvs (Hash)

    list of key values to be reported in the span

Returns:

  • (Hash)

    the context: Trace ID and Span ID in the form of :trace_id => 12345 :span_id => 12345



186
187
188
189
# File 'lib/instana/tracer.rb', line 186

def log_async_entry(name, kvs)
  return unless tracing?
  self.current_trace.new_async_span(name, kvs)
end

#log_async_error(e, span) ⇒ Object

Add an error to an asynchronous span

Parameters:

  • e (Exception)

    Add exception to the current span

  • span (Span)

    the span for this Async op (previously returned from ‘log_async_entry`)



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/instana/tracer.rb', line 220

def log_async_error(e, span)
  # Asynchronous spans can persist longer than the parent
  # trace.  With the trace ID, we check the current trace
  # but otherwise, we search staged traces.

  if tracing? && self.current_trace.id == span.context.trace_id
    self.current_trace.add_async_error(e, span)
  else
    trace = ::Instana.processor.staged_trace(span.context.trace_id)
    if trace
      trace.add_async_error(e, span)
    else
      ::Instana.logger.debug "#{__method__}: Couldn't find staged trace. #{span.inspect}"
    end
  end
end

#log_async_exit(_name, kvs, span) ⇒ Object

Closes out an asynchronous span

Parameters:

  • name (String)

    the name of the async span to exit (close out)

  • kvs (Hash)

    list of key values to be reported in the span

  • span (Span)

    the span for this Async op (previously returned from ‘log_async_entry`)



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/instana/tracer.rb', line 244

def log_async_exit(_name, kvs, span)
  # An asynchronous span can end after the current trace has
  # already completed so we make sure that we end the span
  # on the right trace.

  if tracing? && self.current_trace.id == span.context.trace_id
    self.current_trace.end_async_span(kvs, span)
  else
    # Different trace from current so find the staged trace
    # and close out the span on it.
    trace = ::Instana.processor.staged_trace(span.context.trace_id)
    if trace
      trace.end_async_span(kvs, span)
    else
      ::Instana.logger.debug "#{__method__}: Couldn't find staged trace. #{span.inspect}"
    end
  end
end

#log_async_info(kvs, span) ⇒ Object

Add info to an asynchronous span

Parameters:

  • kvs (Hash)

    list of key values to be reported in the span

  • span (Span)

    the span for this Async op (previously returned from ‘log_async_entry`)



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/instana/tracer.rb', line 197

def log_async_info(kvs, span)
  # Asynchronous spans can persist longer than the parent
  # trace.  With the trace ID, we check the current trace
  # but otherwise, we search staged traces.

  if tracing? && self.current_trace.id == span.context.trace_id
    self.current_trace.add_async_info(kvs, span)
  else
    trace = ::Instana.processor.staged_trace(span.context.trace_id)
    if trace
      trace.add_async_info(kvs, span)
    else
      ::Instana.logger.debug "#{__method__}: Couldn't find staged trace. #{span.inspect}"
    end
  end
end

#log_end(name, kvs = {}, end_time = ::Instana::Util.now_in_ms) ⇒ Object

Note:

‘name` isn’t really required but helps keep sanity that

Closes out the current span in the current trace and queues the trace for reporting

we’re ending the span that we really want to close out.

Parameters:

  • name (String)

    the name of the span to end

  • kvs (Hash) (defaults to: {})

    list of key values to be reported in the span



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/instana/tracer.rb', line 148

def log_end(name, kvs = {}, end_time = ::Instana::Util.now_in_ms)
  return unless tracing?

  if ENV.key?('INSTANA_DEBUG') || ENV.key?('INSTANA_TEST')
    unless current_span_name?(name)
      ::Instana.logger.debug "Span mismatch: Attempt to end #{name} span but #{current_span.name} is active."
    end
  end

  self.current_trace.finish(kvs, end_time)

  if ::Instana.agent.ready?
    if !self.current_trace.has_async? ||
        (self.current_trace.has_async? && self.current_trace.complete?)
      Instana.processor.add(self.current_trace)
    else
      # This trace still has outstanding/uncompleted asynchronous spans.
      # Put it in the staging queue until the async span closes out or
      # 5 minutes has passed.  Whichever comes first.
      Instana.processor.stage(self.current_trace)
    end
  end
  self.current_trace = nil
end

#log_entry(name, kvs = {}) ⇒ Object

Will establish a new span as a child of the current span in an existing trace

Parameters:

  • name (String)

    the name of the span to create

  • kvs (Hash) (defaults to: {})

    list of key values to be reported in the span



96
97
98
99
# File 'lib/instana/tracer.rb', line 96

def log_entry(name, kvs = {})
  return unless tracing?
  self.current_trace.new_span(name, kvs)
end

#log_error(e) ⇒ Object

Add an error to the current span

Parameters:

  • e (Exception)

    Add exception to the current span



114
115
116
117
# File 'lib/instana/tracer.rb', line 114

def log_error(e)
  return unless tracing?
  self.current_trace.add_error(e)
end

#log_exit(name, kvs = {}) ⇒ Object

Note:

‘name` isn’t really required but helps keep sanity that

Closes out the current span

we’re closing out the span that we really want to close out.

Parameters:

  • name (String)

    the name of the span to exit (close out)

  • kvs (Hash) (defaults to: {})

    list of key values to be reported in the span



127
128
129
130
131
132
133
134
135
136
137
# File 'lib/instana/tracer.rb', line 127

def log_exit(name, kvs = {})
  return unless tracing?

  if ENV.key?('INSTANA_DEBUG') || ENV.key?('INSTANA_TEST')
    unless current_span_name?(name)
      ::Instana.logger.debug "Span mismatch: Attempt to exit #{name} span but #{current_span.name} is active."
    end
  end

  self.current_trace.end_span(kvs)
end

#log_info(kvs) ⇒ Object

Add info to the current span

Parameters:

  • kvs (Hash)

    list of key values to be reported in the span



105
106
107
108
# File 'lib/instana/tracer.rb', line 105

def log_info(kvs)
  return unless tracing?
  self.current_trace.add_info(kvs)
end

#log_start_or_continue(name, kvs = {}, incoming_context = {}) ⇒ Object

Will start a new trace or continue an on-going one (such as from incoming remote requests with context headers).

Parameters:

  • name (String)

    the name of the span to start

  • kvs (Hash) (defaults to: {})

    list of key values to be reported in the span

  • incoming_context (Hash) (defaults to: {})

    specifies the incoming context. At a minimum, it should specify :trace_id and :span_id from the following:

    :trace_id the trace ID (must be an unsigned hex-string)
    :span_id the ID of the parent span (must be an unsigned hex-string)
    :level specifies data collection level (optional)
    


84
85
86
87
88
# File 'lib/instana/tracer.rb', line 84

def log_start_or_continue(name, kvs = {}, incoming_context = {})
  return if !::Instana.agent.ready? || !::Instana.config[:tracing][:enabled]
  ::Instana.logger.debug "#{__method__} passed a block.  Use `start_or_continue` instead!" if block_given?
  self.current_trace = ::Instana::Trace.new(name, kvs, incoming_context)
end

#span_idObject

Returns the current [Span] ID for the active trace (if there is one), otherwise nil.



395
396
397
# File 'lib/instana/tracer.rb', line 395

def span_id
  self.current_trace  ? current_trace.current_span_id : nil
end

#span_id_headerString

Take the current span_id and convert it to a header compatible formate.

Returns:

  • (String)

    a hexadecimal representation of the current span ID



381
382
383
# File 'lib/instana/tracer.rb', line 381

def span_id_header
  ::Instana::Util.id_to_header(span_id)
end

#start_or_continue_trace(name, kvs = {}, incoming_context = {}, &block) ⇒ Object

Will start a new trace or continue an on-going one (such as from incoming remote requests with context headers).

Parameters:

  • name (String)

    the name of the span to start

  • kvs (Hash) (defaults to: {})

    list of key values to be reported in the span

  • incoming_context (Hash) (defaults to: {})

    specifies the incoming context. At a minimum, it should specify :trace_id and :span_id from the following:

    @:trace_id the trace ID (must be an unsigned hex-string)
    :span_id the ID of the parent span (must be an unsigned hex-string)
    :level specifies data collection level (optional)
    


38
39
40
41
42
43
44
45
46
# File 'lib/instana/tracer.rb', line 38

def start_or_continue_trace(name, kvs = {}, incoming_context = {}, &block)
  log_start_or_continue(name, kvs, incoming_context)
  yield
rescue Exception => e
  log_error(e)
  raise
ensure
  log_end(name)
end

#start_span(operation_name, child_of: nil, start_time: ::Instana::Util.now_in_ms, tags: nil) ⇒ Span

Start a new span

Parameters:

  • operation_name (String)

    The name of the operation represented by the span

  • child_of (Span) (defaults to: nil)

    A span to be used as the ChildOf reference

  • start_time (Time) (defaults to: ::Instana::Util.now_in_ms)

    the start time of the span

  • tags (Hash) (defaults to: nil)

    Starting tags for the span

Returns:



276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/instana/tracer.rb', line 276

def start_span(operation_name, child_of: nil, start_time: ::Instana::Util.now_in_ms, tags: nil)
  # return unless ::Instana.agent.ready?

  if tracing?
    span = self.current_trace.new_span(operation_name, tags, start_time, child_of)
  else
    self.current_trace = ::Instana::Trace.new(operation_name, tags, nil, start_time)
    span = self.current_trace.current_span
  end
  span.set_tags(tags)
  span
end

#trace(name, kvs = {}, &block) ⇒ Object

Trace a block of code within the context of the exiting trace

Example usage:

::Instana.tracer.trace(:dbwork, { :db_name => @db.name }) do

@db.select(1)

end

Parameters:

  • name (String)

    the name of the span to start

  • kvs (Hash) (defaults to: {})

    list of key values to be reported in this new span



59
60
61
62
63
64
65
66
67
# File 'lib/instana/tracer.rb', line 59

def trace(name, kvs = {}, &block)
  log_entry(name, kvs)
  yield
rescue Exception => e
  log_error(e)
  raise
ensure
  log_exit(name)
end

#trace_idObject

Returns the trace ID for the active trace (if there is one), otherwise nil.



388
389
390
# File 'lib/instana/tracer.rb', line 388

def trace_id
  self.current_trace ? self.current_trace.id : nil
end

#trace_id_headerString

Take the current trace_id and convert it to a header compatible format.

Returns:

  • (String)

    a hexadecimal representation of the current trace ID



372
373
374
# File 'lib/instana/tracer.rb', line 372

def trace_id_header
  ::Instana::Util.id_to_header(trace_id)
end

#tracing?Boolean

Indicates if we’re are currently in the process of collecting a trace. This is false when the host agent isn available.

Returns:

  • (Boolean)

    true or false on whether we are currently tracing or not



337
338
339
340
341
342
# File 'lib/instana/tracer.rb', line 337

def tracing?
  # The non-nil value of this instance variable
  # indicates if we are currently tracing
  # in this thread or not.
  self.current_trace ? true : false
end

#tracing_span?(name) ⇒ Boolean

Indicates if we’re tracing and the current span name matches <name>

Parameters:

  • name (Symbol)

    the name to check against the current span

Returns:

  • (Boolean)


351
352
353
354
355
356
# File 'lib/instana/tracer.rb', line 351

def tracing_span?(name)
  if self.current_trace
    return self.current_trace.current_span.name == name
  end
  false
end