radar 0.2.0 → 0.3.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.
Files changed (39) hide show
  1. data/.yardopts +1 -0
  2. data/CHANGELOG.md +14 -1
  3. data/Gemfile +10 -4
  4. data/Gemfile.lock +65 -1
  5. data/README.md +10 -1
  6. data/docs/user_guide.md +214 -18
  7. data/examples/README.md +5 -0
  8. data/examples/rack/README.md +15 -0
  9. data/examples/rack/config.ru +18 -0
  10. data/lib/radar.rb +20 -2
  11. data/lib/radar/application.rb +30 -1
  12. data/lib/radar/config.rb +54 -12
  13. data/lib/radar/data_extensions/rack.rb +72 -0
  14. data/lib/radar/error.rb +1 -0
  15. data/lib/radar/exception_event.rb +6 -4
  16. data/lib/radar/filters/key_filter.rb +54 -0
  17. data/lib/radar/integration/rack.rb +41 -0
  18. data/lib/radar/integration/rails3.rb +19 -0
  19. data/lib/radar/integration/rails3/generator.rb +19 -0
  20. data/lib/radar/integration/rails3/railtie.rb +12 -0
  21. data/lib/radar/integration/rails3/templates/README +17 -0
  22. data/lib/radar/integration/rails3/templates/radar.rb +15 -0
  23. data/lib/radar/logger.rb +37 -0
  24. data/lib/radar/reporter/file_reporter.rb +31 -12
  25. data/lib/radar/reporter/io_reporter.rb +35 -0
  26. data/lib/radar/reporter/logger_reporter.rb +31 -0
  27. data/lib/radar/version.rb +1 -1
  28. data/radar.gemspec +2 -4
  29. data/test/radar/application_test.rb +38 -0
  30. data/test/radar/config_test.rb +34 -0
  31. data/test/radar/data_extensions/rack_test.rb +51 -0
  32. data/test/radar/exception_event_test.rb +20 -0
  33. data/test/radar/filters/key_filter_test.rb +28 -0
  34. data/test/radar/integration/rack_test.rb +61 -0
  35. data/test/radar/integration/rails3_test.rb +29 -0
  36. data/test/radar/logger_test.rb +13 -0
  37. data/test/radar/reporter/io_reporter_test.rb +20 -0
  38. data/test/radar/reporter/logger_reporter_test.rb +21 -0
  39. metadata +25 -4
data/.yardopts CHANGED
@@ -1,3 +1,4 @@
1
1
  -m markdown
2
+ --exclude 'lib/radar/integration/rails3/templates/*'
2
3
  --files
3
4
  docs/user_guide.md
@@ -1,4 +1,17 @@
1
- ## 0.2.0 (unreleased)
1
+ ## 0.3.0 (August 21, 2010)
2
+
3
+ - Added `KeyFilter` to filter out specific keys in the data hash.
4
+ - Added filters, which are called after data extensions as a way
5
+ to filter out information (passwords, shorten backtrace, etc.) [GH-19]
6
+ - Added `LoggerReporter` to log to any `Logger` object. [GH-20]
7
+ - Rails 3 integration. See user guide for more information.
8
+ - Rack integration. See user guide for more information. [GH-13]
9
+ - Added `IoReporter` to log to any `IO` object.
10
+ - Refinements to `FileReporter` and sprinkled logger statements around.
11
+ - Lightweight logging mechanism added so that you can verify Radar is doing
12
+ its job. [GH-9]
13
+
14
+ ## 0.2.0 (August 17, 2010)
2
15
 
3
16
  - Built in matcher: `:backtrace` (or `BacktraceMatcher`) which checks that
4
17
  the backtrace includes the given text. [GH-18]
data/Gemfile CHANGED
@@ -1,11 +1,17 @@
1
- source :gemcutter
1
+ source "http://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in radar.gemspec
4
- gemspec
3
+ # Specify the path directly so that files in the examples/
4
+ # directory just work.
5
+ gemspec :path => File.expand_path("../", __FILE__)
5
6
 
