bugsnag 6.14.0 → 6.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|