Class: Instana::Tracer

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

Instance Method Summary collapse

Methods included from ThreadLocal

thread_local

Instance Method Details

#clear!Object

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



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

def clear!
  self.current_trace = nil
end

#contextSpanContext

Retrieve the current context of the tracer.

Returns:



350
351
352
353
# File 'lib/instana/tracer.rb', line 350

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.



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

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)


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

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

#extract(format, carrier) ⇒ SpanContext

Extract a span from a carrier



302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/instana/tracer.rb', line 302

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:



283
284
285
286
287
288
289
290
291
292
293
# File 'lib/instana/tracer.rb', line 283

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



174
175
176
177
# File 'lib/instana/tracer.rb', line 174

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`)



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/instana/tracer.rb', line 208

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`)



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/instana/tracer.rb', line 232

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`)



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/instana/tracer.rb', line 185

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 = Time.now) ⇒ 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



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/instana/tracer.rb', line 138

def log_end(name, kvs = {}, end_time = Time.now)
  return unless tracing?

  if ::Instana.debug? || ::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 !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
  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



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

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



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

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



117
118
119
120
121
122
123
124
125
126
127
# File 'lib/instana/tracer.rb', line 117

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

  if ::Instana.debug? || ::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



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

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)
    


74
75
76
77
78
# File 'lib/instana/tracer.rb', line 74

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.



383
384
385
# File 'lib/instana/tracer.rb', line 383

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



369
370
371
# File 'lib/instana/tracer.rb', line 369

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)
    


27
28
29
30
31
32
33
34
35
# File 'lib/instana/tracer.rb', line 27

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

#start_span(operation_name, child_of: nil, start_time: Time.now, 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: Time.now)

    the start time of the span

  • tags (Hash) (defaults to: nil)

    Starting tags for the span

Returns:



264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/instana/tracer.rb', line 264

def start_span(operation_name, child_of: nil, start_time: Time.now, 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



48
49
50
51
52
53
54
55
56
57
# File 'lib/instana/tracer.rb', line 48

def trace(name, kvs = {}, &block)
  log_entry(name, kvs)
  result = block.call
  result
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.



376
377
378
# File 'lib/instana/tracer.rb', line 376

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



360
361
362
# File 'lib/instana/tracer.rb', line 360

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



325
326
327
328
329
330
# File 'lib/instana/tracer.rb', line 325

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)


339
340
341
342
343
344
# File 'lib/instana/tracer.rb', line 339

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