module NewRelic::Agent::CrossAppTracing

Constants

NR_APPDATA_HEADER

The cross app response header for “outgoing” calls

NR_ID_HEADER

The cross app id header for “outgoing” calls

NR_MESSAGE_BROKER_ID_HEADER
NR_MESSAGE_BROKER_SYNTHETICS_HEADER
NR_MESSAGE_BROKER_TXN_HEADER
NR_TXN_HEADER

The cross app transaction header for “outgoing” calls

Attributes

cat_path_hashes[RW]
cross_app_payload[RW]
is_cross_app_caller[RW]

Public Instance Methods

add_message_cat_headers(headers) click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 64
def add_message_cat_headers(headers)
  return unless CrossAppTracing.cross_app_enabled?

  @is_cross_app_caller = true
  insert_message_headers(headers,
    transaction.guid,
    cat_trip_id,
    cat_path_hash,
    transaction.raw_synthetics_header)
end
assign_cross_app_intrinsics() click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 85
def assign_cross_app_intrinsics
  transaction.attributes.add_intrinsic_attribute(:trip_id, cat_trip_id)
  transaction.attributes.add_intrinsic_attribute(:path_hash, cat_path_hash)
end
assign_intrinsic_transaction_attributes(state) click to toggle source

From inbound request headers

# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 237
def assign_intrinsic_transaction_attributes(state)
  # We expect to get the before call to set the id (if we have it) before
  # this, and then write our custom parameter when the transaction starts
  return unless (txn = state.current_transaction)
  return unless (payload = txn.distributed_tracer.cross_app_payload)

  if (cross_app_id = payload.id)
    txn.attributes.add_intrinsic_attribute(:client_cross_process_id, cross_app_id)
  end

  if (referring_guid = payload.referring_guid)
    txn.attributes.add_intrinsic_attribute(:referring_transaction_guid, referring_guid)
  end
end
cat_path_hash() click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 45
def cat_path_hash
  referring_path_hash = cat_referring_path_hash || '0'
  seed = referring_path_hash.to_i(16)
  result = cross_app_monitor.path_hash(transaction.best_name, seed)
  record_cat_path_hash(result)
  result
end
cat_trip_id() click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 37
def cat_trip_id
  cross_app_payload&.referring_trip_id || transaction.guid
end
cross_app_enabled?() click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 142
def cross_app_enabled?
  valid_cross_process_id? &&
    valid_encoding_key? &&
    cross_application_tracer_enabled?
end
cross_app_monitor() click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 41
def cross_app_monitor
  NewRelic::Agent.instance.monitors.cross_app_monitor
end
cross_application_tracer_enabled?() click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 166
def cross_application_tracer_enabled?
  !NewRelic::Agent.config[:"distributed_tracing.enabled"] &&
    NewRelic::Agent.config[:"cross_application_tracer.enabled"]
end
extract_appdata(response) click to toggle source

Extract x-process application data from the specified response and return it as an array of the form:

[
  <cross app ID>,
  <transaction name>,
  <queue time in seconds>,
  <response time in seconds>,
  <request content length in bytes>,
  <transaction GUID>
]
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 203
def extract_appdata(response)
  appdata = response[NR_APPDATA_HEADER]

  decoded_appdata = obfuscator.deobfuscate(appdata)
  decoded_appdata.set_encoding(::Encoding::UTF_8) if
    decoded_appdata.respond_to?(:set_encoding)

  ::JSON.load(decoded_appdata)
end
insert_cross_app_header(headers) click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 53
def insert_cross_app_header(headers)
  return unless CrossAppTracing.cross_app_enabled?

  @is_cross_app_caller = true
  txn_guid = transaction.guid
  trip_id = cat_trip_id
  path_hash = cat_path_hash

  insert_request_headers(headers, txn_guid, trip_id, path_hash)
end
insert_request_headers(request, txn_guid, trip_id, path_hash) click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 175
def insert_request_headers(request, txn_guid, trip_id, path_hash)
  cross_app_id = NewRelic::Agent.config[:cross_process_id]
  txn_data = ::JSON.dump([txn_guid, false, trip_id, path_hash])

  request[NR_ID_HEADER] = obfuscator.obfuscate(cross_app_id)
  request[NR_TXN_HEADER] = obfuscator.obfuscate(txn_data)
end
is_cross_app?() click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 33
def is_cross_app?
  is_cross_app_caller? || is_cross_app_callee?
