Class: NewRelic::Agent::Tracer

Inherits:
Object
  • Object
show all
Defined in:
lib/new_relic/agent/tracer.rb

Overview

This class helps you interact with the current transaction (if it exists), start new transactions/segments, etc.

Defined Under Namespace

Classes: State

Constant Summary collapse

UNKNOWN =
'Unknown'.freeze
OTHER =
'other'.freeze

Class Method Summary collapse

Class Method Details

.accept_distributed_trace_payload(payload) ⇒ Object



196
197
198
199
200
# File 'lib/new_relic/agent/tracer.rb', line 196

def accept_distributed_trace_payload(payload)
  return unless txn = current_transaction

  txn.distributed_tracer.accept_distributed_trace_payload(payload)
end

.capture_segment_error(segment) ⇒ Object

Will potentially capture and notice an error at the segment that was executing when error occurred. if passed segment is something that doesn’t respond to notice_segment_error then this method is effectively just a yield to the given &block



354
355
356
357
358
359
360
361
362
# File 'lib/new_relic/agent/tracer.rb', line 354

def capture_segment_error(segment)
  return unless block_given?

  yield
rescue => exception
  # needs else branch coverage
  segment.notice_error(exception) if segment&.is_a?(Transaction::AbstractSegment)
  raise
end

.clear_stateObject Also known as: tl_clear



407
408
409
# File 'lib/new_relic/agent/tracer.rb', line 407

def clear_state
  ThreadLocalStorage[:newrelic_tracer_state] = nil
end

.create_distributed_trace_payloadObject



190
191
192
193
194
# File 'lib/new_relic/agent/tracer.rb', line 190

def create_distributed_trace_payload
  return unless txn = current_transaction

  txn.distributed_tracer.create_distributed_trace_payload
end

.current_segmentObject

Returns the currently active segment in the transaction in progress for this thread, or nil if no segment or transaction exists.



207
208
209
210
211
# File 'lib/new_relic/agent/tracer.rb', line 207

def current_segment
  return unless txn = current_transaction

  txn.current_segment
end

.current_segment_keyObject



413
414
415
# File 'lib/new_relic/agent/tracer.rb', line 413

def current_segment_key
  ::Fiber.current.object_id
end

.current_span_idObject Also known as: span_id

Returns the id of the current span, or nil if none exists.



57
58
59
60
61
# File 'lib/new_relic/agent/tracer.rb', line 57

def current_span_id
  if span = current_segment
    span.guid
  end
end

.current_trace_idObject Also known as: trace_id

Returns the trace_id of the current_transaction, or nil if none exists.



47
48
49
50
51
# File 'lib/new_relic/agent/tracer.rb', line 47

def current_trace_id
  if txn = current_transaction
    txn.trace_id
  end
end

.current_transactionObject

Returns the transaction in progress for this thread, or nil if none exists.



39
40
41
# File 'lib/new_relic/agent/tracer.rb', line 39

def current_transaction
  state.current_transaction
end

.in_transaction(name: nil, partial_name: nil, category:, options: {}) ⇒ Object

Runs the given block of code in a transaction.

Parameters:

  • name (String) (defaults to: nil)

    reserved for New Relic internal use

  • partial_name (String) (defaults to: nil)

    a meaningful name for this transaction (e.g., blogs/index); the Ruby agent will add a New-Relic-specific prefix

  • category (Symbol)

    :web for web transactions or :background for background transactions

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

    reserved for New Relic internal use



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/new_relic/agent/tracer.rb', line 90

def in_transaction(name: nil,
  partial_name: nil,
  category:,
  options: {})

  finishable = start_transaction_or_segment(
    name: name,
    partial_name: partial_name,
    category: category,
    options: options
  )

  begin
    # We shouldn't raise from Tracer.start_transaction_or_segment, but
    # only wrap the yield to be absolutely sure we don't report agent
    # problems as app errors
    yield
  rescue => exception
    current_transaction.notice_error(exception)
    raise
  ensure
    finishable&.finish
  end
end

.start_datastore_segment(product: nil, operation: nil, collection: nil, host: nil, port_path_or_id: nil, database_name: nil, start_time: nil, parent: nil) ⇒ DatastoreSegment

Creates and starts a datastore segment used to time datastore operations.

Parameters:

  • product (String) (defaults to: nil)

    the datastore name for use in metric naming, e.g. “FauxDB”

  • operation (String) (defaults to: nil)

    the name of the operation (e.g. “select”), often named after the method that’s being instrumented.

  • collection (optional, String) (defaults to: nil)

    the collection name for use in statement-level metrics (i.e. table or model name)

  • host (optional, String) (defaults to: nil)

    the host this database instance is running on

  • port_path_or_id (optional, String) (defaults to: nil)

    TCP port, file path, UNIX domain socket, or other connection-related info

  • database_name (optional, String) (defaults to: nil)

    the name of this database

  • start_time (optional, Time) (defaults to: nil)

    a Time instance denoting the start time of the segment. Value is set by AbstractSegment#start if not given.

  • parent (optional, Segment) (defaults to: nil)

    Use for the rare cases (such as async) where the parent segment should be something other than the current segment

