Class: Instana::Tracer

Inherits:
OpenTelemetry::Trace::Tracer
  • Object
show all
Defined in:
lib/instana/trace/tracer.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(_name, _version, tracer_provider, logger = Instana.logger) ⇒ Tracer

Returns a new instance of Tracer.



20
21
22
23
24
25
# File 'lib/instana/trace/tracer.rb', line 20

def initialize(_name, _version, tracer_provider, logger = Instana.logger)
  super()
  @tracer_provider = tracer_provider
  @current_span = Concurrent::ThreadLocalVar.new
  @logger = logger
end

Class Method Details

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

rubocop:disable Style/MissingRespondToMissing



11
12
13
14
15
16
17
# File 'lib/instana/trace/tracer.rb', line 11

def method_missing(method, *args, &block) # rubocop:disable Style/MissingRespondToMissing
  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.



306
307
308
# File 'lib/instana/trace/tracer.rb', line 306

def clear!
  self.current_span = nil
end

#contextSpanContext

Retrieve the current context of the tracer.

Returns:



298
299
300
301
302
# File 'lib/instana/trace/tracer.rb', line 298

def context
  return unless current_span

  current_span.context
end

#current_spanInstana::Span, NilClass

Returns the current active span or nil if we are not tracing.

Returns:

  • (Instana::Span, NilClass)

    the current active span or nil if we are not tracing



28
29
30
# File 'lib/instana/trace/tracer.rb', line 28

def current_span
  @current_span.value
end

#current_span=(value) ⇒ Object

Set the value of the current span

Parameters:



34
35
36
# File 'lib/instana/trace/tracer.rb', line 34

def current_span=(value)
  @current_span.value = value
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



224
225
226
227
228
229
230
# File 'lib/instana/trace/tracer.rb', line 224

def log_async_entry(name, kvs)
  return unless tracing?

  new_span = Span.new(name, current_span)
  new_span.set_tags(kvs) unless kvs.empty?
  new_span
end

#log_async_error(error, 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`)



246
247
248
# File 'lib/instana/trace/tracer.rb', line 246

def log_async_error(error, span)
  span.record_exception(error)
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 additional key/values to be reported in the span (or use {})

  • span (Span)

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



256
257
258
259
# File 'lib/instana/trace/tracer.rb', line 256

