class NewRelic::Agent::ServerlessHandler

Constants

AGENT_ATTRIBUTE_DESTINATIONS
ATTRIBUTE_ARN
ATTRIBUTE_COLD_START
ATTRIBUTE_REQUEST_ID
EXECUTION_ENVIRONMENT
FUNCTION_NAME
LAMBDA_ENVIRONMENT_VARIABLE
LAMBDA_MARKER
METHOD_BLOCKLIST
NAMED_PIPE
PAYLOAD_VERSION
SUPPORTABILITY_METRIC

Public Class Methods

env_var_set?() click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 26
def self.env_var_set?
  ENV.key?(LAMBDA_ENVIRONMENT_VARIABLE)
end
new() click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 30
def initialize
  @context = nil
  @payloads = {}
end

Public Instance Methods

error_data(errors) click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 83
def error_data(errors)
  store_payload(:error_data, [nil, errors.map(&:to_collector_array)])
end
invoke_lambda_function_with_new_relic(event:, context:, method_name:, namespace: nil) click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 35
def invoke_lambda_function_with_new_relic(event:, context:, method_name:, namespace: nil)
  NewRelic::Agent.increment_metric(SUPPORTABILITY_METRIC)

  @context = context

  NewRelic::Agent::Tracer.in_transaction(category: :other, name: function_name) do
    add_agent_attributes

    NewRelic::LanguageSupport.constantize(namespace).send(method_name, event: event, context: context)
  end
ensure
  harvest!
  write_output
  reset!
end
metric_data(stats_hash) click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 57
def metric_data(stats_hash)
  payload = [nil,
    stats_hash.started_at,
    (stats_hash.harvested_at || Process.clock_gettime(Process::CLOCK_REALTIME)),
    []]
  stats_hash.each do |metric_spec, stats|
    next if stats.is_reset?

    hash = {name: metric_spec.name}
    hash[:scope] = metric_spec.scope unless metric_spec.scope.empty?

    payload.last.push([hash, [
      stats.call_count,
      stats.total_call_time,
      stats.total_exclusive_time,
      stats.min_call_time,
      stats.max_call_time,
      stats.sum_of_squares
    ]])
  end

  return if payload.last.empty?

  store_payload(:metric_data, payload)
end
store_payload(method, payload) click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 51
def store_payload(method, payload)
  return if METHOD_BLOCKLIST.include?(method)

  @payloads[method] = payload
end

Private Instance Methods

add_agent_attribute(attribute, value) click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 154
def add_agent_attribute(attribute, value)
  NewRelic::Agent::Tracer.current_transaction.add_agent_attribute(attribute, value, AGENT_ATTRIBUTE_DESTINATIONS)
end
add_agent_attributes() click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 146
def add_agent_attributes
  return unless NewRelic::Agent::Tracer.current_transaction

  add_agent_attribute(ATTRIBUTE_COLD_START, true) if cold?
  add_agent_attribute(ATTRIBUTE_ARN, @context.invoked_function_arn)
  add_agent_attribute(ATTRIBUTE_REQUEST_ID, @context.aws_request_id)
end
cold?() click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 158
def cold?
  return @cold if defined?(@cold)

  @cold = false
  true
end
function_name() click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 108
def function_name
  ENV.fetch(LAMBDA_ENVIRONMENT_VARIABLE, FUNCTION_NAME)
end
harvest!() click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 89
def harvest!
  NewRelic::Agent.instance.harvest_and_send_analytic_event_data
  NewRelic::Agent.instance.harvest_and_send_custom_event_data
  NewRelic::Agent.instance.harvest_and_send_data_types
end
metadata() click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 95
def metadata
  m = {arn: @context.invoked_function_arn,
       protocol_version: NewRelic::Agent::NewRelicService::PROTOCOL_VERSION,
       function_version: @context.function_version,
       execution_environment: EXECUTION_ENVIRONMENT,
       agent_version: NewRelic::VERSION::STRING}
  if PAYLOAD_VERSION >= 2
    m[:metadata_version] = PAYLOAD_VERSION
    m[:agent_language] = NewRelic::LANGUAGE
  end
  m
end
payload_v1() click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 123
def payload_v1
  payload_hash = {'metadata' => metadata, 'data' => @payloads}
  json = NewRelic::Agent.agent.service.marshaller.dump(payload_hash)
  gzipped = NewRelic::Agent::NewRelicService::Encoders::Compressed::Gzip.encode(json)
  base64_encoded = NewRelic::Base64.strict_encode64(gzipped)
  array = [PAYLOAD_VERSION, LAMBDA_MARKER, base64_encoded]
  ::JSON.dump(array)
end
payload_v2() click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 132
def payload_v2
  json = NewRelic::Agent.agent.service.marshaller.dump(@payloads)
  gzipped = NewRelic::Agent::NewRelicService::Encoders::Compressed::Gzip.encode(json)
  base64_encoded = NewRelic::Base64.strict_encode64(gzipped)
  array = [PAYLOAD_VERSION, LAMBDA_MARKER, metadata, base64_encoded]
  ::JSON.dump(array)
end
reset!() click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 165
def reset!
  @context = nil
  @payloads.replace({})
end
use_named_pipe?() click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 140
def use_named_pipe?
  return @use_named_pipe if defined?(@use_named_pipe)

  @use_named_pipe = File.exist?(NAMED_PIPE) && File.writable?(NAMED_PIPE)
end
write_output() click to toggle source
# File lib/new_relic/agent/serverless_handler.rb, line 112
def write_output
  string = PAYLOAD_VERSION == 1 ? payload_v1 : payload_v2

  return puts string unless use_named_pipe?

  File.write(NAMED_PIPE, string)

  NewRelic::Agent.logger.debug "Wrote serverless payload to #{NAMED_PIPE}\n" \
    "BEGIN PAYLOAD>>>\n#{string}\n<<<END PAYLOAD"
end