enhanced_errors 2.0.5 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3897d29e868998ef225633f9f0f0f99a994b55563d65bff843ade348e7229bb9
4
- data.tar.gz: 37f1db45f0c18c64846d396088f71bab2312b89da1a375b4d325309832983efc
3
+ metadata.gz: 03cd9eda304184a474e2cccf59199027d622d2a32f5b90e93f75a0d1dbed4530
4
+ data.tar.gz: 48d4974d2b53c00155a6768d22f814ce072fd0d407051fab955d72f156fa81cc
5
5
  SHA512:
6
- metadata.gz: b156387888d9479f5d474808a922127fbfc5c6ed7035c6d8ef552dabca39b550655e191bfc6f0f11d64db36ded66fe03619cca7eaff2f5b725abb3d106f07d87
7
- data.tar.gz: ce0fcadb20a8be1f0b53921cf990e5c458ce6ba1099dc5b9ceff1c8459ab82b707234bfbf964b10036a5b37a33b0248337d0289005b0f32c61933efa25ef44de
6
+ metadata.gz: 03c169ed6955512bd4ce743d9343199f4a0f833a3d663be47f366fe8be0a46b8b749fc7f6e326f07a008e16f26cfbed790f561829068e61eecb6510ae95a3b20
7
+ data.tar.gz: 38ef1f2cb5daa334fa067cdbffd873046e7936688556d1393a5e10a7e7992eb677e47b1fd6c91a97f2d511b76a5e8acd3bbcd4118505a5b4180c944c4627bf00
data/README.md CHANGED
@@ -2,45 +2,13 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- **EnhancedErrors** is a pure Ruby gem that enhances exceptions by capturing variables and their values from the scope where the error was raised.
5
+ **EnhancedErrors** is a lightweight Ruby gem that enhances exceptions by capturing variables and their values from the scope where the exception was raised.
6
6
 
7
7
  **EnhancedErrors** leverages Ruby's built-in [TracePoint](https://ruby-doc.org/core-3.1.0/TracePoint.html) feature to provide detailed context for exceptions, making debugging easier without significant performance overhead.
8
8
 
9
- When an exception is raised, EnhancedErrors captures the surrounding context. It works like this:
10
- <br>
11
-
12
- #### Enhanced Exception In Code:
13
-
14
- ```ruby
15
-
16
- require 'enhanced_errors'
17
- require 'awesome_print' # Optional, for better output
18
-
19
- # Enable capturing of variables at exception at raise-time. The .captured_variables method
20
- # is added to all Exceptions and gets populated with in-scope variables and values on `raise`
21
-
22
- EnhancedErrors.enhance_exceptions!
23
-
24
- def foo
25
- begin
26
- myvar = 0
27
- @myinstance = 10
28
- foo = @myinstance / myvar
29
- rescue => e
30
- puts e.captured_variables
31
- end
32
- end
33
-
34
- foo
35
- ```
9
+ EnhancedErrors captures exception context using either a test-framework integration (RSpec/Minitest) or a global enhancement for runtime exceptions.
36
10
 
37
- ##### Output:
38
-
39
- <img src="./doc/images/enhanced-error.png" style="height: 215px; width: 429px;"></img>
40
- <br>
41
-
42
-
43
- #### Enhanced Exception In Specs:
11
+ ### Enhanced Errors In RSpec:
44
12
 
45
13
  ```ruby
46
14
  describe 'sees through' do
@@ -64,60 +32,84 @@ end
64
32
 
65
33
  <img src="./doc/images/enhanced-spec.png" style="height: 369px; width: 712px;"></img>
66
34
 
67
- # RSpec Setup
68
35
 
69
- The simplest way to get started with EnhancedErrors is to use it for RSpec
70
- exception capturing. To get variable output into RSpec, the approach below
71
- enables capturing, but also gives nice output by formatting the failure message
72
- with the variable capture.
73
36
 
74
- The advantage of this approach is that it is only active for your spec runs.
75
- This approach is ideal for CI and local testing because it doesn't make
76
- any changes that should bleed through to production--it doesn't enhance
77
- exceptions except those that pass by during the RSpec run.
37
+ The RSpec test-time only approach constrained only to test-time.
38
+
39
+ ### RSpec Setup
40
+
41
+ Use EnhancedErrors with RSpec for test-specific exception capturing, ideal for CI and local testing without impacting production.
78
42
 
79
43
  ```ruby
