class NewRelic::Agent::Tracer
This class helps you interact with the current transaction (if it exists), start new transactions/segments, etc.
@api public
Constants
- OTHER
- UNKNOWN
Public Class Methods
# 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
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
# 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
# File lib/new_relic/agent/tracer.rb, line 407 def clear_state ThreadLocalStorage[:newrelic_tracer_state] = nil end
# 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
Returns the currently active segment in the transaction in progress for this thread, or nil
if no segment or transaction exists.
@api public
# File lib/new_relic/agent/tracer.rb, line 207 def current_segment return unless txn = current_transaction txn.current_segment end
# File lib/new_relic/agent/tracer.rb, line 413 def current_segment_key ::Fiber.current.object_id end
Returns the id of the current span, or nil
if none exists.
@api public
# File lib/new_relic/agent/tracer.rb, line 57 def current_span_id if span = current_segment span.guid end end
Returns the trace_id
of the current_transaction
, or nil
if none exists.
@api public
# File lib/new_relic/agent/tracer.rb, line 47 def current_trace_id if txn = current_transaction txn.trace_id end end
Returns the transaction in progress for this thread, or nil
if none exists.
@api public
# File lib/new_relic/agent/tracer.rb, line 39 def current_transaction state.current_transaction end
Runs the given block of code in a transaction.
@param [String] name reserved for New Relic internal use
@param [String] partial_name a meaningful name for this
transaction (e.g., +blogs/index+); the Ruby agent will add a New-Relic-specific prefix
@param [Symbol] category :web
for web transactions or
+:background+ for background transactions
@param [Hash] options reserved for New Relic internal use
@api public
# 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
Creates and starts a datastore segment used to time datastore operations.
@param [String] product the datastore name for use in metric
naming, e.g. "FauxDB"
@param [String] operation the name of the operation
(e.g. "select"), often named after the method that's being instrumented.
@param [optional, String] collection the collection name for use in
statement-level metrics (i.e. table or model name)
@param [optional, String] host the host this database
instance is running on
@param [optional, String] port_path_or_id TCP port, file
path, UNIX domain socket, or other connection-related info
@param [optional, String] database_name the name of this
database
@param start_time [optional, Time] a Time
instance
denoting the start time of the segment. Value is set by AbstractSegment#start if not given.
@param parent [optional, Segment] Use for the rare cases
(such as async) where the parent segment should be something other than the current segment
@return [DatastoreSegment] the newly created segment; you
_must_ call +finish+ on it at the end of the code you're tracing
@api public
# 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
Creates and starts an external request segment using the given library, URI, and procedure. This is used to time external calls made over HTTP.
@param [String] library a string of the class name of the library used to
make the external call, for example, 'Net::HTTP'.
@param [String, URI] uri indicates the URI to which the
external request is being made. The URI should begin with the protocol, for example, 'https://github.com'.
@param [String] procedure the HTTP method being used for the external
request as a string, for example, 'GET'.
@param start_time [optional, Time] a Time
instance
denoting the start time of the segment. Value is set by AbstractSegment#start if not given.
@param parent [optional, Segment] Use for the rare cases
(such as async) where the parent segment should be something other than the current segment
@return [ExternalRequestSegment] the newly created segment;
you _must_ call +finish+ on it at the end of the code you're tracing
@api public
# 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
For New Relic internal use only.
# 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
Creates and starts a general-purpose segment used to time arbitrary code.
@param [String] name 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+
@param [optional, String, Array] unscoped_metrics additional
unscoped metrics to record using this segment's timing information
@param start_time [optional, Time] a Time
instance
denoting the start time of the segment. Value is set by AbstractSegment#start if not given.
@param parent [optional, Segment] Use for the rare cases
(such as async) where the parent segment should be something other than the current segment
@return [Segment] the newly created segment; you must call
+finish+ on it at the end of the code you're tracing
@api public
# 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
Takes name or partial_name and a category. Returns a transaction instance or nil
# 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
Starts a segment on the current transaction (if one exists) or starts a new transaction otherwise.
@param [String] name reserved for New Relic internal use
@param [String] partial_name a meaningful name for this
transaction (e.g., +blogs/index+); the Ruby agent will add a New-Relic-specific prefix
@param [Symbol] category :web
for web transactions or
+:task+ for background transactions
@param [Hash] options reserved for New Relic internal use
@return [Object, finish] an object that responds to
+finish+; you _must_ call +finish+ on it at the end of the code you're tracing
@api public
# 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
# File lib/new_relic/agent/tracer.rb, line 21 def state state_for(Thread.current) end
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
# 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
# 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
# File lib/new_relic/agent/tracer.rb, line 417 def thread_tracing_enabled? NewRelic::Agent.config[:'instrumentation.thread.tracing'] end
Returns true
unless called from within an NewRelic::Agent.disable_all_tracing
block.
@api public
# File lib/new_relic/agent/tracer.rb, line 31 def tracing_enabled? state.tracing_enabled? end
Returns a boolean indicating whether the current_transaction
is sampled, or nil
if there is no current transaction.
@api public
# File lib/new_relic/agent/tracer.rb, line 68 def transaction_sampled? txn = current_transaction return false unless txn txn.sampled? end
Private Class Methods
# File lib/new_relic/agent/tracer.rb, line 456 def log_error(method_name, exception) NewRelic::Agent.logger.error("Exception during Tracer.#{method_name}", exception) nil end
# File lib/new_relic/agent/tracer.rb, line 444 def start_and_add_segment(segment, parent = nil) tracer_state = state if (txn = tracer_state.current_transaction) && tracer_state.tracing_enabled? txn.add_segment(segment, parent) else segment.record_metrics = false end segment.start segment end