Class: Instana::Trace

Inherits:
Object
  • Object
show all
Defined in:
lib/instana/tracing/trace.rb

Constant Summary collapse

REGISTERED_SPANS =
[ :rack, :'net-http', :excon ]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, kvs = {}, incoming_context = {}) ⇒ Trace

Initializes a new instance of Trace

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)
    


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

def initialize(name, kvs = {}, incoming_context = {})
  # The collection of spans that make
  # up this trace.
  @spans = Set.new

  # The current active span
  @current_span = nil

  # Generate a random 64bit ID for this trace
  @id = generate_id

  # Indicates the time when this trace was started.  Used to timeout
  # traces that have asynchronous spans that never close out.
  @started_at = Time.now

  # Indicates if this trace has any asynchronous spans within it
  @has_async = false

  # This is a new trace so open the first span with the proper
  # root span IDs.
  @current_span = Span.new({
    :s => @id,         # Span ID
    :ts => ts_now,     # Timestamp
    :ta => :ruby,      # Agent
    :f => { :e => ::Instana.agent.report_pid, :h => ::Instana.agent.agent_uuid } # Entity Source
  })

  # Check for custom tracing
  if !REGISTERED_SPANS.include?(name.to_sym)
    configure_custom_span(nil, name, kvs)
  else
    @current_span[:n]    = name.to_sym
    @current_span[:data] = kvs
  end

  # Handle potential incoming context
  if incoming_context.empty?
    # No incoming context. Set trace ID the same
    # as this first span.
    @current_span[:t] = @id
  else
    @id = incoming_context[:trace_id]
    @current_span[:t] = incoming_context[:trace_id]
    @current_span[:p] = incoming_context[:span_id]
  end

  @spans.add(@current_span)
end

Instance Attribute Details

#idInteger (readonly)

Returns the ID for this trace.

Returns:

  • (Integer)

    the ID for this trace



6
7
8
# File 'lib/instana/tracing/trace.rb', line 6

def id
  @id
end

#spansSet (readonly)

