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
  add_attributes(attributes)
  ::Instana.processor.on_start(self)
  # Attach a backtrace to all exit spans
  add_stack if should_collect_stack_trace?
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.



188
189
190
# File 'lib/instana/trace/span.rb', line 188

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



252
253
254
# File 'lib/instana/trace/span.rb', line 252

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

#[]=(key, value) ⇒ Object

Hash setter to the internal @attributes hash



258
259
260
# File 'lib/instana/trace/span.rb', line 258

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



471
472
473
474
475
476
477
478
# File 'lib/instana/trace/span.rb', line 471

def add_attributes(attributes)
  return unless attributes.is_a?(Hash)

  attributes.each do |k, v|
    set_tag(k, v)
  end
  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



523
524
525
# File 'lib/instana/trace/span.rb', line 523

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



499
500
501
# File 'lib/instana/trace/span.rb', line 499

def add_link(_link)
  self
end

#add_stack(span_stack_config: nil, stack: Kernel.caller) ⇒ Object

Adds a backtrace to this span

Parameters:

  • limit (Integer)

    Limit the backtrace to the top <limit> frames



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

def add_stack(span_stack_config: nil, stack: Kernel.caller)
  # Get technology-specific config if not provided
  span_stack_config ||= current_span_stack_config

  limit = span_stack_config[:stack_trace_length]
  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].min)
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:



165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/instana/trace/span.rb', line 165

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



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/instana/trace/span.rb', line 142

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

#current_span_stack_configHash

Get the stack trace configuration for this span’s technology Falls back to global configuration if technology-specific config is not found

Returns:

  • (Hash)

    Configuration hash with :stack_trace_level and :stack_trace_length



550
551
552
553
# File 'lib/instana/trace/span.rb', line 550

def current_span_stack_config
  technology = @attributes[:n]
  ::Instana.config.get_stack_trace_config(technology)
end

#custom?Boolean

Indicates whether this span is a custom or registered Span

Returns:

  • (Boolean)


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

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

#durationInteger

Get the duration value for this Span

Returns:

  • (Integer)

    the duration in milliseconds



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

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)


285
286
287
# File 'lib/instana/trace/span.rb', line 285

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



423
424
425
426
427
# File 'lib/instana/trace/span.rb', line 423

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



384
385
386
# File 'lib/instana/trace/span.rb', line 384

def get_baggage_item(key)
  @baggage[key]
end

#idInteger

Retrieve the ID for this span

Returns:

  • (Integer)

    the span ID



195
196
197
# File 'lib/instana/trace/span.rb', line 195

def id
  @attributes[:s]
end

#inspectObject



279
280
281
# File 'lib/instana/trace/span.rb', line 279

def inspect
  @attributes.inspect
end

#key?(key) ⇒ Boolean

Hash key query to the internal @attributes hash

Returns:

  • (Boolean)


264
265
266
# File 'lib/instana/trace/span.rb', line 264

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



406
407
408
409
410
411
412
413
414
415
416
# File 'lib/instana/trace/span.rb', line 406

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



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

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

#name=(name) ⇒ Object

Set the name (operation) for this Span



235
236
237
238
239
240
241
# File 'lib/instana/trace/span.rb', line 235

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



298
299
300
# File 'lib/instana/trace/span.rb', line 298

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

#parent_idInteger

Retrieve the parent ID of this span

Returns:

  • (Integer)

    parent span ID



209
210
211
# File 'lib/instana/trace/span.rb', line 209

def parent_id
  @attributes[:p]
end

#parent_id=(id) ⇒ Integer

Set the parent ID of this span

Returns:

  • (Integer)

    parent span ID



216
217
218
# File 'lib/instana/trace/span.rb', line 216

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

#rawObject

Get the raw @attributes hash that summarizes this span



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

def raw
  @attributes
end

#record_exception(error) ⇒ Object

Log an error into the span

Parameters:

  • e (Exception)

    The exception to be logged



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

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.



434
435
436
# File 'lib/instana/trace/span.rb', line 434

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



452
453
454
455
# File 'lib/instana/trace/span.rb', line 452

def set_attribute(key, value)
  set_tag(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



365
366
367
368
369
370
371
372
373
374
375
376
# File 'lib/instana/trace/span.rb', line 365

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



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
340
341
342
343
# File 'lib/instana/trace/span.rb', line 309

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:



350
351
352
353
354
355
356
357
# File 'lib/instana/trace/span.rb', line 350

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

#should_collect_stack_trace?Boolean

Returns:

  • (Boolean)


540
541
542
543
544
545
# File 'lib/instana/trace/span.rb', line 540

def should_collect_stack_trace?
  return false unless exit_span?

  config = current_span_stack_config
  config[:stack_trace_level] == "all"
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.



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

def status=(status); end

#tags(key = nil) ⇒ Object

Retrieve the hash of tags for this span



390
391
392
393
394
395
396
397
# File 'lib/instana/trace/span.rb', line 390

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



202
203
204
# File 'lib/instana/trace/span.rb', line 202

def trace_id
  @attributes[:t]
end