Class: Instana::Span

Inherits:
OpenTelemetry::Trace::Span
  • Object
show all
Includes:
SpanKind
Defined in:
lib/instana/trace/span.rb

Constant Summary

Constants included from SpanKind

Instana::SpanKind::CLIENT, Instana::SpanKind::CONSUMER, Instana::SpanKind::ENTRY, Instana::SpanKind::ENTRY_SPANS, Instana::SpanKind::EXIT, Instana::SpanKind::EXIT_SPANS, Instana::SpanKind::HTTP_SPANS, Instana::SpanKind::INTERMEDIATE, Instana::SpanKind::INTERNAL, Instana::SpanKind::PRODUCER, Instana::SpanKind::REGISTERED_SPANS, Instana::SpanKind::SERVER

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, parent_ctx = nil, _context = nil, parent_span = nil, _kind = nil, parent_span_id = nil, _span_limits = nil, _span_processors = nil, attributes = nil, _links = nil, start_timestamp = ::Instana::Util.now_in_ms, _resource = nil, _instrumentation_scope = nil) ⇒ Span

rubocop:disable Lint/MissingSuper, Metrics/ParameterLists, Layout/LineLength



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/instana/trace/span.rb', line 12

def initialize(name, parent_ctx = nil, _context = nil, parent_span = nil, _kind = nil, parent_span_id = nil, _span_limits = nil, _span_processors = nil, attributes = nil, _links = nil, start_timestamp = ::Instana::Util.now_in_ms, _resource = nil, _instrumentation_scope = nil) # rubocop:disable Lint/MissingSuper, Metrics/ParameterLists, Layout/LineLength
  @attributes = {}

  @ended = false
  if parent_span.is_a?(::Instana::Span)
    @parent = parent_span
  end
  if parent_ctx.is_a?(::Instana::Span)
    @parent = parent_ctx
    parent_ctx = parent_ctx.context
  end

  if parent_ctx.is_a?(::Instana::SpanContext)
    @is_root = false

    # If we have a parent trace, link to it
    if parent_ctx.trace_id
      @attributes[:t] = parent_ctx.trace_id # Trace ID
      @attributes[:p] = parent_span_id || parent_ctx.span_id # Parent ID
    else
      @attributes[:t] = ::Instana::Trace.generate_trace_id
    end

    @attributes[:s] = ::Instana::Trace.generate_span_id # Span ID

    @baggage = parent_ctx.baggage.dup
    @level = parent_ctx.level
  else
    # No parent specified so we're starting a new Trace - this will be the root span
    @is_root = true
    @level = 1

    id = ::Instana::Trace.generate_span_id
    @attributes[:t] = id                    # Trace ID
    @attributes[:s] = id                    # Span ID
  end

  @attributes[:data] = {}

  if ENV.key?('INSTANA_SERVICE_NAME')
    @attributes[:data][:service] = ENV['INSTANA_SERVICE_NAME']
  end

  # Entity Source
  @attributes[:f] = ::Instana.agent.source
  # Start time
  @attributes[:ts] = if start_timestamp.is_a?(Time)
                       ::Instana::Util.time_to_ms(start_timestamp)
                     else
                       start_timestamp
                     end

  # Check for custom tracing
  if REGISTERED_SPANS.include?(name&.to_sym) # Todo remove the safe & operator once all the tests are adapted to new init structure
    @attributes[:n] = name.to_sym
  else
    configure_custom(name)
  end
  set_tags(attributes)
  ::Instana.processor.on_start(self)
  # Attach a backtrace to all exit spans
  add_stack if ::Instana.config[:collect_backtraces] && exit_span?
end

Instance Attribute Details

#baggageObject

Returns the value of attribute baggage.



10
11
12
# File 'lib/instana/trace/span.rb', line 10

def baggage
  @baggage
end

#contextInstana::SpanContext

Retrieve the context of this span.



184
185
186
# File 'lib/instana/trace/span.rb', line 184

def context
  @context
end

#is_rootObject

Returns the value of attribute is_root.



10
11
12
# File 'lib/instana/trace/span.rb', line 10

def is_root
  @is_root
end

#parentObject

Returns the value of attribute parent.



10
11
12
# File 'lib/instana/trace/span.rb', line 10

def parent
  @parent
end

Instance Method Details

#[](key) ⇒ Object

Hash accessor to the internal @attributes hash



248
249
250
# File 'lib/instana/trace/span.rb', line 248

def [](key)
  @attributes[key.to_sym]
end

#[]=(key, value) ⇒ Object

Hash setter to the internal @attributes hash



254
255
256
# File 'lib/instana/trace/span.rb', line 254

def []=(key, value)
  @attributes[key.to_sym] = value
end

#add_attributes(attributes) ⇒ self

