bugsnag 6.14.0 → 6.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/VERSION +1 -1
- data/features/fixtures/docker-compose.yml +5 -1
- data/features/fixtures/plain/app/report_modification/initiators/handled_on_error.rb +10 -0
- data/features/fixtures/plain/app/report_modification/initiators/unhandled_on_error.rb +11 -0
- data/features/fixtures/plain/app/stack_frame_modification/initiators/handled_on_error.rb +29 -0
- data/features/fixtures/plain/app/stack_frame_modification/initiators/unhandled_on_error.rb +26 -0
- data/features/fixtures/rails3/app/config/initializers/bugsnag.rb +8 -0
- data/features/fixtures/rails4/app/config/initializers/bugsnag.rb +8 -0
- data/features/fixtures/rails5/app/config/initializers/bugsnag.rb +8 -0
- data/features/fixtures/rails6/app/config/initializers/bugsnag.rb +8 -0
- data/features/plain_features/add_tab.feature +7 -1
- data/features/plain_features/ignore_report.feature +2 -0
- data/features/plain_features/report_api_key.feature +3 -1
- data/features/plain_features/report_severity.feature +2 -0
- data/features/plain_features/report_stack_frames.feature +4 -0
- data/features/plain_features/report_user.feature +7 -1
- data/features/rails_features/on_error.feature +29 -0
- data/lib/bugsnag.rb +35 -0
- data/lib/bugsnag/code_extractor.rb +137 -0
- data/lib/bugsnag/configuration.rb +27 -0
- data/lib/bugsnag/middleware_stack.rb +38 -3
- data/lib/bugsnag/on_error_callbacks.rb +33 -0
- data/lib/bugsnag/report.rb +1 -1
- data/lib/bugsnag/session_tracker.rb +3 -3
- data/lib/bugsnag/stacktrace.rb +25 -68
- data/spec/code_extractor_spec.rb +129 -0
- data/spec/fixtures/crashes/file1.rb +29 -0
- data/spec/fixtures/crashes/file2.rb +25 -0
- data/spec/fixtures/crashes/file_with_long_lines.rb +7 -0
- data/spec/fixtures/crashes/functions.rb +29 -0
- data/spec/fixtures/crashes/short_file.rb +2 -0
- data/spec/on_error_spec.rb +332 -0
- data/spec/report_spec.rb +7 -4
- data/spec/spec_helper.rb +8 -0
- data/spec/stacktrace_spec.rb +276 -30
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a4f6c896c7046dba3d9db2adfa32e232232e3a7923cb9984321ee3a8b09b6e3
|
4
|
+
data.tar.gz: f6b27c0b50846c1a3e012bb80bccbe5ad6663257c8de4e300742e7af4b065b3f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d51d64bbf0b2cc094742a8a207702e963c33eac9e18f58f2348f3d9e6475a1bd0c43dc44baf139bb976823bfabd8b3ff5bab261ae3dababd46f083b7539602f4
|
7
|
+
data.tar.gz: 3621690efd266608313b000f7acff6df354380457e9b78e448290573e97dd0b17beb68afa5efff96fdcc7b21b39d2d292fddc38082eae7bece24dd1f2d457f0d
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,23 @@
|
|
1
1
|
Changelog
|
2
2
|
=========
|
3
3
|
|
4
|
+
## 6.15.0 (27 July 2020)
|
5
|
+
|
6
|
+
### Enhancements
|
7
|
+
|
8
|
+
* Add `on_error` callbacks to replace `before_notify_callbacks`
|
9
|
+
| [#608](https://github.com/bugsnag/bugsnag-ruby/pull/608)
|
10
|
+
|
11
|
+
* Improve performance when extracting code from files in stacktraces
|
12
|
+
| [#604](https://github.com/bugsnag/bugsnag-ruby/pull/604)
|
13
|
+
|
14
|
+
* Reduce memory use when session tracking is disabled
|
15
|
+
| [#606](https://github.com/bugsnag/bugsnag-ruby/pull/606)
|
16
|
+
|
17
|
+
### Deprecated
|
18
|
+
|
19
|
+
* `before_notify_callbacks` have been deprecated in favour of `on_error` and will be removed in the next major release
|
20
|
+
|
4
21
|
## 6.14.0 (20 July 2020)
|
5
22
|
|
6
23
|
### Enhancements
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
6.
|
1
|
+
6.15.0
|
@@ -120,6 +120,7 @@ services:
|
|
120
120
|
- BUGSNAG_TIMEOUT
|
121
121
|
- CALLBACK_INITIATOR
|
122
122
|
- SQL_ONLY_BREADCRUMBS
|
123
|
+
- ADD_ON_ERROR
|
123
124
|
- USE_DEFAULT_AUTO_CAPTURE_SESSIONS
|
124
125
|
restart: "no"
|
125
126
|
|
@@ -155,6 +156,7 @@ services:
|
|
155
156
|
- BUGSNAG_TIMEOUT
|
156
157
|
- CALLBACK_INITIATOR
|
157
158
|
- SQL_ONLY_BREADCRUMBS
|
159
|
+
- ADD_ON_ERROR
|
158
160
|
- USE_DEFAULT_AUTO_CAPTURE_SESSIONS
|
159
161
|
restart: "no"
|
160
162
|
|
@@ -190,6 +192,7 @@ services:
|
|
190
192
|
- BUGSNAG_TIMEOUT
|
191
193
|
- CALLBACK_INITIATOR
|
192
194
|
- SQL_ONLY_BREADCRUMBS
|
195
|
+
- ADD_ON_ERROR
|
193
196
|
- USE_DEFAULT_AUTO_CAPTURE_SESSIONS
|
194
197
|
restart: "no"
|
195
198
|
|
@@ -225,6 +228,7 @@ services:
|
|
225
228
|
- BUGSNAG_TIMEOUT
|
226
229
|
- CALLBACK_INITIATOR
|
227
230
|
- SQL_ONLY_BREADCRUMBS
|
231
|
+
- ADD_ON_ERROR
|
228
232
|
- USE_DEFAULT_AUTO_CAPTURE_SESSIONS
|
229
233
|
restart: "no"
|
230
234
|
networks:
|
@@ -296,4 +300,4 @@ services:
|
|
296
300
|
|
297
301
|
networks:
|
298
302
|
default:
|
299
|
-
name: ${NETWORK_NAME}
|
303
|
+
name: ${NETWORK_NAME}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'bugsnag'
|
2
|
+
require './app'
|
3
|
+
|
4
|
+
configure_basics
|
5
|
+
|
6
|
+
def run(callback)
|
7
|
+
Bugsnag.add_on_error(callback)
|
8
|
+
step_one
|
9
|
+
end
|
10
|
+
|
11
|
+
def step_one
|
12
|
+
step_two
|
13
|
+
end
|
14
|
+
|
15
|
+
def step_two
|
16
|
+
step_three
|
17
|
+
end
|
18
|
+
|
19
|
+
def step_three
|
20
|
+
crash
|
21
|
+
end
|
22
|
+
|
23
|
+
def crash
|
24
|
+
begin
|
25
|
+
"Test".insrt(-1, "!")
|
26
|
+
rescue Exception => e
|
27
|
+
Bugsnag.notify(e)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'bugsnag'
|
2
|
+
require './app'
|
3
|
+
|
4
|
+
configure_basics
|
5
|
+
add_at_exit
|
6
|
+
|
7
|
+
def run(callback)
|
8
|
+
Bugsnag.add_on_error(callback)
|
9
|
+
step_one
|
10
|
+
end
|
11
|
+
|
12
|
+
def step_one
|
13
|
+
step_two
|
14
|
+
end
|
15
|
+
|
16
|
+
def step_two
|
17
|
+
step_three
|
18
|
+
end
|
19
|
+
|
20
|
+
def step_three
|
21
|
+
crash
|
22
|
+
end
|
23
|
+
|
24
|
+
def crash
|
25
|
+
raise RuntimeError.new "Oh no"
|
26
|
+
end
|
@@ -18,4 +18,12 @@ Bugsnag.configure do |config|
|
|
18
18
|
breadcrumb.ignore! unless breadcrumb.meta_data[:event_name] == "sql.active_record" && breadcrumb.meta_data[:name] == "User Load"
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
if ENV["ADD_ON_ERROR"] == "true"
|
23
|
+
config.add_on_error(proc do |report|
|
24
|
+
report.add_tab(:on_error, {
|
25
|
+
source: report.unhandled ? 'on_error unhandled' : 'on_error handled'
|
26
|
+
})
|
27
|
+
end)
|
28
|
+
end
|
21
29
|
end
|
@@ -18,4 +18,12 @@ Bugsnag.configure do |config|
|
|
18
18
|
breadcrumb.ignore! unless breadcrumb.meta_data[:event_name] == "sql.active_record" && breadcrumb.meta_data[:name] == "User Load"
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
if ENV["ADD_ON_ERROR"] == "true"
|
23
|
+
config.add_on_error(proc do |report|
|
24
|
+
report.add_tab(:on_error, {
|
25
|
+
source: report.unhandled ? 'on_error unhandled' : 'on_error handled'
|
26
|
+
})
|
27
|
+
end)
|
28
|
+
end
|
21
29
|
end
|
@@ -18,4 +18,12 @@ Bugsnag.configure do |config|
|
|
18
18
|
breadcrumb.ignore! unless breadcrumb.meta_data[:event_name] == "sql.active_record" && breadcrumb.meta_data[:name] == "User Load"
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
if ENV["ADD_ON_ERROR"] == "true"
|
23
|
+
config.add_on_error(proc do |report|
|
24
|
+
report.add_tab(:on_error, {
|
25
|
+
source: report.unhandled ? 'on_error unhandled' : 'on_error handled'
|
26
|
+
})
|
27
|
+
end)
|
28
|
+
end
|
21
29
|
end
|
@@ -18,4 +18,12 @@ Bugsnag.configure do |config|
|
|
18
18
|
breadcrumb.ignore! unless breadcrumb.meta_data[:event_name] == "sql.active_record" && breadcrumb.meta_data[:name] == "User Load"
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
if ENV["ADD_ON_ERROR"] == "true"
|
23
|
+
config.add_on_error(proc do |report|
|
24
|
+
report.add_tab(:on_error, {
|
25
|
+
source: report.unhandled ? 'on_error unhandled' : 'on_error handled'
|
26
|
+
})
|
27
|
+
end)
|
28
|
+
end
|
21
29
|
end
|
@@ -15,6 +15,8 @@ Scenario Outline: Metadata can be added to a report using add_tab
|
|
15
15
|
| handled_before_notify |
|
16
16
|
| handled_block |
|
17
17
|
| unhandled_before_notify |
|
18
|
+
| handled_on_error |
|
19
|
+
| unhandled_on_error |
|
18
20
|
|
19
21
|
Scenario Outline: Metadata can be added to an existing tab using add_tab
|
20
22
|
Given I set environment variable "CALLBACK_INITIATOR" to "<initiator>"
|
@@ -33,6 +35,8 @@ Scenario Outline: Metadata can be added to an existing tab using add_tab
|
|
33
35
|
| handled_before_notify |
|
34
36
|
| handled_block |
|
35
37
|
| unhandled_before_notify |
|
38
|
+
| handled_on_error |
|
39
|
+
| unhandled_on_error |
|
36
40
|
|
37
41
|
Scenario Outline: Metadata can be overwritten using add_tab
|
38
42
|
Given I set environment variable "CALLBACK_INITIATOR" to "<initiator>"
|
@@ -46,4 +50,6 @@ Scenario Outline: Metadata can be overwritten using add_tab
|
|
46
50
|
| initiator |
|
47
51
|
| handled_before_notify |
|
48
52
|
| handled_block |
|
49
|
-
| unhandled_before_notify |
|
53
|
+
| unhandled_before_notify |
|
54
|
+
| handled_on_error |
|
55
|
+
| unhandled_on_error |
|
@@ -12,6 +12,8 @@ Scenario Outline: Stack frames can be removed
|
|
12
12
|
| initiator | lineNumber |
|
13
13
|
| handled_before_notify | 20 |
|
14
14
|
| unhandled_before_notify | 21 |
|
15
|
+
| handled_on_error | 20 |
|
16
|
+
| unhandled_on_error | 21 |
|
15
17
|
|
16
18
|
Scenario: Stack frames can be removed from a notified string
|
17
19
|
Given I set environment variable "CALLBACK_INITIATOR" to "handled_block"
|
@@ -36,6 +38,8 @@ Scenario Outline: Stack frames can be marked as in project
|
|
36
38
|
| initiator |
|
37
39
|
| handled_before_notify |
|
38
40
|
| unhandled_before_notify |
|
41
|
+
| handled_on_error |
|
42
|
+
| unhandled_on_error |
|
39
43
|
|
40
44
|
Scenario: Stack frames can be marked as in project with a handled string
|
41
45
|
Given I set environment variable "CALLBACK_INITIATOR" to "handled_block"
|
@@ -14,6 +14,8 @@ Scenario Outline: A report can have a user name, email, and id set
|
|
14
14
|
| handled_before_notify |
|
15
15
|
| handled_block |
|
16
16
|
| unhandled_before_notify |
|
17
|
+
| handled_on_error |
|
18
|
+
| unhandled_on_error |
|
17
19
|
|
18
20
|
Scenario Outline: A report can have custom info set
|
19
21
|
Given I set environment variable "CALLBACK_INITIATOR" to "<initiator>"
|
@@ -30,6 +32,8 @@ Scenario Outline: A report can have custom info set
|
|
30
32
|
| handled_before_notify |
|
31
33
|
| handled_block |
|
32
34
|
| unhandled_before_notify |
|
35
|
+
| handled_on_error |
|
36
|
+
| unhandled_on_error |
|
33
37
|
|
34
38
|
Scenario Outline: A report can have its user info removed
|
35
39
|
Given I set environment variable "CALLBACK_INITIATOR" to "<initiator>"
|
@@ -42,4 +46,6 @@ Scenario Outline: A report can have its user info removed
|
|
42
46
|
| initiator |
|
43
47
|
| handled_before_notify |
|
44
48
|
| handled_block |
|
45
|
-
| unhandled_before_notify |
|
49
|
+
| unhandled_before_notify |
|
50
|
+
| handled_on_error |
|
51
|
+
| unhandled_on_error |
|
@@ -0,0 +1,29 @@
|
|
1
|
+
Feature: On error callbacks
|
2
|
+
|
3
|
+
@rails3 @rails4 @rails5 @rails6
|
4
|
+
Scenario: Rails on_error works on handled errors
|
5
|
+
Given I set environment variable "ADD_ON_ERROR" to "true"
|
6
|
+
And I start the rails service
|
7
|
+
When I navigate to the route "/handled/unthrown" on the rails app
|
8
|
+
And I wait to receive a request
|
9
|
+
Then the request is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier"
|
10
|
+
And the exception "errorClass" equals "RuntimeError"
|
11
|
+
And the exception "message" starts with "handled unthrown error"
|
12
|
+
And the event "unhandled" is false
|
13
|
+
And the event "app.type" equals "rails"
|
14
|
+
And the event "metaData.request.url" ends with "/handled/unthrown"
|
15
|
+
And the event "metaData.on_error.source" equals "on_error handled"
|
16
|
+
|
17
|
+
@rails3 @rails4 @rails5 @rails6
|
18
|
+
Scenario: Rails on_error works on unhandled errors
|
19
|
+
Given I set environment variable "ADD_ON_ERROR" to "true"
|
20
|
+
And I start the rails service
|
21
|
+
When I navigate to the route "/unhandled/error" on the rails app
|
22
|
+
And I wait to receive a request
|
23
|
+
Then the request is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier"
|
24
|
+
And the exception "errorClass" equals "NameError"
|
25
|
+
And the exception "message" starts with "undefined local variable or method `generate_unhandled_error' for #<UnhandledController"
|
26
|
+
And the event "unhandled" is true
|
27
|
+
And the event "app.type" equals "rails"
|
28
|
+
And the event "metaData.request.url" ends with "/unhandled/error"
|
29
|
+
And the event "metaData.on_error.source" equals "on_error unhandled"
|
data/lib/bugsnag.rb
CHANGED
@@ -48,6 +48,12 @@ module Bugsnag
|
|
48
48
|
def configure(validate_api_key=true)
|
49
49
|
yield(configuration) if block_given?
|
50
50
|
|
51
|
+
# Create the session tracker if sessions are enabled to avoid the overhead
|
52
|
+
# of creating it on the first request. We skip this if we're not validating
|
53
|
+
# the API key as we use this internally before the user's configure block
|
54
|
+
# has run, so we don't know if sessions are enabled yet.
|
55
|
+
session_tracker if validate_api_key && configuration.auto_capture_sessions
|
56
|
+
|
51
57
|
check_key_valid if validate_api_key
|
52
58
|
check_endpoint_setup
|
53
59
|
|
@@ -169,6 +175,8 @@ module Bugsnag
|
|
169
175
|
# Allow access to "before notify" callbacks as an array.
|
170
176
|
#
|
171
177
|
# These callbacks will be called whenever an error notification is being made.
|
178
|
+
#
|
179
|
+
# @deprecated Use {Bugsnag#add_on_error} instead
|
172
180
|
def before_notify_callbacks
|
173
181
|
Bugsnag.configuration.request_data[:before_callbacks] ||= []
|
174
182
|
end
|
@@ -227,6 +235,33 @@ module Bugsnag
|
|
227
235
|
configuration.breadcrumbs << breadcrumb unless breadcrumb.ignore?
|
228
236
|
end
|
229
237
|
|
238
|
+
##
|
239
|
+
# Add the given callback to the list of on_error callbacks
|
240
|
+
#
|
241
|
+
# The on_error callbacks will be called when an error is captured or reported
|
242
|
+
# and are passed a {Bugsnag::Report} object
|
243
|
+
#
|
244
|
+
# Returning false from an on_error callback will cause the error to be ignored
|
245
|
+
# and will prevent any remaining callbacks from being called
|
246
|
+
#
|
247
|
+
# @param callback [Proc]
|
248
|
+
# @return [void]
|
249
|
+
def add_on_error(callback)
|
250
|
+
configuration.add_on_error(callback)
|
251
|
+
end
|
252
|
+
|
253
|
+
##
|
254
|
+
# Remove the given callback from the list of on_error callbacks
|
255
|
+
#
|
256
|
+
# Note that this must be the same Proc instance that was passed to
|
257
|
+
# {Bugsnag#add_on_error}, otherwise it will not be removed
|
258
|
+
#
|
259
|
+
# @param callback [Proc]
|
260
|
+
# @return [void]
|
261
|
+
def remove_on_error(callback)
|
262
|
+
configuration.remove_on_error(callback)
|
263
|
+
end
|
264
|
+
|
230
265
|
##
|
231
266
|
# Returns the client's Cleaner object, or creates one if not yet created.
|
232
267
|
#
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module Bugsnag
|
2
|
+
# @api private
|
3
|
+
class CodeExtractor
|
4
|
+
MAXIMUM_LINES_TO_KEEP = 7
|
5
|
+
|
6
|
+
##
|
7
|
+
# @param configuration [Configuration]
|
8
|
+
def initialize(configuration)
|
9
|
+
@files = {}
|
10
|
+
@configuration = configuration
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# Add a file and its corresponding trace hash to be processed.
|
15
|
+
#
|
16
|
+
# @param path [String] The full path to the file
|
17
|
+
# @param trace [Hash]
|
18
|
+
# @return [void]
|
19
|
+
def add_file(path, trace)
|
20
|
+
# If the file doesn't exist we can't extract code from it, so we can skip
|
21
|
+
# this file entirely
|
22
|
+
unless File.exist?(path)
|
23
|
+
trace[:code] = nil
|
24
|
+
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
@files[path] ||= []
|
29
|
+
@files[path].push(trace)
|
30
|
+
|
31
|
+
# Record the line numbers we want to fetch for this trace
|
32
|
+
# We grab extra lines so that we can compensate if the error is on the
|
33
|
+
# first or last line of a file
|
34
|
+
first_line_number = trace[:lineNumber] - MAXIMUM_LINES_TO_KEEP
|
35
|
+
|
36
|
+
trace[:first_line_number] = first_line_number < 1 ? 1 : first_line_number
|
37
|
+
trace[:last_line_number] = trace[:lineNumber] + MAXIMUM_LINES_TO_KEEP
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Add the code to the hashes that were given in {#add_file} by modifying
|
42
|
+
# them in-place. They will have a new ':code' key containing a hash of line
|
43
|
+
# number => string of code for that line
|
44
|
+
#
|
45
|
+
# @return [void]
|
46
|
+
def extract!
|
47
|
+
@files.each do |path, traces|
|
48
|
+
begin
|
49
|
+
line_numbers = Set.new
|
50
|
+
|
51
|
+
traces.each do |trace|
|
52
|
+
trace[:first_line_number].upto(trace[:last_line_number]) do |line_number|
|
53
|
+
line_numbers << line_number
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
extract_from(path, traces, line_numbers)
|
58
|
+
rescue StandardError => e
|
59
|
+
# Clean up after ourselves
|
60
|
+
traces.each do |trace|
|
61
|
+
trace[:code] ||= nil
|
62
|
+
trace.delete(:first_line_number)
|
63
|
+
trace.delete(:last_line_number)
|
64
|
+
end
|
65
|
+
|
66
|
+
@configuration.warn("Error extracting code: #{e.inspect}")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
##
|
74
|
+
# @param path [String]
|
75
|
+
# @param traces [Array<Hash>]
|
76
|
+
# @param line_numbers [Set<Integer>]
|
77
|
+
# @return [void]
|
78
|
+
def extract_from(path, traces, line_numbers)
|
79
|
+
code = {}
|
80
|
+
|
81
|
+
File.open(path) do |file|
|
82
|
+
current_line_number = 0
|
83
|
+
|
84
|
+
file.each_line do |line|
|
85
|
+
current_line_number += 1
|
86
|
+
|
87
|
+
next unless line_numbers.include?(current_line_number)
|
88
|
+
|
89
|
+
code[current_line_number] = line[0...200].rstrip
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
associate_code_with_trace(code, traces)
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# @param code [Hash{Integer => String}]
|
98
|
+
# @param traces [Array<Hash>]
|
99
|
+
# @return [void]
|
100
|
+
def associate_code_with_trace(code, traces)
|
101
|
+
traces.each do |trace|
|
102
|
+
trace[:code] = {}
|
103
|
+
|
104
|
+
code.each do |line_number, line|
|
105
|
+
# If we've gone past the last line we care about, we can stop iterating
|
106
|
+
break if line_number > trace[:last_line_number]
|
107
|
+
|
108
|
+
# Skip lines that aren't in the range we want
|
109
|
+
next unless line_number >= trace[:first_line_number]
|
110
|
+
|
111
|
+
trace[:code][line_number] = line
|
112
|
+
end
|
113
|
+
|
114
|
+
trim_excess_lines(trace[:code], trace[:lineNumber])
|
115
|
+
trace.delete(:first_line_number)
|
116
|
+
trace.delete(:last_line_number)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# @param code [Hash{Integer => String}]
|
122
|
+
# @param line_number [Integer]
|
123
|
+
# @return [void]
|
124
|
+
def trim_excess_lines(code, line_number)
|
125
|
+
while code.length > MAXIMUM_LINES_TO_KEEP
|
126
|
+
last_line = code.keys.max
|
127
|
+
first_line = code.keys.min
|
128
|
+
|
129
|
+
if (last_line - line_number) > (line_number - first_line)
|
130
|
+
code.delete(last_line)
|
131
|
+
else
|
132
|
+
code.delete(first_line)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|