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.



403
404
405
# File 'lib/instana/tracer.rb', line 403

def clear!
  self.current_trace = nil
end

#contextSpanContext

Retrieve the current context of the tracer.

Returns:



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

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.



391
392
393
# File 'lib/instana/tracer.rb', line 391

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)


397
398
399
# File 'lib/instana/tracer.rb', line 397

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



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

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:



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

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



176
177
178
179
# File 'lib/instana/tracer.rb', line 176

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



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

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



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

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



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

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



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

def log_end(name, kvs = {}, end_time = ::Instana::Util.now_in_ms)
  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 ::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



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.



385
386
387
# File 'lib/instana/tracer.rb', line 385

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



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

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: ::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:



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

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



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.



378
379
380
# File 'lib/instana/tracer.rb', line 378

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



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

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



327
328
329
330
331
332
# File 'lib/instana/tracer.rb', line 327

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)


341
342
343
344
345
346
# File 'lib/instana/tracer.rb', line 341

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