class NewRelic::Agent::JavaScriptInstrumentor

Constants

AGENT_KEY
APPLICATIONID_KEY
APPLICATION_TIME_KEY
ATTS_AGENT_SUBKEY
ATTS_KEY
ATTS_USER_SUBKEY
BEACON_KEY
ERROR_BEACON_KEY
LICENSE_KEY_KEY
QUEUE_TIME_KEY
RUM_KEY_LENGTH
SSL_FOR_HTTP_KEY
TRANSACTION_NAME_KEY

Public Class Methods

new(event_listener) click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 15
def initialize(event_listener)
  event_listener.subscribe(:initial_configuration_complete, &method(:log_configuration))
end

Public Instance Methods

add_attributes(data, txn) click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 172
def add_attributes(data, txn)
  return unless txn

  atts = {}
  append_custom_attributes!(txn, atts)
  append_agent_attributes!(txn, atts)

  unless atts.empty?
    json = ::JSON.dump(atts)
    data[ATTS_KEY] = obfuscator.obfuscate(json)
  end
end
add_ssl_for_http(data) click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 165
def add_ssl_for_http(data)
  ssl_for_http = NewRelic::Agent.config[:'browser_monitoring.ssl_for_http']
  unless ssl_for_http.nil?
    data[SSL_FOR_HTTP_KEY] = ssl_for_http
  end
end
append_agent_attributes!(txn, atts) click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 197
def append_agent_attributes!(txn, atts)
  agent_attributes = txn.attributes.agent_attributes_for(NewRelic::Agent::AttributeFilter::DST_BROWSER_MONITORING)
  unless agent_attributes.empty?
    atts[ATTS_AGENT_SUBKEY] = agent_attributes
  end
end
append_custom_attributes!(txn, atts) click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 185
def append_custom_attributes!(txn, atts)
  custom_attributes = txn.attributes.custom_attributes_for(NewRelic::Agent::AttributeFilter::DST_BROWSER_MONITORING)
  if custom_attributes.empty?
    NewRelic::Agent.logger.debug("#{self.class}: No custom attributes found to append.")
    return
  end

  NewRelic::Agent.logger.debug("#{self.class}: Appending the following custom attribute keys for browser " \
    "monitoring: #{custom_attributes.keys}")
  atts[ATTS_USER_SUBKEY] = custom_attributes
end
browser_timing_config(state, nonce = nil) click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 105
def browser_timing_config(state, nonce = nil)
  txn = state.current_transaction
  return '' if txn.nil?

  txn.freeze_name_and_execute_if_not_ignored do
    data = data_for_js_agent(txn)
    json = ::JSON.dump(data)
    return html_safe_if_needed("\n<script type=\"text/javascript\"#{create_nonce(nonce)}>window.NREUM||(NREUM={});NREUM.info=#{json}</script>")
  end

  ''
end
browser_timing_header(nonce = nil) click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 85
def browser_timing_header(nonce = nil) # THREAD_LOCAL_ACCESS
  return '' unless js_enabled_and_ready? # fast exit

  state = NewRelic::Agent::Tracer.state

  return '' unless insert_js?(state) # slower exit

  bt_config = browser_timing_config(state, nonce)
  return '' if bt_config.empty?

  bt_config + browser_timing_loader(nonce)
rescue => e
  ::NewRelic::Agent.logger.debug('Failure during RUM browser_timing_header construction', e)
  ''
end
browser_timing_loader(nonce = nil) click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 101
def browser_timing_loader(nonce = nil)
  html_safe_if_needed("\n<script type=\"text/javascript\"#{create_nonce(nonce)}>#{Agent.config[:js_agent_loader]}</script>")
end
create_nonce(nonce = nil) click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 118
def create_nonce(nonce = nil)
  return '' unless nonce

  " nonce=\"#{nonce.to_s}\""
end
data_for_js_agent(transaction) click to toggle source

NOTE: Internal prototyping may override this, so leave name stable!

