class NewRelic::Agent::Transaction::ExternalRequestSegment

This class represents an external segment in a transaction trace.

@api public

Constants

APP_DATA_KEY
EXTERNAL_ALL
EXTERNAL_ALL_OTHER
EXTERNAL_ALL_WEB
MISSING_STATUS_CODE
NR_SYNTHETICS_HEADER
NR_SYNTHETICS_INFO_HEADER

Attributes

http_status_code[R]
library[R]
procedure[R]
uri[R]

Public Instance Methods

add_request_headers(request) click to toggle source

This method adds New Relic request headers to a given request made to an external API and checks to see if a host header is used for the request. If a host header is used, it updates the segment name to match the host header.

@param [NewRelic::Agent::HTTPClients::AbstractRequest] request the request object (must belong to a subclass of NewRelic::Agent::HTTPClients::AbstractRequest)

@api public

# File lib/new_relic/agent/transaction/external_request_segment.rb, line 54
def add_request_headers(request)
  process_host_header(request)
  synthetics_header = transaction&.raw_synthetics_header
  synthetics_info_header = transaction&.raw_synthetics_info_header
  insert_synthetics_header(request, synthetics_header, synthetics_info_header) if synthetics_header

  return unless record_metrics?

  transaction.distributed_tracer.insert_headers(request)
rescue => e
  NewRelic::Agent.logger.error('Error in add_request_headers', e)
end
get_request_metadata() click to toggle source

Obtain an obfuscated String suitable for delivery across public networks that identifies this application and transaction to another application which is also running a New Relic agent. This String can be processed by process_request_metadata on the receiving application.

@return [String] obfuscated request metadata to send

@api public

# File lib/new_relic/agent/transaction/external_request_segment.rb, line 117
def get_request_metadata
  NewRelic::Agent.record_api_supportability_metric(:get_request_metadata)
  return unless CrossAppTracing.cross_app_enabled?

  if transaction

    # build hash of CAT metadata
    #
    rmd = {
      NewRelicID: NewRelic::Agent.config[:cross_process_id],
      NewRelicTransaction: [
        transaction.guid,
        false,
        transaction.distributed_tracer.cat_trip_id,
        transaction.distributed_tracer.cat_path_hash
      ]
    }

    # flag cross app in the state so transaction knows to add bits to payload
    #
    transaction.distributed_tracer.is_cross_app_caller = true

    # add Synthetics header if we have it
    #
    rmd[:NewRelicSynthetics] = transaction.raw_synthetics_header if transaction.raw_synthetics_header

    # obfuscate the generated request metadata JSON
    #
    obfuscator.obfuscate(::JSON.dump(rmd))

  end
rescue => e
  NewRelic::Agent.logger.error('error during get_request_metadata', e)
end
process_response_metadata(response_metadata) click to toggle source

Process obfuscated String sent from a called application that is also running a New Relic agent and save information in current transaction for inclusion in a trace. This String is generated by get_response_metadata on the receiving application.

@param response_metadata [String] received obfuscated response metadata

@api public

# File lib/new_relic/agent/transaction/external_request_segment.rb, line 160
def process_response_metadata(response_metadata)
  NewRelic::Agent.record_api_supportability_metric(:process_response_metadata)
  if transaction
    app_data = ::JSON.parse(obfuscator.deobfuscate(response_metadata))[APP_DATA_KEY]

    # validate cross app id
    #
    if Array === app_data and CrossAppTracing.trusted_valid_cross_app_id?(app_data[0])
      @app_data = app_data
      update_segment_name
    else
      NewRelic::Agent.logger.error('error processing response metadata: invalid/non-trusted ID')
    end
  end

  nil
rescue => e
  NewRelic::Agent.logger.error('error during process_response_metadata', e)
end
read_response_headers(response) click to toggle source

This method extracts app data from an external response if present. If a valid cross-app ID is found, the name of the segment is updated to reflect the cross-process ID and transaction name.

@param [Hash] response a hash of response headers

