class NewRelic::NoticedError

This class encapsulates an error that was noticed by New Relic in a managed app.

Constants

AGENT_ATTRIBUTES
AGENT_ATTRIBUTE_ERROR_GROUP
DESTINATION
ERROR_CLASS_KEY
ERROR_EXPECTED_KEY
ERROR_MESSAGE_KEY
ERROR_PREFIX_KEY
INTRINSIC_ATTRIBUTES
NIL_ERROR_MESSAGE
STRIPPED_EXCEPTION_REPLACEMENT_MESSAGE
UNKNOWN_ERROR_CLASS_NAME
USER_ATTRIBUTES

Attributes

attributes[RW]
attributes_from_notice_error[RW]
error_group[R]
exception_class_name[RW]
exception_id[R]
expected[RW]
file_name[RW]
is_internal[R]
line_number[RW]
message[RW]
path[RW]
request_port[RW]
request_uri[RW]
stack_trace[RW]
timestamp[RW]
transaction_id[RW]

Public Class Methods

new(path, exception, timestamp = Process.clock_gettime(Process::CLOCK_REALTIME), expected = false) click to toggle source
# File lib/new_relic/noticed_error.rb, line 37
def initialize(path, exception, timestamp = Process.clock_gettime(Process::CLOCK_REALTIME), expected = false)
  @exception_id = exception.object_id
  @path = path

  # It's critical that we not hold onto the exception class constant in this
  # class. These objects get serialized for Resque to a process that might
  # not have the original exception class loaded, so do all processing now
  # while we have the actual exception!
  @is_internal = (exception.class < NewRelic::Agent::InternalAgentError)

  extract_class_name_and_message_from(exception)
  @transaction_id = NewRelic::Agent::Tracer&.current_transaction&.guid

  # clamp long messages to 4k so that we don't send a lot of
  # overhead across the wire
  @message = @message[0..4095] if @message.length > 4096

  # replace error message if enabled
  if NewRelic::Agent.config[:'strip_exception_messages.enabled'] &&
      !self.class.passes_message_allowlist(exception.class)
    @message = STRIPPED_EXCEPTION_REPLACEMENT_MESSAGE
  end

  @attributes_from_notice_error = nil
  @attributes = nil
  @timestamp = timestamp
  @expected = expected
end
passes_message_allowlist(exception_class) click to toggle source
# File lib/new_relic/noticed_error.rb, line 74
def self.passes_message_allowlist(exception_class)
  NewRelic::Agent.config[:'strip_exception_messages.allowed_classes'].any? do |klass|
    exception_class <= klass
  end
end

Public Instance Methods

==(other) click to toggle source
# File lib/new_relic/noticed_error.rb, line 66
def ==(other)
  if other.respond_to?(:exception_id)
    exception_id == other.exception_id
  else
    false
  end
end
agent_attributes() click to toggle source
# File lib/new_relic/noticed_error.rb, line 166
def agent_attributes
  processed_attributes[AGENT_ATTRIBUTES]
end
append_attributes(outgoing_params, outgoing_key, source_attributes) click to toggle source
# File lib/new_relic/noticed_error.rb, line 162
def append_attributes(outgoing_params, outgoing_key, source_attributes)
  outgoing_params[outgoing_key] = source_attributes || {}
end
base_parameters() click to toggle source
# File lib/new_relic/noticed_error.rb, line 106
def base_parameters
  params = {}
  params[:file_name] = file_name if file_name
  params[:line_number] = line_number if line_number
  params[:stack_trace] = stack_trace if stack_trace
  params[ERROR_EXPECTED_KEY.to_sym] = expected
  params
end
build_agent_attributes(merged_attributes) click to toggle source
# File lib/new_relic/noticed_error.rb, line 148
def build_agent_attributes(merged_attributes)
  return NewRelic::EMPTY_HASH unless @attributes

  @attributes.agent_attributes_for(DESTINATION)
end
build_error_attributes() click to toggle source
# File lib/new_relic/noticed_error.rb, line 140
def build_error_attributes
  @attributes_from_notice_error ||= {}
  @attributes_from_notice_error[ERROR_MESSAGE_KEY] = string(message)
  @attributes_from_notice_error[ERROR_CLASS_KEY] = string(exception_class_name)

  @attributes_from_notice_error[ERROR_EXPECTED_KEY] = true if expected