6
7
  # Additional gems which I don't really want in the gemspec but
7
8
  # are useful for development
8
9
  group :development do
9
- gem "yard", :git => "http://github.com/lsegal/yard.git"
10
+ gem "yard", :git => "http://github.com/lsegal/yard.git", :ref => "bf84275"
10
11
  gem "bluecloth"
11
12
  end
13
+
14
+ group :examples do
15
+ gem "rack"
16
+ gem "rails"
17
+ end
@@ -1,24 +1,86 @@
1
1
  GIT
2
2
  remote: http://github.com/lsegal/yard.git
3
3
  revision: bf84275
4
+ ref: bf84275
4
5
  specs:
5
6
  yard (0.6.0.rc1)
6
7
 
7
8
  PATH
8
9
  remote: .
9
10
  specs:
10
- radar (0.2.0)
11
+ radar (0.3.0)
11
12
  json (>= 1.4.6)
12
13
 
13
14
  GEM
14
15
  remote: http://rubygems.org/
15
16
  specs:
17
+ abstract (1.0.0)
18
+ actionmailer (3.0.0.rc)
19
+ actionpack (= 3.0.0.rc)
20
+ mail (~> 2.2.5)
21
+ actionpack (3.0.0.rc)
22
+ activemodel (= 3.0.0.rc)
23
+ activesupport (= 3.0.0.rc)
24
+ builder (~> 2.1.2)
25
+ erubis (~> 2.6.6)
26
+ i18n (~> 0.4.1)
27
+ rack (~> 1.2.1)
28
+ rack-mount (~> 0.6.9)
29
+ rack-test (~> 0.5.4)
30
+ tzinfo (~> 0.3.22)
31
+ activemodel (3.0.0.rc)
32
+ activesupport (= 3.0.0.rc)
33
+ builder (~> 2.1.2)
34
+ i18n (~> 0.4.1)
35
+ activerecord (3.0.0.rc)
36
+ activemodel (= 3.0.0.rc)
37
+ activesupport (= 3.0.0.rc)
38
+ arel (~> 0.4.0)
39
+ tzinfo (~> 0.3.22)
40
+ activeresource (3.0.0.rc)
41
+ activemodel (= 3.0.0.rc)
42
+ activesupport (= 3.0.0.rc)
43
+ activesupport (3.0.0.rc)
44
+ arel (0.4.0)
45
+ activesupport (>= 3.0.0.beta)
16
46
  bluecloth (2.0.7)
47
+ builder (2.1.2)
48
+ erubis (2.6.6)
49
+ abstract (>= 1.0.0)
50
+ i18n (0.4.1)
17
51
  json (1.4.6)
52
+ mail (2.2.5)
53
+ activesupport (>= 2.3.6)
54
+ mime-types
55
+ treetop (>= 1.4.5)
56
+ mime-types (1.16)
18
57
  mocha (0.9.8)
19
58
  rake
59
+ polyglot (0.3.1)
60
+ rack (1.2.1)
61
+ rack-mount (0.6.9)
62
+ rack (>= 1.0.0)
63
+ rack-test (0.5.4)
64
+ rack (>= 1.0)
65
+ rails (3.0.0.rc)
66
+ actionmailer (= 3.0.0.rc)
67
+ actionpack (= 3.0.0.rc)
68
+ activerecord (= 3.0.0.rc)
69
+ activeresource (= 3.0.0.rc)
70
+ activesupport (= 3.0.0.rc)
71
+ bundler (>= 1.0.0.rc.1)
72
+ railties (= 3.0.0.rc)
73
+ railties (3.0.0.rc)
74
+ actionpack (= 3.0.0.rc)
75
+ activesupport (= 3.0.0.rc)
76
+ rake (>= 0.8.3)
77
+ thor (~> 0.14.0)
20
78
  rake (0.8.7)