def log_async_exit(_name, kvs, span)
  span.set_tags(kvs) unless kvs.empty?
  span.close
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`)



237
238
239
# File 'lib/instana/trace/tracer.rb', line 237

def log_async_info(kvs, span)
  span.set_tags(kvs)
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



199
200
201
202
203
204
205
206
207
208
209
# File 'lib/instana/trace/tracer.rb', line 199

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

  if current_span.name != name
    @logger.warn "Span mismatch: Attempt to end #{name} span but #{current_span.name} is active."
  end

  current_span.set_tags(kvs)
  current_span.close(end_time)
  self.current_span = nil
end

#log_entry(name, kvs = nil, _start_time = ::Instana::Util.now_in_ms, child_of = nil) ⇒ Object

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

Parameters:

  • name (String, Symbol)

    the name of the span to create

  • kvs (Hash) (defaults to: nil)

    list of key values to be reported in the span



137
138
139
140
141
142
143
144
145
146
147
# File 'lib/instana/trace/tracer.rb', line 137

def log_entry(name, kvs = nil, _start_time = ::Instana::Util.now_in_ms, child_of = nil)
  return unless tracing? || child_of

  new_span = if child_of.nil? && !current_span.nil?
               Span.new(name, current_span)
             else
               Span.new(name, child_of)
             end
  new_span.set_tags(kvs) if kvs
  self.current_span = new_span
end

#log_error(error) ⇒ Object

Add an error to the current span

Parameters:

  • e (Exception)

    Add exception to the current span



163
164
165
166
167
# File 'lib/instana/trace/tracer.rb', line 163

def log_error(error)
  return unless current_span

  current_span.record_exception(error)
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, Symbol)

    the name of the span to exit (close out)

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

    list of key values to be reported in the span



177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/instana/trace/tracer.rb', line 177

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

  if current_span.name != name
    @logger.warn "Span mismatch: Attempt to end #{name} span but #{current_span.name} is active."
  end

  current_span.set_tags(kvs)
  current_span.close

  self.current_span = current_span.parent || nil
end

#log_info(kvs) ⇒ Object

Add info to the current span

Parameters:

  • kvs (Hash)

    list of key values to be reported in the span



153
154
155
156
157
# File 'lib/instana/trace/tracer.rb', line 153

def log_info(kvs)
  return unless current_span

  current_span.set_tags(kvs)
end

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

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

Parameters:

  • name (String, Symbol)

    the name of the span to start

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

    list of key values to be reported in the span

  • incoming_context (SpanContext or Hash) (defaults to: nil)

    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)
    


99
100
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
# File 'lib/instana/trace/tracer.rb', line 99

def log_start_or_continue(name, kvs = {}, incoming_context = nil)
  return if !::Instana.agent.ready? || !::Instana.config[:tracing][:enabled]

  # Handle the potential variations on `incoming_context`
  if incoming_context
    if incoming_context.is_a?(Hash)
      unless incoming_context.empty?
        parent_context = SpanContext.new(
          trace_id: incoming_context[:trace_id],
          span_id: incoming_context[:span_id],
          level: incoming_context[:level],
          baggage: {
            external_trace_id: incoming_context[:external_trace_id],
            external_state: incoming_context[:external_state]
          }
        )
      end
    else
      parent_context = incoming_context
    end
  end

  self.current_span = if parent_context
                        Span.new(name, parent_context)
                      else
                        Span.new(name)
                      end

  current_span.set_tags(kvs) unless kvs.empty?
  current_span
end

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

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

Parameters:

  • name (String, Symbol)

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

    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)
    


53
54
55
56
57
58
59
60
61
# File 'lib/instana/trace/tracer.rb', line 53

def start_or_continue_trace(name, kvs = {}, incoming_context = nil)
  span = log_start_or_continue(name, kvs, incoming_context)
  yield(span)
rescue Exception => e # rubocop:disable Lint/RescueException
  log_error(e)
  raise
ensure
  log_end(name)
end

#start_span(name, with_parent: nil, attributes: nil, links: nil, start_timestamp: ::Instana::Util.now_in_ms, kind: nil) ⇒ Span

Note:

This method will only create a span if the Instana agent is ready and tracing is enabled. Default values are set for nil parameters: name=‘empty’, kind=:internal, with_parent=current context, start_timestamp=current time.

Starts a new span with the given parameters.

Parameters:

  • name (String, Symbol)

    the name of the span to create (defaults to ‘empty’ if nil)

  • with_parent (Context, nil) (defaults to: nil)

    the parent context for the span (defaults to current context if nil)

  • attributes (Hash, nil) (defaults to: nil)

    optional attributes to set on the span

  • links (Array<Link>, nil) (defaults to: nil)

    optional links to associate with the span

  • start_timestamp (Integer, nil) (defaults to: ::Instana::Util.now_in_ms)

    optional start time for the span in milliseconds

  • kind (Symbol, nil) (defaults to: nil)

    optional span kind (defaults to :internal if nil)

Returns:

  • (Span)

    the newly created span



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

def start_span(name, with_parent: nil, attributes: nil, links: nil, start_timestamp: ::Instana::Util.now_in_ms, kind: nil) # rubocop:disable Metrics/ParameterLists
  return if !::Instana.agent.ready? || !::Instana.config[:tracing][:enabled]

  with_parent ||= OpenTelemetry::Context.current
  name ||= 'empty'
  kind ||= :internal
  start_timestamp ||= ::Instana::Util.now_in_ms
  self.current_span = @tracer_provider.internal_start_span(name, kind, attributes, links, start_timestamp, with_parent, @instrumentation_scope)
end

#trace(name, kvs = {}) ⇒ 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, Symbol)

    the name of the span to start

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

    list of key values to be reported in this new span



74
75
76
77
78
79
80
81
82
# File 'lib/instana/trace/tracer.rb', line 74

def trace(name, kvs = {})
  span = log_entry(name, kvs)
  yield(span)
rescue Exception => e # rubocop:disable Lint/RescueException
  log_error(e)
  raise
ensure
  log_exit(name)
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



271
272
273
274
275
276
277
# File 'lib/instana/trace/tracer.rb', line 271

def tracing?
  # The non-nil value of this instance variable
  # indicates if we are currently tracing
  # in this thread or not.
  (current_span ? true : false) ||
    (::Instana.config[:allow_exit_as_root] && ::Instana.config[:tracing][:enabled])
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)


286
287
288
289
290
291
292
# File 'lib/instana/trace/tracer.rb', line 286

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

  false
end