@api public

# File lib/new_relic/agent/transaction/external_request_segment.rb, line 74
def read_response_headers(response)
  return unless record_metrics? && CrossAppTracing.cross_app_enabled?
  return unless CrossAppTracing.response_has_crossapp_header?(response)

  unless data = CrossAppTracing.extract_appdata(response)
    NewRelic::Agent.logger.debug("Couldn't extract_appdata from external segment response")
    return
  end

  if CrossAppTracing.valid_cross_app_id?(data[0])
    @app_data = data
    update_segment_name
  else
    NewRelic::Agent.logger.debug('External segment response has invalid cross_app_id')
  end
rescue => e
  NewRelic::Agent.logger.error('Error in read_response_headers', e)
end
record_metrics() click to toggle source
Calls superclass method
# File lib/new_relic/agent/transaction/external_request_segment.rb, line 180
def record_metrics
  add_unscoped_metrics
  super
end

Private Instance Methods

add_unscoped_metrics() click to toggle source
# File lib/new_relic/agent/transaction/external_request_segment.rb, line 222
def add_unscoped_metrics
  @unscoped_metrics = [EXTERNAL_ALL,
    "External/#{host}/all",
    suffixed_rollup_metric]

  if cross_app_request?
    @unscoped_metrics << "ExternalApp/#{host}/#{cross_process_id}/all"
  end
end
insert_synthetics_header(request, header, info) click to toggle source
# File lib/new_relic/agent/transaction/external_request_segment.rb, line 202
def insert_synthetics_header(request, header, info)
  request[NR_SYNTHETICS_HEADER] = header
  request[NR_SYNTHETICS_INFO_HEADER] = info if info
end
obfuscator() click to toggle source
# File lib/new_relic/agent/transaction/external_request_segment.rb, line 248
def obfuscator
  CrossAppTracing.obfuscator
end
process_host_header(request) click to toggle source
# File lib/new_relic/agent/transaction/external_request_segment.rb, line 216
def process_host_header(request)
  if @host_header = request.host_from_header
    update_segment_name
  end
end
record_span_event() click to toggle source
# File lib/new_relic/agent/transaction/external_request_segment.rb, line 252
def record_span_event
  # don't record a span event if the transaction is ignored
  return if transaction.ignore?

  aggregator = ::NewRelic::Agent.agent.span_event_aggregator
  priority = transaction.priority
  aggregator.record(priority: priority) do
    SpanEventPrimitive.for_external_request_segment(self)
  end
end
segment_complete() click to toggle source
Calls superclass method
# File lib/new_relic/agent/transaction/external_request_segment.rb, line 207
def segment_complete
  params[:uri] = uri.to_s
  if cross_app_request?
    params[:transaction_guid] = transaction_guid
  end

  super
end
set_http_status_code(response) click to toggle source

Only sets the http_status_code if response.status_code is non-empty value

# File lib/new_relic/agent/transaction/external_request_segment.rb, line 193
def set_http_status_code(response)
  if response.respond_to?(:status_code)
    @http_status_code = response.status_code if response.has_status_code?
  else
    NewRelic::Agent.logger.warn("Cannot extract HTTP Status Code from response #{response.class.to_s}")
    NewRelic::Agent.record_metric("#{name}/#{MISSING_STATUS_CODE}", 1)
  end
end
suffixed_rollup_metric() click to toggle source
# File lib/new_relic/agent/transaction/external_request_segment.rb, line 232
def suffixed_rollup_metric
  if Transaction.recording_web_transaction?
    EXTERNAL_ALL_WEB
  else
    EXTERNAL_ALL_OTHER
  end
end
update_segment_name() click to toggle source
# File lib/new_relic/agent/transaction/external_request_segment.rb, line 240
def update_segment_name
  if @app_data
    @name = "ExternalTransaction/#{host}/#{cross_process_id}/#{cross_process_transaction_name}"
  else
    @name = "External/#{host}/#{library}/#{procedure}"
  end
end