21
79
  shoulda (2.11.3)
80
+ thor (0.14.0)
81
+ treetop (1.4.8)
82
+ polyglot (>= 0.3.1)
83
+ tzinfo (0.3.22)
22
84
 
23
85
  PLATFORMS
24
86
  ruby
@@ -28,7 +90,9 @@ DEPENDENCIES
28
90
  bundler (>= 1.0.0.rc.5)
29
91
  json (>= 1.4.6)
30
92
  mocha
93
+ rack
31
94
  radar!
95
+ rails
32
96
  rake
33
97
  shoulda
34
98
  yard!
data/README.md CHANGED
@@ -22,6 +22,15 @@ user all the same questions which could've easily have been gathered automatical
22
22
  Now, Vagrant users can simply send me the radar output and I don't have to
23
23
  ask for any more information 90% of the time.
24
24
 
25
+ ## Brief Feature Breakdown
26
+
27
+ - Reporters allow Radar to report anywhere: a file, a server, email, etc.
28
+ - Data extensions to add additional contextual data to exceptions
29
+ - Matchers to filter exceptions which Radar reports
30
+ - Run multiple Radar "applications" side-by-side to catch and report
31
+ different exceptions to different places
32
+ - Integration with 3rd party software: Rack, Rails 3.
33
+
25
34
  ## Quick Start
26
35
 
27
36
  gem install radar
@@ -29,7 +38,7 @@ ask for any more information 90% of the time.
29
38
  Then just begin logging exceptions in your application:
30
39
 
31
40
  r = Radar::Application.new(:my_application)
32
- r.config.reporters.use Radar::Reporter::FileReporter
41
+ r.reporters.use :file
33
42
  r.report(exception)
34
43
 
35
44
  You can also tell Radar to attach itself to Ruby's `at_exit` hook
@@ -3,7 +3,15 @@
3
3
  ## Overview
4
4
 
5
5
  Radar is a tool which provides a drop-in solution to catching and reporting
6
- errors in your Ruby application via customizable mediums.
6
+ errors in your Ruby application via customizable mediums. A quick feature
7
+ breakdown of Radar:
8
+
9
+ - Reporters allow Radar to report anywhere: a file, a server, email, etc.
10
+ - Data extensions to add additional contextual data to exceptions
11
+ - Matchers to filter exceptions which Radar reports
12
+ - Run multiple Radar "applications" side-by-side to catch and report
13
+ different exceptions to different places
14
+ - Integration with 3rd party software: Rack, Rails 3.
7
15
 
8
16
  ## Installation
9
17
 
@@ -32,7 +40,7 @@ remote server, etc.). Radar comes with some built-in reporters. Below, we config
32
40
  the application to log errors to a file (by default at `~/.radar/errors/my_application`):
33
41
 
34
42
  Radar::Application.new(:my_application) do |app|
35
- app.config.reporters.use Radar::Reporter::FileReporter
43
+ app.reporters.use :file
36
44
  end
37
45
 
38
46
  ### Reporting Errors
@@ -61,6 +69,26 @@ Now, whenever your application is about to crash (an exception not caught by
61
69
  a `rescue`), Radar will catch your exception and report it just prior to
62
70
  crashing.
63
71
 
72
+ ## Terminology
73
+
74
+ Although you've already seen a basic example, I think its a good idea to
75
+ go over terminology quickly before moving onto to the details of every feature:
76
+
77
+ - **application** - A single exception reporter which can contain its own
78
+ set of matchers, reporters, data extensions, etc.
79
+ - **reporter** - A reporter takes Radar-generated exception data when an
80
+ exception is reported and does _something_ with it such as save it to a file,
81
+ store it on a server, etc.
82
+ - **data extension** - Adds additional contextual information to the exception
83
+ event hash before it is sent to the reporters.
84
+ - **matcher** - An exception must adhere to at least one matcher for Radar
85
+ to send the exception to reporters. This allows for filtering of specific
86
+ exceptions.
87
+ - **filter** - A class or proc that filters the data before it is reported.
88
+ This allows you to filter passwords, for example.
89
+ - **integration** - The act of integrating Radar with 3rd party software,
90
+ such as Rack or Rails.
91
+
64
92
  # Features
