Skip to content

Commit

Permalink
Release v6.8.0 (#474)
Browse files Browse the repository at this point in the history
  • Loading branch information
kattrali authored Jul 11, 2018
2 parents 687fa51 + afd2a08 commit 2887674
Show file tree
Hide file tree
Showing 11 changed files with 265 additions and 128 deletions.
4 changes: 3 additions & 1 deletion .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,9 @@ Metrics/MethodLength:
# Offense count: 1
# Configuration parameters: CountComments.
Metrics/ModuleLength:
Max: 111
Max: 125
Exclude:
- 'lib/bugsnag/helpers.rb'

# Offense count: 11
Metrics/PerceivedComplexity:
Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
Changelog
=========

## 6.8.0 (11 Jul 2018)

This release includes general performance improvements to payload trimming and
filtering.

### Enhancements

* Capture unexpected app terminations automatically with `at_exit`
| [#397](https://github.com/bugsnag/bugsnag-ruby/pull/397)
| [Alex Moinet](https://github.com/Cawllec)

* (DelayedJob) Improve max attempts handling - If the max attempts method
returns nil it should fallback to `Delayed::Worker.max_attempts`
| [#471](https://github.com/bugsnag/bugsnag-ruby/pull/471)
| [Johnny Shields](https://github.com/johnnyshields)

* Increase payload size limit to 512kb (from 256kb)
| [#431](https://github.com/bugsnag/bugsnag-ruby/pull/431)
| [Alex Moinet](https://github.com/Cawllec)

### Fixes

## 6.7.3 (18 May 2018)

### Fixes
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6.7.3
6.8.0
26 changes: 26 additions & 0 deletions lib/bugsnag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ def configure(validate_api_key=true)
yield(configuration) if block_given?

check_key_valid if validate_api_key

register_at_exit
end

##
Expand Down Expand Up @@ -111,6 +113,30 @@ def notify(exception, auto_notify=false, &block)
end
end

##
# Registers an at_exit function to automatically catch errors on exit
def register_at_exit
return if at_exit_handler_installed?
@exit_handler_added = true
at_exit do
if $!
Bugsnag.notify($!, true) do |report|
report.severity = 'error'
report.severity_reason = {
:type => Bugsnag::Report::UNHANDLED_EXCEPTION
}
end
end
end
end

##
# Checks if an at_exit handler has been added
def at_exit_handler_installed?
@exit_handler_added ||= false
end

# Configuration getters
##
# Returns the client's Configuration object, or creates one if not yet created.
def configuration
Expand Down
76 changes: 69 additions & 7 deletions lib/bugsnag/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,35 @@
module Bugsnag
module Helpers
MAX_STRING_LENGTH = 3072
MAX_PAYLOAD_LENGTH = 256000
MAX_ARRAY_LENGTH = 40
MAX_PAYLOAD_LENGTH = 512000
MAX_ARRAY_LENGTH = 80
MAX_TRIM_STACK_FRAMES = 30
RAW_DATA_TYPES = [Numeric, TrueClass, FalseClass]

##
# Trim the size of value if the serialized JSON value is longer than is
# accepted by Bugsnag
def self.trim_if_needed(value)
value = "" if value.nil?

# Sanitize object
sanitized_value = Bugsnag::Cleaner.clean_object_encoding(value)
return sanitized_value unless payload_too_long?(sanitized_value)
reduced_value = trim_strings_in_value(sanitized_value)

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

# Trim code from stacktrace
reduced_value = trim_stacktrace_code(reduced_value)
return reduced_value unless payload_too_long?(reduced_value)
remove_metadata_from_events(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

##
Expand Down Expand Up @@ -60,13 +73,56 @@ def self.deep_merge!(l_hash, r_hash)

TRUNCATION_INFO = '[TRUNCATED]'

##
# Remove all code from stacktraces
def self.trim_stacktrace_code(payload)
extract_exception(payload) do |exception|
exception[:stacktrace].each do |frame|
frame.delete(:code)
end
end
payload
end

##
# Truncate stacktraces
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

##
# Wrapper for trimming stacktraces
def self.extract_exception(payload)
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 { |exception| yield exception }
end
end

##
# Take the metadata from the events and trim it down
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

##
# Check if a value is a raw type which should not be trimmed, truncated
# or converted to a string
def self.is_json_raw_type?(value)
RAW_DATA_TYPES.detect {|klass| value.is_a?(klass)} != nil
end

##
# Shorten array until it fits within the payload size limit when serialized
def self.truncate_array(array)
return [] unless array.respond_to?(:slice)
Expand All @@ -75,6 +131,7 @@ def self.truncate_array(array)
end
end

##
# Trim all strings to be less than the maximum allowed string length
def self.trim_strings_in_value(value)
return value if is_json_raw_type?(value)
Expand All @@ -88,13 +145,18 @@ def self.trim_strings_in_value(value)
end
end

##
# Validate that the serialized JSON string value is below maximum payload
# length
def self.payload_too_long?(value)
get_payload_length(value) >= MAX_PAYLOAD_LENGTH
end

def self.get_payload_length(value)
if value.is_a?(String)
value.length >= MAX_PAYLOAD_LENGTH
value.length
else
::JSON.dump(value).length >= MAX_PAYLOAD_LENGTH
::JSON.dump(value).length
end
end

Expand Down
17 changes: 0 additions & 17 deletions lib/bugsnag/integrations/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,6 @@ class Railtie < Rails::Railtie
load "bugsnag/tasks/bugsnag.rake"
end

# send notifications if a command fails in a 'rails runner' call
if self.respond_to? :runner
runner do
at_exit do
if $!
Bugsnag.notify($!, true) do |report|
report.severity = "error"
report.severity_reason = {
:type => Bugsnag::Report::UNHANDLED_EXCEPTION_MIDDLEWARE,
:attributes => FRAMEWORK_ATTRIBUTES
}
end
end
end
end
end

config.before_initialize do
# Configure bugsnag rails defaults
# Skipping API key validation as the key may be set later in an
Expand Down
1 change: 1 addition & 0 deletions lib/bugsnag/stacktrace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def initialize(backtrace, configuration)
@configuration = configuration

backtrace = caller if !backtrace || backtrace.empty?

@processed_backtrace = backtrace.map do |trace|
if trace.match(BACKTRACE_LINE_REGEX)
file, line_str, method = [$1, $2, $3]
Expand Down
56 changes: 56 additions & 0 deletions spec/bugsnag_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,62 @@
end
end

describe "add_exit_handler" do

before do
Bugsnag.instance_variable_set(:@exit_handler_added, false)
end

it "automatically adds an exit handler" do
expect(Bugsnag).to receive(:register_at_exit)
Bugsnag.configure do |conf|
conf.api_key = "TEST KEY"
end
end

it "calls at_exit when register_at_exit is called" do
expect(Bugsnag).to receive(:at_exit)
Bugsnag.register_at_exit
end

it "doesn't call at_exit on subsequent calls" do
expect(Bugsnag).to receive(:at_exit).once
Bugsnag.register_at_exit
Bugsnag.register_at_exit
end

context 'with aliased at_exit' do
before do
module Kernel
alias_method :old_at_exit, :at_exit
def at_exit
begin
raise BugsnagTestException.new("Oh no")
rescue
yield
end
end
end
end

it "sends an exception when at_exit is called" do
report_mock = double('report')
expect(report_mock).to receive(:severity=).with('error')
expect(report_mock).to receive(:severity_reason=).with({
:type => Bugsnag::Report::UNHANDLED_EXCEPTION
})
expect(Bugsnag).to receive(:notify).with(kind_of(BugsnagTestException), true).and_yield(report_mock)
Bugsnag.register_at_exit
end

after do
module Kernel
alias_method :at_exit, :old_at_exit
end
end
end
end

describe 'loading integrations' do
before do
module Kernel
Expand Down
1 change: 1 addition & 0 deletions spec/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,5 @@ def debug(name, &block)
it "should have exit exception classes ignored by default" do
expect(subject.ignore_classes).to eq(Set.new([SystemExit, Interrupt]))
end

end
Loading

0 comments on commit 2887674

Please sign in to comment.