Add attributes

Note that the OpenTelemetry project documents certain “standard attributes” that have prescribed semantic meanings.

Parameters:

  • attributes (Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>})

    Values must be non-nil and (array of) string, boolean or numeric type. Array values must not contain nil elements and all elements must be of the same basic type (string, numeric, boolean).

Returns:

  • (self)

    returns itself



468
469
470
471
472
# File 'lib/instana/trace/span.rb', line 468

def add_attributes(attributes)
  @attributes ||= {}
  @attributes.merge!(attributes)
  self
end

#add_event(_name, attributes: nil, timestamp: nil) ⇒ self

Add an event to a Instana::Span.

Example:

span.add_event('event', attributes: {'eager' => true})

Note that the OpenTelemetry project documents certain “standard event names and keys” which have prescribed semantic meanings.

Todo Add the vent logic later

Parameters:

  • name (String)

    Name of the event.

  • attributes (optional Hash{String => String, Numeric, Boolean, Array<String, Numeric, Boolean>}) (defaults to: nil)

    One or more key:value pairs, where the keys must be strings and the values may be (array of) string, boolean or numeric type.

  • timestamp (optional Time) (defaults to: nil)

    Optional timestamp for the event.

Returns:

  • (self)

    returns itself



517
518
519
# File 'lib/instana/trace/span.rb', line 517

def add_event(_name, attributes: nil, timestamp: nil) # rubocop:disable Lint/UnusedMethodArgument
  self
end

Add a link to a Instana::Span.

