radar 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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.