80
44
 
81
45
  RSpec.configure do |config|
82
-
83
- # add these config changes to your RSpec config to get variable messages
84
- config.before(:suite) do
85
- RSpec::Core::Example.prepend(Enhanced::Integrations::RSpecErrorFailureMessage)
86
- end
87
-
88
46
  config.before(:example) do |_example|
89
47
  EnhancedErrors.start_rspec_binding_capture
90
48
  end
91
49
 
92
50
  config.after(:example) do |example|
93
- example.metadata[:expect_binding] = EnhancedErrors.stop_rspec_binding_capture
51
+ EnhancedErrors.override_exception_message(example.exception, EnhancedErrors.stop_rspec_binding_capture)
94
52
  end
95
-
96
53
  end
54
+ ```
55
+
56
+ <br>
57
+
58
+
59
+ ## MiniTest Setup
60
+
61
+ ```ruby
62
+ require 'enhanced_errors'
63
+ require 'enhanced/minitest_patch'
64
+
65
+ # Once the patch is loaded, it should just work!
97
66
 
98
67
  ```
99
68
 
100
- ## Minitest
69
+ <br>
70
+
71
+ ### Enhanced Errors In Everyday Ruby Exceptions:
72
+
73
+ ```ruby
74
+
75
+ require 'enhanced_errors'
76
+ require 'awesome_print' # Optional, for better output
77
+
78
+ # Enable capturing of variables at exception at raise-time. The .captured_variables method
79
+ # is added to all Exceptions and gets populated with in-scope variables and values on `raise`
80
+
81
+ EnhancedErrors.enhance_exceptions!
82
+
83
+ def foo
84
+ begin
85
+ myvar = 0
86
+ @myinstance = 10
87
+ foo = @myinstance / myvar
88
+ rescue => e
89
+ puts e.captured_variables
90
+ end
91
+ end
101
92
 
102
- Untested as of yet, but enhance_exceptions!(override_messages: true) is likely to work.
93
+ foo
94
+ ```
103
95
 
104
- If anyone wants to look into an integration implementation like RSpec, it would
105
- be welcomed. With a more targeted approach like the RSpec one, exceptions could be captured and
106
- modified only during test time, like the RSpec approach. This would be advantageous as
107
- it wouldn't modify the exception message itself, but still makes the variable output available in
108
- test messages.
109
96
 
110
- ## Enhancing .message
97
+ ### Enhancing .message
111
98
 
112
- EnhancedErrors can also append the captured variable description into the Exception's
113
- .message method output if the override_messages argument is true.
99
+ EnhancedErrors can append the captured variable description onto every Exception's
100
+ .message method with
101
+ ```ruby
102
+ EnhancedErrors.enhance_exceptions(override_messages: true)
103
+ ```
114
104
 
115
- This can be very convenient as it lets you capture and diagnose
116
- the context of totally unanticipated exceptions without modifying all your error handlers.
105
+ This captures unanticipated exceptions without modifying all your error handlers.
106
+ This approach can be used to get detailed logs when problems happen in something like a cron-job.
117
107
 
118
- The downside to this approach is that if you have expectations in your tests/specs
119
- around exception messages, those may break. Also, if you are doing something with the error messages,
120
- like storing them in a database, they could be *much* longer and that may pose an issue.
108
+ The tradeoff of this approach is that if you have expectations in your tests/specs around
109
+ exception messages, those may break. Also, if you are doing something like storing the errors
110
+ in a database, they could be *much* longer and that may pose an issue on field lengths.
111
+ Or if you are writing your logs to Datadog, New Relic, Splunk, etc, log messages for
112
+ errors will be longer, and you should consider what data/PII you are sharing.
121
113
 
122
114
  Ideally, use exception.captured_variables instead.
123
115
 
@@ -126,6 +118,13 @@ EnhancedErrors.enhance_exceptions!(override_messages: true)
126
118
  ```
127
119
 
128
120
 