end
is_cross_app_callee?() click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 29
def is_cross_app_callee?
  !cross_app_payload.nil?
end
is_cross_app_caller?() click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 25
def is_cross_app_caller?
  @is_cross_app_caller ||= false
end
message_has_crossapp_request_header?(headers) click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 217
def message_has_crossapp_request_header?(headers)
  !!headers[NR_MESSAGE_BROKER_ID_HEADER]
end
obfuscator() click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 171
def obfuscator
  @obfuscator ||= NewRelic::Agent::Obfuscator.new(Agent.config[:encoding_key])
end
record_cross_app_metrics() click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 75
def record_cross_app_metrics
  if (id = cross_app_payload&.id)
    app_time_in_seconds = [
      Process.clock_gettime(Process::CLOCK_REALTIME) - transaction.start_time,
      0.0
    ].max
    NewRelic::Agent.record_metric("ClientApplication/#{id}/all", app_time_in_seconds)
  end
end
reject_messaging_cat_headers(headers) click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 221
def reject_messaging_cat_headers(headers)
  headers.reject { |k, _| k == NR_MESSAGE_BROKER_ID_HEADER || k == NR_MESSAGE_BROKER_TXN_HEADER }
end
response_has_crossapp_header?(response) click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 183
def response_has_crossapp_header?(response)
  if !!response[NR_APPDATA_HEADER]
    true
  else
    NewRelic::Agent.logger.debug("No #{NR_APPDATA_HEADER} header")
    false
  end
end
trusted_valid_cross_app_id?(id) click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 232
def trusted_valid_cross_app_id?(id)
  valid_cross_app_id?(id) && trusts?(id)
end
trusts?(id) click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 225
def trusts?(id)
  split_id = id.match(/(\d+)#\d+/)
  return false if split_id.nil?

  NewRelic::Agent.config[:trusted_account_ids].include?(split_id.captures.first.to_i)
end
valid_cross_app_id?(xp_id) click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 213
def valid_cross_app_id?(xp_id)
  !!(xp_id =~ /\A\d+#\d+\z/)
end
valid_cross_process_id?() click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 148
def valid_cross_process_id?
  if Agent.config[:cross_process_id] && Agent.config[:cross_process_id].length > 0
    true
  else
    NewRelic::Agent.logger.debug('No cross_process_id configured')
    false
  end
end
valid_encoding_key?() click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 157
def valid_encoding_key?
  if Agent.config[:encoding_key] && Agent.config[:encoding_key].length > 0
    true
  else
    NewRelic::Agent.logger.debug('No encoding_key set')
    false
  end
end

Private Instance Methods

append_cat_info(payload) click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 109
def append_cat_info(payload)
  if (referring_guid = cross_app_payload&.referring_guid)
    payload[:referring_transaction_guid] = referring_guid
  end

  return unless transaction.include_guid?

  payload[:guid] = transaction.guid

  return unless is_cross_app?

  trip_id = cat_trip_id
  path_hash = cat_path_hash
  referring_path_hash = cat_referring_path_hash

  payload[:cat_trip_id] = trip_id if trip_id
  payload[:cat_referring_path_hash] = referring_path_hash if referring_path_hash

  if path_hash
    payload[:cat_path_hash] = path_hash

    alternate_path_hashes = cat_path_hashes - [path_hash]
    unless alternate_path_hashes.empty?
      payload[:cat_alternate_path_hashes] = alternate_path_hashes
    end
  end
end
cat_referring_path_hash() click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 105
def cat_referring_path_hash
  cross_app_payload&.referring_path_hash
end
insert_message_headers(headers, txn_guid, trip_id, path_hash, synthetics_header) click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 92
def insert_message_headers(headers, txn_guid, trip_id, path_hash, synthetics_header)
  headers[NR_MESSAGE_BROKER_ID_HEADER] = obfuscator.obfuscate(Agent.config[:cross_process_id])
  headers[NR_MESSAGE_BROKER_TXN_HEADER] = obfuscator.obfuscate(::JSON.dump([txn_guid, false, trip_id, path_hash]))
  headers[NR_MESSAGE_BROKER_SYNTHETICS_HEADER] = synthetics_header if synthetics_header
end
record_cat_path_hash(hash) click to toggle source
# File lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb, line 98
def record_cat_path_hash(hash)
  @cat_path_hashes ||= []
  if @cat_path_hashes.size < 10 && !@cat_path_hashes.include?(hash)
    @cat_path_hashes << hash
  end
end