65
93
 
66
94
  ## Reporters
@@ -80,14 +108,14 @@ of what this means with a few examples:
80
108
  Reporters are enabled using the appilication configuration:
81
109
 
82
110
  Radar::Application.new(:my_application) do |app|
83
- app.config.reporters.use FileReporter
111
+ app.reporters.use :file
84
112
  end
85
113
 
86
114
  And can be configured by passing a block to the reporter, which is yielded with
87
115
  the instance of that reporter:
88
116
 
89
117
  Radar::Application.new(:my_application) do |app|
90
- app.config.reporters.use FileReporter do |reporter|
118
+ app.reporters.use :file do |reporter|
91
119
  reporter.output_directory = "~/.radar/exceptions"
92
120
  end
93
121
  end
@@ -96,10 +124,17 @@ Radar also allows multiple reporters to be used, which are then called
96
124
  in the order they are defined when an exception occurs:
97
125
 
98
126
  Radar::Application.new(:my_application) do |app|
99
- app.config.reporters.use FileReporter
100
- app.config.reporters.use AnotherReporter
127
+ app.reporters.use FileReporter
128
+ app.reporters.use AnotherReporter
101
129
  end
102
130
 
131
+ As you can see from the above examples, a reporter takes both a symbol
132
+ or a class. If a symbol is given, Radar expects the class to be camelcased
133
+ suffixed with `Reporter` and be under the `Radar::Reporter` namespace.
134
+ For example, if you used the symbol `:my_place`, it would expect the reporter
135
+ to be at `Radar::Reporter::MyPlaceReporter`. To avoid this, you can always
136
+ use the class explicitly.
137
+
103
138
  ### Built-in Reporters
104
139
 
105
140
  #### FileReporter
@@ -112,7 +147,7 @@ where `timestamp` is the time that the exception occurred and `uniquehash` is th
112
147
  The directory where these files will be stored is configurable:
113
148
 
114
149
  Radar::Application.new(:my_application) do |app|
115
- app.config.reporters.use Radar::Reporter::FileReporter do |reporter|
150
+ app.reporters.use :file do |reporter|
116
151
  reporter.output_directory = "~/my_application_errors"
117
152
  end
118
153
  end
@@ -133,6 +168,28 @@ A few notes:
133
168
  For complete documentation on this reporter, please see the actual {Radar::Reporter::FileReporter}
134
169
  page.
135
170
 
171
+ #### IoReporter
172
+
173
+ {Radar::Reporter::IoReporter IoReporter} outputs the exception event JSON to
174
+ any IO object (`stdout`, `stderr`, a net stream, etc.).
175
+
176
+ Radar::Application.new(:my_application) do |app|
177
+ app.reporters.use :io, :io_object => STDOUT
178
+ end
179
+
180
+ #### LoggerReporter
181
+
182
+ {Radar::Reporter::LoggerReporter LoggerReporter} outputs the exception event JSON
183
+ to any `Logger` object. This is useful if you want to integrate Radar with an
184
+ existing logging system, or if you simply want a backup for your exceptions (e.g.
185
+ report to both a server and a logger).
186
+
187
+ Radar::Application.new(:my_application) do |app|
188
+ app.reporters.use :logger, :log_object => Logger.new(STDOUT), :log_level => :error
189
+ end
190
+
191
+ `log_level` will default to `:error` if not specified.
192
+
136
193
  ### Custom Reporters
137
194
 
138
195
  It is very easy to write custom reporters. A reporter is simply a class which
@@ -149,7 +206,7 @@ occurred:
149
206
  And then using that reporter is just as easy:
