enhanced_errors 2.0.6 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e6ed241407a54376e0c64f2c1adc087e3dae4980ac5b7e84c947d92ff787392
4
- data.tar.gz: 4f949a5145869e73e7f795a9c009800433956162a6d25695c2a9e8ba3a39da05
3
+ metadata.gz: 03cd9eda304184a474e2cccf59199027d622d2a32f5b90e93f75a0d1dbed4530
4
+ data.tar.gz: 48d4974d2b53c00155a6768d22f814ce072fd0d407051fab955d72f156fa81cc
5
5
  SHA512:
6
- metadata.gz: 02312f5287c4214c59b4b4a53e723d53d4dff511f24c335a7f7a84f049bb06638f77ea22f488cba5682fba66f581116b2bb1a4b3a79d8b6a5a290f5f822f1ea8
7
- data.tar.gz: 13d06fa670a3f34bb67624ec40bc9a87810536aace8c2867e64620092c13d8e48d25675241491d821491cb2f3a893c1d8faa2ede7ff2f22c45b50fb506fbbd74
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>
9
+ EnhancedErrors captures exception context using either a test-framework integration (RSpec/Minitest) or a global enhancement for runtime exceptions.
11
10
 
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
- ```
36
-
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,17 +32,13 @@ 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
 
@@ -84,26 +48,68 @@ RSpec.configure do |config|
84
48
  end
85
49
 
86
50
  config.after(:example) do |example|
87
- example.metadata[:expect_binding] = EnhancedErrors.stop_rspec_binding_capture
88
- EnhancedErrors.override_exception_message(example.exception, example.metadata[:expect_binding])
51
+ EnhancedErrors.override_exception_message(example.exception, EnhancedErrors.stop_rspec_binding_capture)
89
52
  end
90
53
  end
91
54
  ```
92
55
 
93
- ## TODO: Minitest
56
+ <br>
94
57
 
95
58
 
96
- ## Enhancing .message
59
+ ## MiniTest Setup
97
60
 
98
- EnhancedErrors can also append the captured variable description into the Exception's
99
- .message method output if the override_messages argument is true.
61
+ ```ruby
62
+ require 'enhanced_errors'
63
+ require 'enhanced/minitest_patch'
64
+
65
+ # Once the patch is loaded, it should just work!
66
+
67
+ ```
100
68
 
101
- This can be very convenient as it lets you capture and diagnose
102
- the context of totally unanticipated exceptions without modifying all your error handlers.
69
+ <br>
70
+
71
+ ### Enhanced Errors In Everyday Ruby Exceptions:
103
72
 
104
- The downside to this approach is that if you have expectations in your tests/specs
105
- around exception messages, those may break. Also, if you are doing something with the error messages,
106
- like storing them in a database, they could be *much* longer and that may pose an issue.
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
92
+
93
+ foo
94
+ ```
95
+
96
+
97
+ ### Enhancing .message
98
+
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
+ ```
104
+
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.
107
+
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.
107
113
 
108
114
  Ideally, use exception.captured_variables instead.
109
115
 
@@ -112,6 +118,13 @@ EnhancedErrors.enhance_exceptions!(override_messages: true)
112
118
  ```
113
119
 
114
120
 
121
+ #### Output:
122
+
123
+ <img src="./doc/images/enhanced-error.png" style="height: 215px; width: 429px;"></img>
124
+ <br>
125
+
126
+
127
+
115
128
  ## Features
116
129
 
117
130
  - **Pure Ruby**: No external dependencies, C extensions, or C API calls.
@@ -124,26 +137,13 @@ EnhancedErrors.enhance_exceptions!(override_messages: true)
124
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.
125
138
  - **Lightweight**: Minimal performance impact, as tracing is only active during exception raising.
126
139
 
127
- EnhancedErrors has a few big use-cases:
128
-
129
- * **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.
130
- 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.
131
- Ideally, without long instrument-re-run-fix loops. If your logging didn't capture the data, normally, you'd be stuck.
132
-
133
- * **Debug** a complex application erroring deep in the stack when you can't tell where the error originates.
134
-
135
- * **Reduce MTTR** Reduce mean time to resolution.
136
-
137
- * **Faster CI -> Fix loop**. When a bug happens in CI, usually there's a step where you first reproduce it locally.
138
- EnhancedErrors can help you skip that step.
140
+ EnhancedErrors use-cases:
139
141
 
140
- * **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.
141
-
142
- * **Heisenbugs** - bugs that disappear when you try to debug them. EnhancedErrors can help you capture the data that causes the bug before it disappears.
143
-
144
- * **Unknown Unknowns** - you can't pre-emptively log variables from failure cases you never imagined.
145
-
146
- * **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.
147
147
 
148
148
  ## Installation
149
149
 
@@ -180,14 +180,15 @@ EnhancedErrors.enhance_exceptions!(override_messages: true)
180
180
 
181
181
  ```
182
182
 
183
- The approach above activates the TracePoint to start capturing exceptions and their surrounding context.
184
- 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.
185
185
 
186
186
  If modifying your exception handlers is an option, it is better *not* to use
187
- override_messages: true, but instead just use the exception.captured_variables, which is
188
- 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.
189
189
 
190
- 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!
191
192
 
192
193
  ### Configuration Options
193
194
 
@@ -205,9 +206,6 @@ end
205
206
  - `enabled`: Enables or disables the enhancement (default: `true`).
206
207
  - `max_length`: Sets the maximum length of the captured_variables string (default: `2500`).
207
208
 
208
- Currently, the first `raise` exception binding is presented.
209
- This may be changed in the future to allow more binding data to be presented.
210
-
211
209
 
212
210
  ### Environment-Based Defaults
213
211
 
@@ -338,8 +336,8 @@ SystemStackError Psych::BadAlias
338
336
 
339
337
  While this is close to "Things that don't descend from StandardError", it's not exactly that.
340
338
 
341
- In Info mode, variables starting with @_ are also ignored.
342
-
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.
343
341
 
344
342
  ### Capture Levels
345
343
 
@@ -398,7 +396,6 @@ The captured data is available in .captured_variables, to provide context for de
398
396
  * There are benchmarks around Tracepoint in the benchmark folder. Targeted tracepoints
399
397
  seem to be very cheap--as in, you can hit them ten thousand+ times a second
400
398
  without heavy overhead.
401
- *
402
399
 
403
400
  ## Awesome Print
404
401
 
@@ -419,28 +416,30 @@ Why not use:
419
416
 
420
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)?
421
418
 
422
- 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!
423
420
 
424
- 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.
425
422
 
426
- With EnhancedErrors is that I want extra details when I run into a problem I __didn't anticipate ahead of time__.
427
- To make that work, it has to be able to safely be 'on' all the time, and it has to gather the data in
428
- 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.
429
426
 
430
- - That won't interrupt CI, but also, that lets me know what happened without reproduction
431
- - That could, theoretically, also be fine in production (if data security, redaction, access, and encryption concerns were all addressed--Ok, big
432
- 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.
433
431
  - Has decent performance characteristics
434
432
  - **Only** becomes active in exception raise/rescue scenarios
435
433
 
436
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).
437
- However, the recommendation is not to use those in production as they use C API extensions. This doesn't. This selectively uses
438
- 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.
439
438
 
440
439
 
441
440
  ## Performance Considerations
442
441
 
443
- - **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)
444
443
 
445
444
  - **TBD**: Memory considerations. This does capture data when an exception happens. EnhancedErrors hides under the bed when it sees **NoMemoryError**.
446
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.6"
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