Adding links at span creation using the ‘links` option is preferred to calling add_link later, because head sampling decisions can only consider information present during span creation.

Example:

span.add_link(OpenTelemetry::Trace::Link.new(span_to_link_from.context))

Note that the OpenTelemetry project documents certain “standard attributes” that have prescribed semantic meanings.

Todo add link logic later

Parameters:

  • the (OpenTelemetry::Trace::Link)

    link object to add on the Instana::Span.

Returns:

  • (self)

    returns itself



493
494
495
# File 'lib/instana/trace/span.rb', line 493

def add_link(_link)
  self
end

#add_stack(limit: 30, stack: Kernel.caller) ⇒ Object

Adds a backtrace to this span

Parameters:

  • limit (Integer) (defaults to: 30)

    Limit the backtrace to the top <limit> frames



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/instana/trace/span.rb', line 80

def add_stack(limit: 30, stack: Kernel.caller)
  cleaner = ::Instana.config[:backtrace_cleaner]
  stack = cleaner.call(stack) if cleaner

  @attributes[:stack] = stack
                        .map do |call|
    file, line, *method = call.split(':')

    {
      c: file,
      n: line,
      m: method.join(' ')
    }
  end.take(limit > 40 ? 40 : limit)
end

#close(end_time = ::Instana::Util.now_in_ms) ⇒ Span

Closes out the span. This difference between this and the finish method tells us how the tracing is being performed (with OpenTracing or Instana default)

Parameters:

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

    custom end time, if not now

Returns:



161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/instana/trace/span.rb', line 161

def close(end_time = ::Instana::Util.now_in_ms)
  result = ::Instana::SpanFiltering.filter_span(self)
  if end_time.is_a?(Time)
    end_time = ::Instana::Util.time_to_ms(end_time)
  end
  @attributes[:d] = end_time - @attributes[:ts]
  @ended = true

  if result.nil?
    # Add this span to the queue for reporting
    ::Instana.processor.on_finish(self)
  end
  self
end

#configure_custom(name) ⇒ Object

Configure this span to be a custom span per the SDK generic span type.

Default to an intermediate kind span. Can be overridden by setting a span.kind tag.

Parameters:

  • name (String)

    name of the span

  • kvs (Hash)

    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
# File 'lib/instana/trace/span.rb', line 138

def configure_custom(name)
  @attributes[:n] = :sdk
  @attributes[:data] = { :sdk => { :name => name&.to_sym } } # Todo remove safe operator once other tests adapt to new init structure
  @attributes[:data][:sdk][:custom] = { :tags => {}, :logs => {} }

  if @is_root
    # For custom root spans (via SDK or opentracing), default to entry type
    @attributes[:k] = 1
    @attributes[:data][:sdk][:type] = :entry
  else
    @attributes[:k] = 3
    @attributes[:data][:sdk][:type] = :intermediate
  end
  self
end

#custom?Boolean

Indicates whether this span is a custom or registered Span

Returns:

  • (Boolean)


271
272
273
# File 'lib/instana/trace/span.rb', line 271

def custom?
  @attributes[:n] == :sdk
end

#durationInteger

Get the duration value for this Span

Returns:

  • (Integer)

    the duration in milliseconds



242
243
244
# File 'lib/instana/trace/span.rb', line 242

def duration
  @attributes[:d]
end

#exit_span?Boolean

Check to see if the current span indicates an exit from application code and into an external service

Returns:

  • (Boolean)


281
282
283
# File 'lib/instana/trace/span.rb', line 281

def exit_span?
  EXIT_SPANS.include?(@attributes[:n])
end

#finish(end_time = ::Instana::Util.now_in_ms) ⇒ Object

Finish the Instana::Span Spec: OpenTracing API

Parameters:

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

    custom end time, if not now



419
420
421
422
423
# File 'lib/instana/trace/span.rb', line 419

def finish(end_time = ::Instana::Util.now_in_ms)
  close(end_time)
  ::Instana.tracer.current_span = ::Instana.tracer.current_span&.parent || nil
  self
end

#get_baggage_item(key) ⇒ Object

Get a baggage item Spec: OpenTracing API

Parameters:

  • key (String)

    the key of the baggage item

Returns:

  • Value of the baggage item



380
381
382
# File 'lib/instana/trace/span.rb', line 380

def get_baggage_item(key)
  @baggage[key]
end

#idInteger

Retrieve the ID for this span

Returns:

  • (Integer)

    the span ID



191
192
193
# File 'lib/instana/trace/span.rb', line 191

def id
  @attributes[:s]
end

#inspectObject



275
276
277
# File 'lib/instana/trace/span.rb', line 275

def inspect
  @attributes.inspect
end

#key?(key) ⇒ Boolean

Hash key query to the internal @attributes hash

Returns:

  • (Boolean)


260
261
262
# File 'lib/instana/trace/span.rb', line 260

def key?(key)
  @attributes.key?(key.to_sym)
end

#log(event = nil, timestamp = Time.now, **fields) ⇒ Object

Add a log entry to this span Spec: OpenTracing API

Parameters:

  • event (String) (defaults to: nil)

    event name for the log

  • timestamp (Time) (defaults to: Time.now)

    time of the log

  • fields (Hash)

    Additional information to log



402
403
404
405
406
407
408
409
410
411
412
# File 'lib/instana/trace/span.rb', line 402

def log(event = nil, timestamp = Time.now, **fields)
  ts = ::Instana::Util.time_to_ms(timestamp).to_s
  if custom?
    @attributes[:data][:sdk][:custom][:logs][ts] = fields
    @attributes[:data][:sdk][:custom][:logs][ts][:event] = event
  else
    set_tags(:log => fields)
  end
rescue StandardError => e
  Instana.logger.debug { "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" }
end

#nameString

Get the name (operation) of this Span

Returns:

  • (String)

    or [Symbol] representing the span name



219
220
221
222
223
224
225
# File 'lib/instana/trace/span.rb', line 219

def name
  if custom?
    @attributes[:data][:sdk][:name]
  else
    @attributes[:n]
  end
end

#name=(name) ⇒ Object

Set the name (operation) for this Span



231
232
233
234
235
236
237
# File 'lib/instana/trace/span.rb', line 231

def name=(name)
  if custom?
    @attributes[:data][:sdk][:name] = name
  else
    @attributes[:n] = name
  end
end

#operation_name=(name) ⇒ Object

Set the name of the operation Spec: OpenTracing API



294
295
296
# File 'lib/instana/trace/span.rb', line 294

def operation_name=(name)
  @attributes[:n] = name
end

#parent_idInteger

Retrieve the parent ID of this span

Returns:

  • (Integer)

    parent span ID



205
206
207
# File 'lib/instana/trace/span.rb', line 205

def parent_id
  @attributes[:p]
end

#parent_id=(id) ⇒ Integer

Set the parent ID of this span

Returns:

  • (Integer)

    parent span ID



212
213
214
# File 'lib/instana/trace/span.rb', line 212

def parent_id=(id)
  @attributes[:p] = id
end

#rawObject

Get the raw @attributes hash that summarizes this span



266
267
268
# File 'lib/instana/trace/span.rb', line 266

def raw
  @attributes
end

#record_exception(error) ⇒ Object

Log an error into the span

Parameters:

  • e (Exception)

    The exception to be logged



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

def record_exception(error)
  @attributes[:error] = true

  @attributes[:ec] = if @attributes.key?(:ec)
                       @attributes[:ec] + 1
                     else
                       1
                     end

  # If a valid exception has been passed in, log the information about it
  # In case of just logging an error for things such as HTTP client 5xx
  # responses, an exception/backtrace may not exist.
  if error
    if error.backtrace.is_a?(Array)
      add_stack(stack: error.backtrace)
    end

    if HTTP_SPANS.include?(@attributes[:n])
      set_tags(:http => { :error => "#{error.class}: #{error.message}" })
    elsif @attributes[:n] == :activerecord
      @attributes[:data][:activerecord][:error] = error.message
    else
      log(:error, Time.now, message: error.message, parameters: error.class.to_s)
    end
    error.instance_variable_set(:@instana_logged, true)
  end
  self
end

#recording?Boolean

Return the flag whether this span is recording events

Returns:

  • (Boolean)

    true if this Span is active and recording information like events with the #add_event operation and attributes using #set_attribute.



430
431
432
# File 'lib/instana/trace/span.rb', line 430

def recording?
  !@ended
end

#set_attribute(key, value) ⇒ self

Set attribute

Note that the OpenTelemetry project documents certain “standard attributes” that have prescribed semantic meanings.

Parameters:

  • key (String)
  • value (String, Boolean, Numeric, Array<String, Numeric, Boolean>)

    Values must be non-nil and (array of) string, boolean or numeric type. Array values must not contain nil elements and all elements must be of the same basic type (string, numeric, boolean).

Returns:

  • (self)

    returns itself



448
449
450
451
452
# File 'lib/instana/trace/span.rb', line 448

def set_attribute(key, value)
  @attributes ||= {}
  @attributes[key] = value
  self
end

#set_baggage_item(key, value) ⇒ Object

Set a baggage item on the span Spec: OpenTracing API

Todo Evalute if baggage is used anywhere in instana

Parameters:

  • key (String)

    the key of the baggage item

  • value (String)

    the value of the baggage item



361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/instana/trace/span.rb', line 361

def set_baggage_item(key, value)
  @baggage ||= {}
  @baggage[key] = value

  # Init/Update the SpanContext item
  if @context
    @context.baggage = @baggage
  else
    @context ||= ::Instana::SpanContext.new(trace_id: @attributes[:t], span_id: @attributes[:s], level: @level, baggage: @baggage)
  end
  self
end

#set_tag(key, value) ⇒ Object

Set a tag value on this span Spec: OpenTracing API

a String, Numeric, or Boolean it will be encoded with to_s

Parameters:

  • key (String)

    the key of the tag

  • value (String, Numeric, Boolean)

    the value of the tag. If it’s not



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/instana/trace/span.rb', line 305

def set_tag(key, value)
  unless [Symbol, String].include?(key.class)
    key = key.to_s
  end

  # If <value> is not a Symbol, String, Array, Hash or Numeric - convert to string
  if ![Symbol, String, Array, TrueClass, FalseClass, Hash].include?(value.class) && !value.is_a?(Numeric)
    value = value.to_s
  end

  if custom?
    @attributes[:data][:sdk][:custom] ||= {}
    @attributes[:data][:sdk][:custom][:tags] ||= {}
    @attributes[:data][:sdk][:custom][:tags][key] = value

    if key.to_sym == :'span.kind'
      case value.to_sym
      when ENTRY, SERVER, CONSUMER
        @attributes[:data][:sdk][:type] = ENTRY
        @attributes[:k] = 1
      when EXIT, CLIENT, PRODUCER
        @attributes[:data][:sdk][:type] = EXIT
        @attributes[:k] = 2
      else
        @attributes[:data][:sdk][:type] = INTERMEDIATE
        @attributes[:k] = 3
      end
    end
  elsif value.is_a?(Hash) && @attributes[:data][key].is_a?(Hash)
    @attributes[:data][key].merge!(value)
  else
    @attributes[:data][key] = value
  end
  self
end

#set_tags(tags) ⇒ Span

Helper method to add multiple tags to this span

Returns:



346
347
348
349
350
351
352
353
# File 'lib/instana/trace/span.rb', line 346

def set_tags(tags) # rubocop:disable Naming
  return unless tags.is_a?(Hash)

  tags.each do |k, v|
    set_tag(k, v)
  end
  self
end

#status=(status) ⇒ void

This method returns an undefined value.

Sets the Status to the Span

If used, this will override the default Span status. Default status is unset.

Only the value of the last call will be recorded, and implementations are free to ignore previous calls.

Parameters:

  • status (Status)

    The new status, which overrides the default Span status, which is OK.



532
# File 'lib/instana/trace/span.rb', line 532

def status=(status); end

#tags(key = nil) ⇒ Object

Retrieve the hash of tags for this span



386
387
388
389
390
391
392
393
# File 'lib/instana/trace/span.rb', line 386

def tags(key = nil)
  tags = if custom?
           @attributes[:data][:sdk][:custom][:tags]
         else
           @attributes[:data]
         end
  key ? tags[key] : tags
end

#trace_idInteger

Retrieve the Trace ID for this span

Returns:

  • (Integer)

    the Trace ID



198
199
200
# File 'lib/instana/trace/span.rb', line 198

def trace_id
  @attributes[:t]
end