150
207
 
151
208
  Radar::Application.new(:my_application) do |app|
152
- app.config.reporters.use StdoutReporter
209
+ app.reporters.use StdoutReporter
153
210
  end
154
211
 
155
212
  ## Data Extensions
@@ -184,7 +241,7 @@ Data extensions are enabled via the application configuration like most other
184
241
  things:
185
242
 
186
243
  Radar::Application.new(:my_application) do |app|
187
- app.config.data_extensions.use UnameExtension
244
+ app.data_extensions.use UnameExtension
188
245
  end
189
246
 
190
247
  ### Built-In Data Extensions
@@ -211,8 +268,8 @@ really exceptional.
211
268
  Matchers are enabled in the application configuration:
212
269
 
213
270
  Radar::Application.new(:app) do |app|
214
- app.config.match :class, StandardError
215
- app.config.match :backtrace, /file.rb$/
271
+ app.match :class, StandardError
272
+ app.match :backtrace, /file.rb$/
216
273
  end
217
274
 
218
275
  As you can see, multiple matchers may be enabled. In this case, as long as at
@@ -237,9 +294,9 @@ A matcher which matches against the backtrace of the exception. It allows:
237
294
 
238
295
  Examples of each are shown below (respective to the above order):
239
296
 
240
- app.config.match :backtrace, "my_file.rb"
241
- app.config.match :backtrace, /.+_test.rb/
242
- app.config.match :backtrace, /.+_test.rb/, :depth => 5
297
+ app.match :backtrace, "my_file.rb"
298
+ app.match :backtrace, /.+_test.rb/
299
+ app.match :backtrace, /.+_test.rb/, :depth => 5
243
300
 
244
301
  If an exception doesn't have a backtrace (can happen if you don't actually
245
302
  `raise` an exception, but instantiate one) then the matcher always returns
@@ -256,9 +313,9 @@ so it can check against:
256
313
 
257
314
  Examples of each are shown below (in the above order):
258
315
 
259
- app.config.match :class, StandardError
260
- app.config.match :class, StandardError, :include_subclasses => true
261
- app.config.match :class, /.*Error/
316
+ app.match :class, StandardError
317
+ app.match :class, StandardError, :include_subclasses => true
318
+ app.match :class, /.*Error/
262
319
 
263
320
  ### Custom Matchers
264
321
 
@@ -283,7 +340,7 @@ configured message:
283
340
  And the usage is shown below:
284
341
 
285
342
  Radar::Application.new(:app) do |app|
286
- app.config.match ErrorMessageMatcher, "sample message"
343
+ app.match ErrorMessageMatcher, "sample message"
287
344
  end
288
345
 
289
346
  And this results in the following behavior:
@@ -291,3 +348,142 @@ And this results in the following behavior:
291
348
  raise "Hello, World" # not reported
292
349
  raise "sample message" # reported since it matches the message
293
350
 
