module Bugsnag::Helpers

Constants

MAX_ARRAY_LENGTH
MAX_PAYLOAD_LENGTH
MAX_STRING_LENGTH
MAX_TRIM_STACK_FRAMES
RAW_DATA_TYPES
TRUNCATION_INFO

Public Class Methods

deep_merge(l_hash, r_hash) click to toggle source

Merges r_hash into l_hash recursively, favouring the values in r_hash.

Returns a new array consisting of the merged values

# File lib/bugsnag/helpers.rb, line 41
def self.deep_merge(l_hash, r_hash)
  l_hash.merge(r_hash) do |key, l_val, r_val|
    if l_val.is_a?(Hash) && r_val.is_a?(Hash)
      deep_merge(l_val, r_val)
    elsif l_val.is_a?(Array) && r_val.is_a?(Array)
      l_val.concat(r_val)
    else
      r_val
    end
  end
end
deep_merge!(l_hash, r_hash) click to toggle source

Merges r_hash into l_hash recursively, favouring the values in r_hash.

Overwrites the values in the existing l_hash

# File lib/bugsnag/helpers.rb, line 57
def self.deep_merge!(l_hash, r_hash)
  l_hash.merge!(r_hash) do |key, l_val, r_val|
    if l_val.is_a?(Hash) && r_val.is_a?(Hash)
      deep_merge(l_val, r_val)
    elsif l_val.is_a?(Array) && r_val.is_a?(Array)
      l_val.concat(r_val)
    else
      r_val
    end
  end
end
extract_exception(payload, &block) click to toggle source

Wrapper for trimming stacktraces

# File lib/bugsnag/helpers.rb, line 105
def self.extract_exception(payload, &block)
  valid_payload = payload.is_a?(Hash) && payload[:events].respond_to?(:map)
  return unless valid_payload && block_given?
  payload[:events].each do |event|
    event[:exceptions].each(&block)
  end
end
get_payload_length(value) click to toggle source
# File lib/bugsnag/helpers.rb, line 161
def self.get_payload_length(value)
  if value.is_a?(String)
    value.length
  else
    ::JSON.dump(value).length
  end
end
is_json_raw_type?(value) click to toggle source

Check if a value is a raw type which should not be trimmed, truncated or converted to a string

# File lib/bugsnag/helpers.rb, line 127
def self.is_json_raw_type?(value)
  RAW_DATA_TYPES.detect {|klass| value.is_a?(klass)} != nil
end
payload_too_long?(value) click to toggle source

Validate that the serialized JSON string value is below maximum payload length

# File lib/bugsnag/helpers.rb, line 157
def self.payload_too_long?(value)
  get_payload_length(value) >= MAX_PAYLOAD_LENGTH
end
remove_metadata_from_events(object) click to toggle source