121
+ #### Output:
122
+
123
+ <img src="./doc/images/enhanced-error.png" style="height: 215px; width: 429px;"></img>
124
+ <br>
125
+
126
+
127
+
129
128
  ## Features
130
129
 
131
130
  - **Pure Ruby**: No external dependencies, C extensions, or C API calls.
@@ -138,26 +137,13 @@ EnhancedErrors.enhance_exceptions!(override_messages: true)
138
137
  - **No dependencies**: EnhancedErrors does not ___require___ any dependencies--it uses [awesome_print](https://github.com/awesome-print/awesome_print) for nicer output if it is installed and available.
139
138
  - **Lightweight**: Minimal performance impact, as tracing is only active during exception raising.
140
139
 
141
- EnhancedErrors has a few big use-cases:
142
-
143
- * **Catch Data-driven bugs**. For example, if, while processing a 10 gig file, you get an error, you can't just re-run the code with a debugger.
144
- You also can't just print out all the data, because it's too big. You want to know what the data was the cause of the error.
145
- Ideally, without long instrument-re-run-fix loops. If your logging didn't capture the data, normally, you'd be stuck.
146
-
147
- * **Debug** a complex application erroring deep in the stack when you can't tell where the error originates.
148
-
149
- * **Reduce MTTR** Reduce mean time to resolution.
150
-
151
- * **Faster CI -> Fix loop**. When a bug happens in CI, usually there's a step where you first reproduce it locally.
152
- EnhancedErrors can help you skip that step.
140
+ EnhancedErrors use-cases:
153
141
 
154
- * **Faster TDD**. In general, you can skip the add-instrumentation step and jump to the fix. Usually, you won't have to re-run to see an error.
155
-
156
- * **Heisenbugs** - bugs that disappear when you try to debug them. EnhancedErrors can help you capture the data that causes the bug before it disappears.
157
-
158
- * **Unknown Unknowns** - you can't pre-emptively log variables from failure cases you never imagined.
159
-
160
- * **Cron jobs** and **daemons** - when it fails for unknown reasons at 4am, check the log and fix--it probably has what you need. Note that
142
+ * Catch data-driven bugs without needing re-runs or extensive logging.
143
+ * Debug deep-stack errors and reduce mean time to resolution (MTTR).
144
+ * Handle CI failures faster by skipping reproduction steps.
145
+ * Address elusive "Heisenbugs" by capturing error context preemptively.
146
+ * Debug cron jobs and daemons with rich, failure-specific logs.
161
147
 
162
148
  ## Installation
163
149
 
@@ -194,14 +180,15 @@ EnhancedErrors.enhance_exceptions!(override_messages: true)
194
180
 
195
181
  ```
196
182
 
197
- The approach above activates the TracePoint to start capturing exceptions and their surrounding context.
198
- It also overrides the .message to have the variables.
183
+ This captures all exceptions and their surrounding context.
184
+ It also overrides the .message to display the variables.
199
185
 
200
186
  If modifying your exception handlers is an option, it is better *not* to use
201
- override_messages: true, but instead just use the exception.captured_variables, which is
202
- a string describing what was found, that is available regardless.
187
+ but instead just use the exception.captured_variables, which is
188
+ a string describing what was found.
203
189
 
204
- Note that a minimalistic approach is taken to generating the string--if no qualifying variables were present, you won't see any message!
190
+ Note: a minimalistic approach is taken to generating the capture string.
191
+ If no qualifying variables were present, you won't see any message additions!
205
192
 
206
193
  ### Configuration Options
207
194
 
@@ -219,9 +206,6 @@ end
219
206
  - `enabled`: Enables or disables the enhancement (default: `true`).
220
207
  - `max_length`: Sets the maximum length of the captured_variables string (default: `2500`).
221
208
 
222
- Currently, the first `raise` exception binding is presented.
223
- This may be changed in the future to allow more binding data to be presented.
224
-
225
209
 
226
210
  ### Environment-Based Defaults
227
211
 
@@ -352,8 +336,8 @@ SystemStackError Psych::BadAlias
352
336
 
353
337
  While this is close to "Things that don't descend from StandardError", it's not exactly that.
354
338
 
355
- In Info mode, variables starting with @_ are also ignored.
356
-
339
+ By default, many noisy instance variables are ignored in the default skip list.
340
+ If you want to see every instance variable, you'll need to clear out the skip list.
357
341
 
358
342
  ### Capture Levels
359
343
 
@@ -412,7 +396,6 @@ The captured data is available in .captured_variables, to provide context for de
412
396
  * There are benchmarks around Tracepoint in the benchmark folder. Targeted tracepoints
413
397
  seem to be very cheap--as in, you can hit them ten thousand+ times a second
414
398
  without heavy overhead.
415
- *
416
399
 
417
400
  ## Awesome Print
418
401
 
@@ -433,28 +416,30 @@ Why not use:
433
416
 
434
417
  [binding_of_caller](https://github.com/banister/binding_of_caller) or [Pry](https://github.com/pry/pry) or [better_errors](https://github.com/BetterErrors/better_errors)?
435
418
 
436
- First off, these gems are, I cannot stress this enough, a-m-a-z-i-n-g!!! I use them every day--kudos to their creators and maintainers!
419
+ First off, these gems are a-m-a-z-i-n-g!!! I use them every day--kudos to their creators and maintainers!
437
420
 
438
- This is intended for different use-cases. In sum, the goal of this gem is an every-day driver for __non-interactive__ variable inspection.
421
+ EnhancedErrors is intended as an every-day driver for __non-interactive__ variable inspection.
439
422
 
440
- With EnhancedErrors is that I want extra details when I run into a problem I __didn't anticipate ahead of time__.
441
- To make that work, it has to be able to safely be 'on' all the time, and it has to gather the data in
442
- a way I naturally will see it without requiring extra preparation I obviously didn't know to do.
423
+ I want extra details when I run into a problem I __didn't anticipate ahead of time__.
424
+ To make that work, it has to be able to safely be 'on' ahead of time, and gather data in
425
+ a way I naturally will retain without requiring extra preparation I obviously didn't know to do.
443
426
 
444
- - That won't interrupt CI, but also, that lets me know what happened without reproduction
445
- - That could, theoretically, also be fine in production (if data security, redaction, access, and encryption concerns were all addressed--Ok, big
446
- list, but another option is to selectively enable targeted capture)
427
+ - EnhancedErrors won't interrupt CI, but it lets me know what happened _without_ reproduction steps
428
+ - EnhancedErrors could, theoretically, also be fine in production (if data security, redaction,
429
+ PII, access, and encryption concerns were all addressed.
430
+ Big list, but another option is to selectively enable targeted capture.
447
431
  - Has decent performance characteristics
448
432
  - **Only** becomes active in exception raise/rescue scenarios
449
433
 
450
434
  This gem could have been implemented using binding_of_caller, or the gem it depends on, [debug_inspector](https://rubygems.org/gems/debug_inspector/versions/1.1.0?locale=en).
451
- However, the recommendation is not to use those in production as they use C API extensions. This doesn't. This selectively uses
452
- Ruby's TracePoint binding capture very narrowly with no other C API or dependencies, and only to target Exceptions--not to allow universal calls to the prior binding. It doesn't work as a debugger, but that also means it can, with care, operate safely in a narrow scope--becoming active only when exceptions are raised.
435
+ However, the recommendation is not to use those in production as they use C API extensions. This doesn't.
436
+ EnhancedErrors selectively uses Ruby's TracePoint binding capture very narrowly with no other C API or dependencies, and only to target
437
+ Exceptions. It operates in a narrow scope--becoming active only when exceptions are raised.
453
438
 
454
439
 
455
440
  ## Performance Considerations
456
441
 
457
- - **Minimal Overhead**: Since TracePoint is only activated during exception raising and rescuing, the performance impact is negligible during normal operation. (Benchmark included)
442
+ - **Small Overhead**: Since TracePoint is only activated during exception raising and rescuing, the performance impact is negligible during normal operation. (Benchmark included)
458
443
 
459
444
  - **TBD**: Memory considerations. This does capture data when an exception happens. EnhancedErrors hides under the bed when it sees **NoMemoryError**.
460
445
 
Binary file
Binary file
Binary file
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "enhanced_errors"
3
- spec.version = "2.0.5"
3
+ spec.version = "2.1.0"
4
4
  spec.authors = ["Eric Beland"]
5
5
 
6
6
  spec.summary = "Automatically enhance your errors with messages containing variable values from the moment they were raised."
@@ -21,7 +21,10 @@ Gem::Specification.new do |spec|
21
21
  end
22
22
  spec.require_paths = ["lib"]
23
23
 
24
+ # For development on this gem
24
25
  spec.add_development_dependency "awesome_print", "~> 1.0"
25
26
  spec.add_development_dependency "rspec", "~> 3.0"
26
27
  spec.add_development_dependency 'yard', '~> 0.9'
28
+ spec.add_development_dependency 'minitest'
29
+
27
30
  end
@@ -1,6 +1,9 @@
1
1
  require './lib/enhanced_errors'
2
2
  require 'awesome_print' # Optional, for better output
3
3
 
4
+ # Demonstrates enhancing exceptions in normal day to day Ruby usage
5
+ # from this folder: ruby demo_exception_enhancement.rb
6
+
4
7
  EnhancedErrors.enhance_exceptions!(override_messages: true, capture_events: [:raise, :rescue])
5
8
 
6
9
  def foo
@@ -24,7 +27,6 @@ rescue Exception => e
24
27
  puts e.message
25
28
  end
26
29
 
27
-
28
30
  puts "\n--- Example with raise ---\n\n\n"
29
31
 
30
32
  foo
@@ -0,0 +1,22 @@
1
+ require 'minitest/autorun'
2
+ require 'enhanced_errors'
3
+ require 'enhanced/minitest_patch'
4
+
5
+ # You must install minitest and load it first to run this demo.
6
+ # EnhancedErrors does NOT ship with minitest as a dependency.
7
+
8
+ class MagicBallTest < Minitest::Test
9
+ def setup
10
+ @foo = 'bar'
11
+ end
12
+
13
+ def test_boo_capture
14
+ bee = 'fee'
15
+ assert false
16
+ end
17
+
18
+ def test_i_raise
19
+ zoo = 'zee'
20
+ raise "Crud"
21
+ end
22
+ end
@@ -0,0 +1,41 @@
1
+ # spec/enhanced_errors_spec.rb
2
+
3
+ # INSTRUCTIONS: Install rspec
4
+ # gem install rspec
5
+ # rspec examples/example_spec.rb
6
+
7
+ require 'rspec'
8
+ require_relative '../lib/enhanced_errors'
9
+
10
+ RSpec.configure do |config|
11
+
12
+ # -- Add to your RSPec config in your spec_helper.
13
+ config.before(:example) do |_example|
14
+ EnhancedErrors.start_rspec_binding_capture
15
+ end
16
+
17
+ config.after(:example) do |example|
18
+ EnhancedErrors.override_exception_message(example.exception, EnhancedErrors.stop_rspec_binding_capture)
19
+ end
20
+ # -- End EnhancedErrors config
21
+
22
+ end
23
+
24
+
25
+ RSpec.describe 'Neo' do
26
+ describe 'sees through' do
27
+ let(:the_matrix) { 'code rains, dramatically' }
28
+
29
+ before(:each) do
30
+ @spoon = 'there is no spoon'
31
+ end
32
+
33
+ it 'the matrix' do
34
+ #activate memoized item
35
+ the_matrix
36
+ stop = 'bullets'
37
+ raise 'No!'
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,17 @@
1
+ module Minitest
2
+ class << self
3
+ alias_method :original_run_one_method, :run_one_method
4
+
5
+ def run_one_method(klass, method_name)
6
+ EnhancedErrors.start_minitest_binding_capture
7
+ result = original_run_one_method(klass, method_name)
8
+ ensure
9
+ begin
10
+ binding_infos = EnhancedErrors.stop_minitest_binding_capture
11
+ EnhancedErrors.override_exception_message(result.failures.last, binding_infos) if result.failures.any?
12
+ rescue => e
13
+ puts "Ignored error during error enhancement: #{e}"
14
+ end
15
+ end
16
+ end
17
+ end