# File lib/new_relic/agent/javascript_instrumentor.rb, line 138
def data_for_js_agent(transaction)
  queue_time_in_seconds = [transaction.queue_time, 0.0].max
  start_time_in_seconds = [transaction.start_time, 0.0].max
  app_time_in_seconds = Process.clock_gettime(Process::CLOCK_REALTIME) - start_time_in_seconds

  queue_time_in_millis = (queue_time_in_seconds * 1000.0).round
  app_time_in_millis = (app_time_in_seconds * 1000.0).round

  transaction_name = transaction.best_name || ::NewRelic::Agent::UNKNOWN_METRIC

  data = {
    BEACON_KEY => NewRelic::Agent.config[:beacon],
    ERROR_BEACON_KEY => NewRelic::Agent.config[:error_beacon],
    LICENSE_KEY_KEY => NewRelic::Agent.config[:browser_key],
    APPLICATIONID_KEY => NewRelic::Agent.config[:application_id],
    TRANSACTION_NAME_KEY => obfuscator.obfuscate(transaction_name),
    QUEUE_TIME_KEY => queue_time_in_millis,
    APPLICATION_TIME_KEY => app_time_in_millis,
    AGENT_KEY => NewRelic::Agent.config[:js_agent_file]
  }

  add_ssl_for_http(data)
  add_attributes(data, transaction)

  data
end
enabled?() click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 29
def enabled?
  Agent.config[:'rum.enabled'] && !!Agent.config[:beacon]
end
html_safe_if_needed(string) click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 204
def html_safe_if_needed(string)
  string = string.html_safe if string.respond_to?(:html_safe)
  string
end
insert_js?(state) click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 62
def insert_js?(state)
  if !state.current_transaction
    ::NewRelic::Agent.logger.debug('Not in transaction. Skipping browser instrumentation.')
    false
  elsif !state.is_execution_traced?
    ::NewRelic::Agent.logger.debug('Execution is not traced. Skipping browser instrumentation.')
    false
  elsif state.current_transaction.ignore_enduser?
    ::NewRelic::Agent.logger.debug('Ignore end user for this transaction is set. Skipping browser instrumentation.')
    false
  else
    true
  end
rescue => e
  ::NewRelic::Agent.logger.debug('Failure during insert_js', e)
  false
end
js_enabled_and_ready?() click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 37
def js_enabled_and_ready?
  if !enabled?
    ::NewRelic::Agent.logger.log_once(:debug, :js_agent_disabled,
      'JS agent instrumentation is disabled.')
    false
  elsif missing_config?(:js_agent_loader)
    ::NewRelic::Agent.logger.log_once(:debug, :missing_js_agent_loader,
      'Missing :js_agent_loader. Skipping browser instrumentation.')
    false
  elsif missing_config?(:beacon)
    ::NewRelic::Agent.logger.log_once(:debug, :missing_beacon,
      'Beacon configuration not received (yet?). Skipping browser instrumentation.')
    false
  elsif missing_config?(:browser_key)
    ::NewRelic::Agent.logger.log_once(:debug, :missing_browser_key,
      'Browser key is not set. Skipping browser instrumentation.')
    false
  else
    true
  end
rescue => e
  ::NewRelic::Agent.logger.debug("Failure during 'js_enabled_and_ready?'", e)
  false
end
log_configuration() click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 19
def log_configuration
  NewRelic::Agent.logger.debug("JS agent loader requested: #{NewRelic::Agent.config[:'browser_monitoring.loader']}",
    "JS agent loader debug: #{NewRelic::Agent.config[:'browser_monitoring.debug']}",
    "JS agent loader version: #{NewRelic::Agent.config[:'browser_monitoring.loader_version']}")

  if !NewRelic::Agent.config[:'rum.enabled']
    NewRelic::Agent.logger.debug('Real User Monitoring is disabled for this agent. Edit your configuration to change this.')
  end
end
missing_config?(key) click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 80
def missing_config?(key)
  value = NewRelic::Agent.config[key]
  value.nil? || value.empty?
end
obfuscator() click to toggle source
# File lib/new_relic/agent/javascript_instrumentor.rb, line 33
def obfuscator
  @obfuscator ||= NewRelic::Agent::Obfuscator.new(NewRelic::Agent.config[:license_key], RUM_KEY_LENGTH)
end