Remove ‘metaData` from array of `events` within object

# File lib/bugsnag/helpers.rb, line 207
def self.remove_metadata_from_events(object)
  return {} unless object.is_a?(Hash) and object[:events].respond_to?(:map)
  object[:events].map do |event|
    event.delete(:metaData) if object.is_a?(Hash)
  end
  object
end
trim_as_string(text) click to toggle source

If possible, convert the provided object to a string and trim to the maximum allowed string length

# File lib/bugsnag/helpers.rb, line 180
def self.trim_as_string(text)
  return "" unless text.respond_to? :to_s
  text = text.to_s
  if text.length > MAX_STRING_LENGTH
    length = MAX_STRING_LENGTH - TRUNCATION_INFO.length
    text = text.slice(0, length) + TRUNCATION_INFO
  end
  text
end
trim_if_needed(value) click to toggle source

Trim the size of value if the serialized JSON value is longer than is accepted by Bugsnag

# File lib/bugsnag/helpers.rb, line 12
def self.trim_if_needed(value)
  value = "" if value.nil?

  return value unless payload_too_long?(value)

  # Truncate exception messages
  reduced_value = truncate_exception_messages(value)
  return reduced_value unless payload_too_long?(reduced_value)

  # Trim metadata
  reduced_value = trim_metadata(reduced_value)
  return reduced_value unless payload_too_long?(reduced_value)

  # Trim code from stacktrace
  reduced_value = trim_stacktrace_code(reduced_value)
  return reduced_value unless payload_too_long?(reduced_value)

  # Remove metadata
  reduced_value = remove_metadata_from_events(reduced_value)
  return reduced_value unless payload_too_long?(reduced_value)

  # Remove oldest functions in stacktrace
  trim_stacktrace_functions(reduced_value)
end
trim_metadata(payload) click to toggle source

Take the metadata from the events and trim it down

# File lib/bugsnag/helpers.rb, line 115
def self.trim_metadata(payload)
  return payload unless payload.is_a?(Hash) and payload[:events].respond_to?(:map)
  payload[:events].map do |event|
    event[:metaData] = truncate_arrays_in_value(event[:metaData])
    event[:metaData] = trim_strings_in_value(event[:metaData])
  end
  payload
end
trim_stacktrace_code(payload) click to toggle source

Remove all code from stacktraces

# File lib/bugsnag/helpers.rb, line 84
def self.trim_stacktrace_code(payload)
  extract_exception(payload) do |exception|
    exception[:stacktrace].each do |frame|
      frame.delete(:code)
    end
  end
  payload
end
trim_stacktrace_functions(payload) click to toggle source

Truncate stacktraces

# File lib/bugsnag/helpers.rb, line 95
def self.trim_stacktrace_functions(payload)
  extract_exception(payload) do |exception|
    stack = exception[:stacktrace]
    exception[:stacktrace] = stack.take(MAX_TRIM_STACK_FRAMES)
  end
  payload
end
trim_strings_in_array(collection) click to toggle source
# File lib/bugsnag/helpers.rb, line 190
def self.trim_strings_in_array(collection)
  return [] unless collection.respond_to?(:map)
  collection.map {|value| trim_strings_in_value(value)}
end
trim_strings_in_hash(hash) click to toggle source
# File lib/bugsnag/helpers.rb, line 169
def self.trim_strings_in_hash(hash)
  return {} unless hash.is_a?(Hash)
  hash.each_with_object({}) do |(key, value), reduced_hash|
    if reduced_value = trim_strings_in_value(value)
      reduced_hash[key] = reduced_value
    end
  end
end
trim_strings_in_value(value) click to toggle source

Trim all strings to be less than the maximum allowed string length

# File lib/bugsnag/helpers.rb, line 142
def self.trim_strings_in_value(value)
  return value if is_json_raw_type?(value)
  case value
  when Hash
    trim_strings_in_hash(value)
  when Array, Set
    trim_strings_in_array(value)
  else
    trim_as_string(value)
  end
end
truncate_array(array) click to toggle source

Shorten array until it fits within the payload size limit when serialized

# File lib/bugsnag/helpers.rb, line 133
def self.truncate_array(array)
  return [] unless array.respond_to?(:slice)
  array.slice(0, MAX_ARRAY_LENGTH).map do |item|
    truncate_arrays_in_value(item)
  end
end
truncate_arrays_in_hash(hash) click to toggle source
# File lib/bugsnag/helpers.rb, line 215
def self.truncate_arrays_in_hash(hash)
  return {} unless hash.is_a?(Hash)
  hash.each_with_object({}) do |(key, value), reduced_hash|
    if reduced_value = truncate_arrays_in_value(value)
      reduced_hash[key] = reduced_value
    end
  end
end
truncate_arrays_in_value(value) click to toggle source
# File lib/bugsnag/helpers.rb, line 195
def self.truncate_arrays_in_value(value)
  case value
  when Hash
    truncate_arrays_in_hash(value)
  when Array, Set
    truncate_array(value)
  else
    value
  end
end
truncate_exception_messages(payload) click to toggle source

Truncate exception messages

# File lib/bugsnag/helpers.rb, line 75
def self.truncate_exception_messages(payload)
  extract_exception(payload) do |exception|
    exception[:message] = trim_as_string(exception[:message])
  end
  payload
end