351
+ ## Filters
352
+
353
+ Filters provide a method of filtering the data hash just before it is sent
354
+ to any reporters. This allows you to filter passwords, modify fields, etc.
355
+
356
+ ### Using a Filter
357
+
358
+ There are two ways to use a filter: as a class or as a lambda.
359
+
360
+ #### Lambda
361
+
362
+ The easiest way, if the filtering is really simple, is to just
363
+ use a lambda. Below is a small example:
364
+
365
+ Radar::Application.new(:my_app) do |app|
366
+ app.filters.use do |data|
367
+ data.delete(:password)
368
+ data
369
+ end
370
+ end
371
+
372
+ This filter would delete the `:password` key from the data hash, and returns
373
+ the new data hash.
374
+
375
+ #### Class
376
+
377
+ You can also create a filtering class if that is more convenient or if
378
+ there is more complex logic in the filtering. The class must respond to
379
+ `filter`.
380
+
381
+ class MyPasswordFilter
382
+ def filter(data)
383
+ data.delete(:password)
384
+ data
385
+ end
386
+ end
387
+
388
+ Radar::Application.new(:my_app) do |app|
389
+ app.filters.use MyPasswordFilter
390
+ end
391
+
392
+ This does the same thing, functionally, as the previous lambda example. However,
393
+ it is clear to see how a class would enable you to more easily encapsulate more
394
+ complex filtering logic.
395
+
396
+ ### Built-in Filters
397
+
398
+ #### KeyFilter
399
+
400
+ The {Radar::Filters::KeyFilter KeyFilter} filters specific keys out of the result
401
+ data and replaces it with specified text ("[FILTERED]" by default). Below we
402
+ configure the key filter to filter passwords:
403
+
404
+ Radar::Application.new(:my_app) do |app|
405
+ app.filters.use :key, :key => :password
406
+ end
407
+
408
+ Then, assuming an exception is raised at some point and the following event data
409
+ is created:
410
+
411
+ { :request => { :password => "foo" },
412
+ :rack_env => { :query => { :password => "foo" } } }
413
+
414
+ Then before it is sent to reporters it will be filtered into this:
415
+
416
+ { :request => { :password => "[FILTERED]" },
417
+ :rack_env => { :query => { :password => "[FILTERED]" } } }
418
+
419
+ There are many options which can be sent to `KeyFilter`, please see the
420
+ class documentation for more details.
421
+
422
+ ## Integration with Other Software
423
+
424
+ ### Rack
425
+
426
+ Radar provides a lightweight Rack middleware to catch and report errors to a
427
+ specified Radar application. Below is a sample `config.ru` file which would
428
+ catch any exceptions by the rack application:
429
+
430
+ require "rubygems"
431
+ require "radar"
432
+
433
+ app = Radar::Application.new(:my_app)
434
+ app.reporters.use :io, :io_object => STDOUT
435
+
436
+ use Rack::Radar, :application => app
437
+ run YourWebApp
438
+
439
+ If `YourWebApp` were to throw any exceptions, `Rack::Radar` would catch it,
440
+ report it to `app`, and reraise the exception.
441
+
442
+ Using the Rack middleware also enables the rack data extension, which provides
443
+ additional information about the rack request and environment. Sample output
444
+ of only the additional information is shown below:
445
+
446
+ { "request": {
447
+ "request_method": "GET",
448
+ "url": "http://localhost:9292/favicon.ico",
449
+ "parameters": {},
450
+ "remote_ip": "127.0.0.1",
451
+ "rack_env": { ... }
452
+ }
453
+ }
454
+
455
+ ### Rails 3
456
+
457
+ Radar can integrate very easily with any Rails 3 application to automatically
458
+ catch any Rails exceptions as well as provide additional information captured
459
+ when the exception was raised. First, add Radar to your `Gemfile`:
460
+
461
+ gem "radar"
462
+
463
+ Then `bundle install` so you pull down the gem. Then install Radar by
464
+ running the built-in generator:
465
+
466
+ rails generate radar
467
+
468
+ This will create the necessary initializer and let you know what further
469
+ steps to take to setup Radar. Radar will already work with your application at this point,
470
+ but it won't report to anywhere by default, so at the very least you must
471
+ open up `config/initializers/radar.rb` and add a reporter.
472
+
473
+ ## Internals Logging
474
+
475
+ Radar provides a lightweight internal log which logs information which
476
+ can be used to easily solve the following problems:
477
+
478
+ * Verifying that Radar is working
479
+ * Investigating why Radar is/isn't reporting certain exceptions
480
+ * Storing more information _in case_ something goes wrong
481
+
482
+ By default, the logger is disabled, but it can easily be enabled on a
483
+ per-application basis by specifying where it should log:
484
+
485
+ Radar::Application.new(:app) do |app|
486
+ app.config.log_location = "/var/log/radar/my_app.log"
487
+ end
488
+
489
+ Multiple applications may be logged to the same place without issue.