The collection of ‘Span` for this trace

Returns:

  • (Set)

    the collection of spans for this trace



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

def spans
  @spans
end

Instance Method Details

#add_async_error(e, ids) ⇒ Object

Log an error into an asynchronous span

Parameters:

  • span (Span)

    the span to configure

  • e (Exception)

    Add exception to the current span



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

def add_async_error(e, ids)
  @spans.each do |span|
    add_error(e, span) if span.id == ids[:span_id]
  end
end

#add_async_info(kvs, ids) ⇒ Object

Log info into an asynchronous span

Parameters:

  • kvs (Hash)

    list of key values to be reported in the span

  • span (Span)

    the span to configure



212
213
214
215
216
217
218
# File 'lib/instana/tracing/trace.rb', line 212

def add_async_info(kvs, ids)
  @spans.each do |span|
    if span.id == ids[:span_id]
      add_info(kvs, span)
    end
  end
end

#add_error(e, span = nil) ⇒ Object

Log an error into the current span

Parameters:

  • e (Exception)

    Add exception to the current span



132
133
134
135
136
137
138
139
140
141
142
# File 'lib/instana/tracing/trace.rb', line 132

def add_error(e, span = nil)
  span ||= @current_span

  span[:error] = true

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

#add_info(kvs, span = nil) ⇒ Object

Add KVs to the current span

Parameters:

  • span (Span) (defaults to: nil)

    the span to add kvs to or otherwise the current span

  • kvs (Hash)

    list of key values to be reported in the span



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/instana/tracing/trace.rb', line 106

def add_info(kvs, span = nil)
  span ||= @current_span

  if span.custom?
    if span[:data][:sdk].key?(:custom)
      span[:data][:sdk][:custom].merge!(kvs)
    else
      span[:data][:sdk][:custom] = kvs
    end
  else
    kvs.each_pair do |k,v|
      if !span[:data].key?(k)
        span[:data][k] = v
      elsif v.is_a?(Hash) && span[:data][k].is_a?(Hash)
        span[:data][k].merge!(v)
      else
        span[:data][k] = v
      end
    end
  end
end

#complete?Boolean

Indicates if every span of this trace has completed. Useful when asynchronous spans potentially could run longer than the parent trace.

Returns:

  • (Boolean)


268
269
270
271
272
273
274
275
# File 'lib/instana/tracing/trace.rb', line 268

def complete?
  @spans.each do |span|
    if !span.duration
      return false
    end
  end
  true
end

#current_span_idInteger

Get the ID of the current span for this trace. Used often to place in HTTP response headers.

Returns:

  • (Integer)

    a random 64bit integer



305
306
307
# File 'lib/instana/tracing/trace.rb', line 305

def current_span_id
  @current_span.id
end

#current_span_nameObject

Get the name of the current span. Supports both registered spans and custom sdk spans.



312
313
314
# File 'lib/instana/tracing/trace.rb', line 312

def current_span_name
  @current_span.name
end

#current_span_name?(name) ⇒ Boolean

Check if the current span has the name value of <name>

Parameters:

  • name (Symbol)

    The name to be checked against.

Returns:

  • (Boolean)


322
323
324
# File 'lib/instana/tracing/trace.rb', line 322

def current_span_name?(name)
  @current_span.name == name
end

#discard?Boolean

For traces that have asynchronous spans, this method indicates whether we have hit the timeout on waiting for those async spans to close out.

Returns:

  • (Boolean)


332
333
334
335
336
337
338
339
# File 'lib/instana/tracing/trace.rb', line 332

def discard?
  # If this trace has async spans that have not closed
  # out in 5 minutes, then it's discarded.
  if has_async? && (Time.now.to_i - @started_at.to_i) > 601
    return true
  end
  false
end

#end_async_span(kvs = {}, ids) ⇒ Object

End an asynchronous span

Parameters:

  • name (Symbol)

    the name of the span

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

    list of key values to be reported in the span

  • ids (Hash)

    the Trace ID and Span ID in the form of :trace_id => 12345 :span_id => 12345



239
240
241
242
243
244
245
246
# File 'lib/instana/tracing/trace.rb', line 239

def end_async_span(kvs = {}, ids)
  @spans.each do |span|
    if span.id == ids[:span_id]
      span[:d] = ts_now - span[:ts]
      add_info(kvs, span) unless kvs.empty?
    end
  end
end

#end_span(kvs = {}) ⇒ Object

Close out the current span and set the parent as the current span

Parameters:

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

    list of key values to be reported in the span



149
150
151
152
153
# File 'lib/instana/tracing/trace.rb', line 149

def end_span(kvs = {})
  @current_span[:d] = ts_now - @current_span[:ts]
  add_info(kvs) unless kvs.empty?
  @current_span = @current_span.parent unless @current_span.is_root?
end

#finish(kvs = {}) ⇒ Object

Closes out the final span in this trace and runs any finalizer steps required. This should be called only on the root span to end the trace.

Parameters:

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

    list of key values to be reported in the span



161
162
163
# File 'lib/instana/tracing/trace.rb', line 161

def finish(kvs = {})
  end_span(kvs)
end

#has_async?Boolean

Indicates whether this trace has any asynchronous spans.

Returns:

  • (Boolean)


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

def has_async?
  @has_async
end

#has_error?Boolean

Searches the set of spans and indicates if there is an error logged in one of them.

Returns:

  • (Boolean)

    true or false indicating the presence of an error



289
290
291
292
293
294
295
296
297
298
# File 'lib/instana/tracing/trace.rb', line 289

def has_error?
  @spans.each do |s|
    if s.key?(:error)
      if s[:error] == true
        return true
      end
    end
  end
  false
end

#new_async_span(name, kvs) ⇒ Object

Start a new asynchronous span

The major differentiator between this method and simple new_span is that this method doesn’t affect @current_trace and instead returns an ID pair that can be used later to close out the created async span.

Parameters:

  • name (String)

    the name of the span to start

  • kvs (Hash)

    list of key values to be reported in the span



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/instana/tracing/trace.rb', line 178

def new_async_span(name, kvs)

  new_span = Span.new({
    :s => generate_id,          # Span ID
    :t => @id,                  # Trace ID (same as :s for root span)
    :p => @current_span.id,     # Parent ID
    :ts => ts_now,              # Timestamp
    :ta => :ruby,               # Agent
    :async => true,             # Asynchonous
    :f => { :e => Process.pid, :h => :agent_id } # Entity Source
  })

  new_span.parent = @current_span
  @has_async = true

  # Check for custom tracing
  if !REGISTERED_SPANS.include?(name.to_sym)
    configure_custom_span(new_span, name, kvs)
  else
    new_span[:n]    = name.to_sym
    new_span[:data] = kvs
  end

  # Add the new span to the span collection
  @spans.add(new_span)

  { :trace_id => new_span[:t], :span_id => new_span.id }
end

#new_span(name, kvs) ⇒ Object

Start a new span as a child of @current_span

Parameters:

  • name (String)

    the name of the span to start

  • kvs (Hash)

    list of key values to be reported in the span



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

def new_span(name, kvs)
  return unless @current_span

  new_span = Span.new({
    :s => generate_id,          # Span ID
    :t => @id,                  # Trace ID (same as :s for root span)
    :p => @current_span.id,     # Parent ID
    :ts => ts_now,              # Timestamp
    :ta => :ruby,               # Agent
    :f => { :e => Process.pid, :h => :agent_id } # Entity Source
  })

  new_span.parent = @current_span
  @spans.add(new_span)
  @current_span = new_span

  # Check for custom tracing
  if !REGISTERED_SPANS.include?(name.to_sym)
    configure_custom_span(nil, name, kvs)
  else
    @current_span[:n]    = name.to_sym
    @current_span[:data] = kvs
  end
end

#valid?Boolean

Indicates whether all seems ok with this trace in it’s current state. Should be only called on finished traces.

Returns:

  • (Boolean)

    true or false on whether this trace is valid



257
258
259
260
261
262
263
# File 'lib/instana/tracing/trace.rb', line 257

def valid?
  @spans.each do |span|
    unless span.key?(:d)
      return false
    end
  end
end