Returns:

  • (DatastoreSegment)

    the newly created segment; you must call finish on it at the end of the code you’re tracing



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/new_relic/agent/tracer.rb', line 288

def start_datastore_segment(product: nil,
  operation: nil,
  collection: nil,
  host: nil,
  port_path_or_id: nil,
  database_name: nil,
  start_time: nil,
  parent: nil)

  product ||= UNKNOWN
  operation ||= OTHER

  segment = Transaction::DatastoreSegment.new(product, operation, collection, host, port_path_or_id, database_name)
  start_and_add_segment(segment, parent)
rescue ArgumentError
  raise
rescue => exception
  log_error('start_datastore_segment', exception)
end

.start_external_request_segment(library:, uri:, procedure:, start_time: nil, parent: nil) ⇒ ExternalRequestSegment

Creates and starts an external request segment using the given library, URI, and procedure. This is used to time external calls made over HTTP.

Parameters:

  • library (String)

    a string of the class name of the library used to make the external call, for example, ‘Net::HTTP’.

  • uri (String, URI)

    indicates the URI to which the external request is being made. The URI should begin with the protocol, for example, ‘github.com’.

  • procedure (String)

    the HTTP method being used for the external request as a string, for example, ‘GET’.

  • start_time (optional, Time) (defaults to: nil)

    a Time instance denoting the start time of the segment. Value is set by AbstractSegment#start if not given.

  • parent (optional, Segment) (defaults to: nil)

    Use for the rare cases (such as async) where the parent segment should be something other than the current segment

Returns:

  • (ExternalRequestSegment)

    the newly created segment; you must call finish on it at the end of the code you’re tracing



335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/new_relic/agent/tracer.rb', line 335

def start_external_request_segment(library:,
  uri:,
  procedure:,
  start_time: nil,
  parent: nil)

  segment = Transaction::ExternalRequestSegment.new(library, uri, procedure, start_time)
  start_and_add_segment(segment, parent)
rescue ArgumentError
  raise
rescue => exception
  log_error('start_external_request_segment', exception)
end

.start_message_broker_segment(action:, library:, destination_type:, destination_name:, headers: nil, parameters: nil, start_time: nil, parent: nil) ⇒ Object

For New Relic internal use only.



365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# File 'lib/new_relic/agent/tracer.rb', line 365

def start_message_broker_segment(action:,
  library:,
  destination_type:,
  destination_name:,
  headers: nil,
  parameters: nil,
  start_time: nil,
  parent: nil)

  segment = Transaction::MessageBrokerSegment.new(
    action: action,
    library: library,
    destination_type: destination_type,
    destination_name: destination_name,
    headers: headers,
    parameters: parameters,
    start_time: start_time
  )
  start_and_add_segment(segment, parent)
rescue ArgumentError
  raise
rescue => exception
  log_error('start_datastore_segment', exception)
end

.start_segment(name:, unscoped_metrics: nil, start_time: nil, parent: nil) ⇒ Segment

Creates and starts a general-purpose segment used to time arbitrary code.

Parameters:

  • name (String)

    full name of the segment; the agent will not add a prefix. Third-party users should begin the name with Custom/; e.g., Custom/UserMailer/send_welcome_email

  • unscoped_metrics (optional, String, Array) (defaults to: nil)

    additional unscoped metrics to record using this segment’s timing information

  • start_time (optional, Time) (defaults to: nil)

    a Time instance denoting the start time of the segment. Value is set by AbstractSegment#start if not given.

  • parent (optional, Segment) (defaults to: nil)

    Use for the rare cases (such as async) where the parent segment should be something other than the current segment

Returns:

  • (Segment)

    the newly created segment; you must call finish on it at the end of the code you’re tracing



237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/new_relic/agent/tracer.rb', line 237

def start_segment(name:,
  unscoped_metrics: nil,
  start_time: nil,
  parent: nil)

  segment = Transaction::Segment.new(name, unscoped_metrics, start_time)
  start_and_add_segment(segment, parent)
rescue ArgumentError
  raise
rescue => exception
  log_error('start_segment', exception)
end

.start_transaction(category:, name: nil, partial_name: nil, **options) ⇒ Object

Takes name or partial_name and a category. Returns a transaction instance or nil



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/new_relic/agent/tracer.rb', line 163