end
build_intrinsic_attributes() click to toggle source
# File lib/new_relic/noticed_error.rb, line 154
def build_intrinsic_attributes
  if @attributes
    @attributes.intrinsic_attributes_for(DESTINATION)
  else
    NewRelic::EMPTY_HASH
  end
end
custom_attributes() click to toggle source
# File lib/new_relic/noticed_error.rb, line 170
def custom_attributes
  processed_attributes[USER_ATTRIBUTES]
end
error_group=(name) click to toggle source
# File lib/new_relic/noticed_error.rb, line 194
def error_group=(name)
  return if name.nil? || name.empty?

  if agent_attributes.frozen?
    processed_attributes[AGENT_ATTRIBUTES] = agent_attributes.merge(AGENT_ATTRIBUTE_ERROR_GROUP => name)
  else
    agent_attributes[AGENT_ATTRIBUTE_ERROR_GROUP] = name
  end

  @error_group = name
end
extract_class_name_and_message_from(exception) click to toggle source
# File lib/new_relic/noticed_error.rb, line 178
def extract_class_name_and_message_from(exception)
  if exception.nil?
    @exception_class_name = UNKNOWN_ERROR_CLASS_NAME
    @message = NIL_ERROR_MESSAGE
  elsif exception.is_a?(NewRelic::Agent::NoticeableError)
    @exception_class_name = exception.class_name
    @message = exception.message
  else
    if defined?(Rails::VERSION::MAJOR) && Rails::VERSION::MAJOR < 5 && exception.respond_to?(:original_exception)
      exception = exception.original_exception || exception
    end
    @exception_class_name = exception.is_a?(Exception) ? exception.class.name : UNKNOWN_ERROR_CLASS_NAME
    @message = exception.to_s
  end
end
intrinsic_attributes() click to toggle source
# File lib/new_relic/noticed_error.rb, line 174
def intrinsic_attributes
  processed_attributes[INTRINSIC_ATTRIBUTES]
end
merge_custom_attributes_from_notice_error(merged_attributes) click to toggle source
# File lib/new_relic/noticed_error.rb, line 133
def merge_custom_attributes_from_notice_error(merged_attributes)
  if @attributes_from_notice_error
    from_notice_error = NewRelic::NoticedError.normalize_params(@attributes_from_notice_error)
    merged_attributes.merge_custom_attributes(from_notice_error)
  end
end
merge_custom_attributes_from_transaction(merged_attributes) click to toggle source
# File lib/new_relic/noticed_error.rb, line 126
def merge_custom_attributes_from_transaction(merged_attributes)
  if @attributes
    from_transaction = @attributes.custom_attributes_for(DESTINATION)
    merged_attributes.merge_custom_attributes(from_transaction)
  end
end
merged_custom_attributes(merged_attributes) click to toggle source

We can get custom attributes from two sources–the transaction, which we hold in @attributes, or passed options to notice_error which show up in @attributes_from_notice_error. Both need filtering, so merge them together in our Attributes class for consistent handling

# File lib/new_relic/noticed_error.rb, line 119
def merged_custom_attributes(merged_attributes)
  merge_custom_attributes_from_transaction(merged_attributes)
  merge_custom_attributes_from_notice_error(merged_attributes)

  merged_attributes.custom_attributes_for(DESTINATION)
end
processed_attributes() click to toggle source

Note that we process attributes lazily and store the result. This is because there is a possibility that a noticed error will be discarded and not sent back as a traced error or TransactionError.

# File lib/new_relic/noticed_error.rb, line 95
def processed_attributes
  @processed_attributes ||= begin
    attributes = base_parameters
    merged_attributes = NewRelic::Agent::Attributes.new(NewRelic::Agent.instance.attribute_filter)
    append_attributes(attributes, USER_ATTRIBUTES, merged_custom_attributes(merged_attributes))
    append_attributes(attributes, AGENT_ATTRIBUTES, build_agent_attributes(merged_attributes))
    append_attributes(attributes, INTRINSIC_ATTRIBUTES, build_intrinsic_attributes)
    attributes
  end
end
to_collector_array(encoder = nil) click to toggle source
# File lib/new_relic/noticed_error.rb, line 82
def to_collector_array(encoder = nil)
  arr = [NewRelic::Helper.time_to_millis(timestamp),
    string(path),
    string(message),
    string(exception_class_name),
    processed_attributes]
  arr << @transaction_id if @transaction_id
  arr
end