def start_transaction(category:,
  name: nil,
  partial_name: nil,
  **options)

  raise ArgumentError, 'missing required argument: name or partial_name' if name.nil? && partial_name.nil?

  return current_transaction if current_transaction

  if name
    options[:transaction_name] = name
  else
    options[:transaction_name] = Transaction.name_from_partial(
      partial_name,
      category
    )
  end

  Transaction.start_new_transaction(state,
    category,
    options)
rescue ArgumentError
  raise
rescue => exception
  log_error('start_transaction', exception)
end

.start_transaction_or_segment(name: nil, partial_name: nil, category:, options: {}) ⇒ Object, #finish

Starts a segment on the current transaction (if one exists) or starts a new transaction otherwise.

Parameters:

  • name (String) (defaults to: nil)

    reserved for New Relic internal use

  • partial_name (String) (defaults to: nil)

    a meaningful name for this transaction (e.g., blogs/index); the Ruby agent will add a New-Relic-specific prefix

  • category (Symbol)

    :web for web transactions or :task for background transactions

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

    reserved for New Relic internal use

Returns:

  • (Object, #finish)

    an object that responds to finish; you must call finish on it at the end of the code you’re tracing



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/new_relic/agent/tracer.rb', line 134

def start_transaction_or_segment(name: nil,
  partial_name: nil,
  category:,
  options: {})

  raise ArgumentError, 'missing required argument: name or partial_name' if name.nil? && partial_name.nil?

  if name
    options[:transaction_name] = name
  else
    options[:transaction_name] = Transaction.name_from_partial(
      partial_name,
      category
    )
  end

  if (txn = current_transaction)
    txn.create_nested_segment(category, options)
  else
    Transaction.start_new_transaction(state, category, options)
  end
rescue ArgumentError
  raise
rescue => exception
  log_error('start_transaction_or_segment', exception)
end

.stateObject Also known as: tl_get



21
22
23
# File 'lib/new_relic/agent/tracer.rb', line 21

def state
  state_for(Thread.current)
end

.state_for(thread) ⇒ Object Also known as: tl_state_for

This method should only be used by Tracer for access to the current thread’s state or to provide read-only accessors for other threads

If ever exposed, this requires additional synchronization



394
395
396
397
398
399
400
401
402
403
# File 'lib/new_relic/agent/tracer.rb', line 394

def state_for(thread)
  state = ThreadLocalStorage.get(thread, :newrelic_tracer_state)

  if state.nil?
    state = Tracer::State.new
    ThreadLocalStorage.set(thread, :newrelic_tracer_state, state)
  end

  state
end

.thread_block_with_current_transaction(segment_name: nil, parent: nil, &block) ⇒ Object



421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
# File 'lib/new_relic/agent/tracer.rb', line 421

def thread_block_with_current_transaction(segment_name: nil, parent: nil, &block)
  parent ||= current_segment
  current_txn = ThreadLocalStorage[:newrelic_tracer_state]&.current_transaction if ThreadLocalStorage[:newrelic_tracer_state]&.is_execution_traced?
  proc do |*args|
    begin
      if current_txn && !current_txn.finished?
        NewRelic::Agent::Tracer.state.current_transaction = current_txn
        ThreadLocalStorage[:newrelic_thread_span_parent] = parent
        current_txn.async = true
        segment_name = "#{segment_name}/Thread#{::Thread.current.object_id}/Fiber#{::Fiber.current.object_id}" if NewRelic::Agent.config[:'thread_ids_enabled']
        segment = NewRelic::Agent::Tracer.start_segment(name: segment_name, parent: parent) if segment_name
      end
      NewRelic::Agent::Tracer.capture_segment_error(segment) do
        yield(*args)
      end
    ensure
      ::NewRelic::Agent::Transaction::Segment.finish(segment)
    end
  end
end

.thread_tracing_enabled?Boolean

Returns:

  • (Boolean)


417
418
419
# File 'lib/new_relic/agent/tracer.rb', line 417

def thread_tracing_enabled?
  NewRelic::Agent.config[:'instrumentation.thread.tracing']
end

.tracing_enabled?Boolean

Returns true unless called from within an NewRelic::Agent.disable_all_tracing block.

Returns:

  • (Boolean)


31
32
33
# File 'lib/new_relic/agent/tracer.rb', line 31

def tracing_enabled?
  state.tracing_enabled?
end

.transaction_sampled?Boolean Also known as: sampled?

Returns a boolean indicating whether the current_transaction is sampled, or nil if there is no current transaction.

Returns:

  • (Boolean)


68
69
70
71
72
73
# File 'lib/new_relic/agent/tracer.rb', line 68

def transaction_sampled?
  txn = current_transaction
  return false unless txn

  txn.sampled?
end