Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' of github.com:airbrake/airbrake

  • Loading branch information...
commit 428f3b39700eef261e2b98f49339dfd3aa72e257 2 parents 0879f46 + 713737b
@shime shime authored
Showing with 1,074 additions and 625 deletions.
  1. +1 −0  .rbenv-version
  2. +121 −0 CHANGELOG
  3. +1 −1  MIT-LICENSE
  4. +9 −386 README.md
  5. +2 −2 README_FOR_HEROKU_ADDON.md
  6. +1 −1  Rakefile
  7. +0 −1  SUPPORTED_RAILS_VERSIONS
  8. +8 −0 TESTING.md
  9. +3 −2 airbrake.gemspec
  10. +12 −0 bin/airbrake
  11. +4 −0 features/rack.feature
  12. +67 −1 features/rails.feature
  13. +46 −4 features/step_definitions/rails_application_steps.rb
  14. +5 −0 features/support/airbrake_shim.rb.template
  15. +1 −1  features/support/rake/Rakefile
  16. +1 −1  generators/airbrake/lib/rake_commands.rb
  17. +1 −1  generators/airbrake/templates/airbrake_tasks.rake
  18. +14 −10 lib/airbrake.rb
  19. +8 −0 lib/airbrake/backtrace.rb
  20. +1 −1  lib/airbrake/capistrano.rb
  21. +68 −0 lib/airbrake/cli/client.rb
  22. +41 −0 lib/airbrake/cli/options.rb
  23. +30 −0 lib/airbrake/cli/printer.rb
  24. +17 −0 lib/airbrake/cli/project.rb
  25. +36 −0 lib/airbrake/cli/project_factory.rb
  26. +48 −0 lib/airbrake/cli/runner.rb
  27. +8 −0 lib/airbrake/cli/validator.rb
  28. +41 −11 lib/airbrake/configuration.rb
  29. +73 −0 lib/airbrake/extensions/blank.rb
  30. +51 −9 lib/airbrake/notice.rb
  31. +0 −1  lib/airbrake/rack.rb
  32. +20 −7 lib/airbrake/rails/controller_methods.rb
  33. +0 −1  lib/airbrake/rails/javascript_notifier.rb
  34. +10 −6 lib/airbrake/rails/middleware/exceptions_catcher.rb
  35. +15 −2 lib/airbrake/rails3_tasks.rb
  36. +1 −1  lib/airbrake/railtie.rb
  37. +10 −5 lib/airbrake/rake_handler.rb
  38. +35 −23 lib/airbrake/sender.rb
  39. +12 −1 lib/airbrake/shared_tasks.rb
  40. +1 −1  lib/airbrake/version.rb
  41. +0 −1  lib/airbrake_tasks.rb
  42. +11 −1 test/{airbrake_2_2.xsd → airbrake_2_3.xsd}
  43. +3 −3 test/airbrake_tasks_test.rb
  44. +2 −2 test/backtrace_test.rb
  45. +6 −6 test/capistrano_test.rb
  46. +2 −2 test/catcher_test.rb
  47. +20 −8 test/configuration_test.rb
  48. +14 −13 test/helper.rb
  49. +1 −2  test/javascript_notifier_test.rb
  50. +8 −2 test/logger_test.rb
  51. +98 −76 test/notice_test.rb
  52. +34 −4 test/notifier_test.rb
  53. +1 −1  test/rack_test.rb
  54. +1 −1  test/rails_initializer_test.rb
  55. +1 −1  test/recursion_test.rb
  56. +48 −21 test/sender_test.rb
  57. +1 −1  test/user_informer_test.rb
View
1  .rbenv-version
@@ -0,0 +1 @@
+1.9.3-p194
View
121 CHANGELOG
@@ -1,3 +1,119 @@
+Version 3.1.6 - 2012-10-23 21:15:50 +0200
+===============================================================================
+
+Hrvoje Šimić (9):
+ load api key from file and env if not provided for executable
+ get a list of your projects from command line
+ create projects from command line
+ fix cli client host
+ creating deploys from command line
+ don't override extension methods
+ update heroku plan in readme
+ another fix for heroku readme
+ don't pollute global namespace with blank?
+
+Sam Umbach (1):
+ Send deploy notification on deploy:cold
+
+
+Version 3.1.5 - 2012-10-05 17:32:23 +0200
+===============================================================================
+
+Hrvoje Šimić (3):
+ make collected user attributes customizable
+ enable filtering of env vars for rake handler
+ add a simple executable
+
+Morgan Mikel McDaris (1):
+ rake section added
+
+Zachary Anker (2):
+ Add the ability to pass XML directly to send_to_airbrake
+ Remove active_support require since it's no longer necessary
+
+
+Version 3.1.4 - 2012-09-11 04:31:51 +0200
+===============================================================================
+
+Ben Arent (1):
+ Update to MIT license to reflect new ownership.
+
+Hrvoje Šimić (4):
+ remove activesupport from runtime dependencies
+ use different logger for async notices
+ Merge pull request #118 from exviva/load_initializer_conditionally
+ symbolize user's keys
+
+Olek Janiszewski (1):
+ Fallback to loading whole environment if initializer does not exist
+
+
+Version 3.1.3 - 2012-09-05 18:58:27 +0200
+===============================================================================
+
+Ben Arent (1):
+ Removed old mailing list.
+
+Dylan Smith (1):
+ Use debug log level for verbose log messages.
+
+Hrvoje Šimić (21):
+ add a note for testing against different frameworks
+ add a scenario to make sure 404s are ignored by default
+ failing scenario for #100
+ remove unnecessary bundler/setup requirement
+ move dependencies from Gemfile to gemspec
+ send info about the current logged in user
+ add async notifications
+ use async? instead of asnyc
+ add info about logger
+ prefer provided error message over original exception message
+ don't load entire environment for deploy task
+ safely handle render exception
+ remove unnecessary AirbrakeError
+ update XML schema
+ test fixes
+ clean whitespace
+ print the failed notice details
+ update supported rails versions
+ update scenarios
+ stop converting notice to xml before passing it to sender
+ remove logger from rack handler
+ update supported rails versions
+
+John Pignata (1):
+ Cherry-pick Object#blank? extension
+
+Joshua Wood (2):
+ rbenv support
+ Fixes outdated rake feature.
+
+Matt Colyer (1):
+ Fix Airbrake so it actually reports data.
+
+Morgan Mikel McDaris (1):
+ changed notify to notify_or_ignore
+
+Sergii Boiko (2):
+ Refactor config.async to provide custom asynchronous delivery way
+ Simplify config.async to use only notice param
+
+grosser (2):
+ fix exceeded available parameter key space breaks notification, fixes #99
+ make all tests runnable via ruby test_file.rb by loading the helper absolutely
+
+
+Version 3.1.2 - 2012-06-23 02:27:01 +0200
+===============================================================================
+
+Hrvoje Šimić (5):
+ routing errors ignored by default, for #92
+ use simplecov for test coverage
+ fix features for javascript
+ ignore user agents in rack, closes #61
+ error out if controller is undefined
+
+
Version 3.1.1 - 2012-06-06 20:35:00 -0700
===============================================================================
@@ -821,3 +937,8 @@ Nick Quaranto (3):
+
+
+
+
+
View
2  MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2007, Tammer Saleh, Thoughtbot, Inc.
+Copyright (c) 2007 - 2012, Exceptional DBA Airbrake.io
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
View
395 README.md
@@ -11,9 +11,7 @@ Help
For help with using Airbrake and this notifier visit [our support site](http://help.airbrake.io).
-For discussion of Airbrake development check out the [mailing list](http://groups.google.com/group/hoptoad-notifier-dev).
-
-For SSL verification see the [Resources](https://github.com/airbrake/airbrake/blob/master/resources/README.md).
+For **SSL** verification see the [Resources](https://github.com/airbrake/airbrake/blob/master/resources/README.md).
Rails Installation
------------------
@@ -54,406 +52,31 @@ every server you deploy to has the gem installed or your application won't start
The generator creates a file under `config/initializers/airbrake.rb` configuring Airbrake with your API key. This file should be checked into your version control system so that it is deployed to your staging and production environments.
-### Upgrading From Earlier Versions of Airbrake
-
-If you're currently using the plugin version (if you have a
-vendor/plugins/hoptoad_notifier directory, you are), you'll need to perform a
-few extra steps when upgrading to the gem version.
-
-Add the airbrake gem to your app. In config/environment.rb:
-
- config.gem 'airbrake'
-
-Remove the plugin:
-
- rm -rf vendor/plugins/hoptoad_notifier
-
-Make sure the following line DOES NOT appear in your ApplicationController file:
-
- include HoptoadNotifier::Catcher
-
-If it does, remove it. The new catcher is automatically included by the gem
-version of Airbrake.
-
-Before running the airbrake generator, you need to find your project's API key.
-Log in to your account at airbrake.io, and click on the "Projects" button.
-Then, find your project in the list, and click on its name. In the left-hand
-column, you'll see an "Edit this project" button. Click on that to get your
-project's API key. If you accidentally use your personal API auth_token,
-you will get API key not found errors, and exceptions will not be stored
-by the Airbrake service.
-
-Then from your project's RAILS_ROOT, run:
-
- rake gems:install
- script/generate airbrake --api-key your_key_here
-
-Once installed, you should vendor the airbrake gem.
-
- rake gems:unpack GEM=airbrake
-
-As always, if you choose not to vendor the airbrake gem, make sure
-every server you deploy to has the gem installed or your application won't
-start.
-
-### Upgrading from Earlier Versions of the Hoptoad Gem (with config.gem)
-
-If you're currently using the gem version of the hoptoad_notifier and have
-a version of Rails that uses config.gem (in the 2.x series), there is
-a step or two that you need to do to upgrade. First, you need to remove
-the old version of the gem from vendor/gems:
-
- rm -rf vendor/gems/hoptoad_notifier-X.X.X
-
-Then you must remove the hoptoad_notifier_tasks.rake file from lib:
-
- rm lib/tasks/hoptoad_notifier_tasks.rake
-
-You can then continue to install normally. If you don't remove the rake file,
-you will be unable to unpack this gem (Rails will think it's part of the
-framework).
-
-
-
-You can test that Airbrake is working in your production environment by using
-this rake task (from RAILS_ROOT):
-
- rake airbrake:test
-
-If everything is configured properly, that task will send a notice to Airbrake
-which will be visible immediately.
-
-### Removing hoptoad_notifier
-
-in your ApplicationController, REMOVE this line:
-
- include HoptoadNotifiable
-
-In your config/environment* files, remove all references to HoptoadNotifier
-
-Remove the vendor/plugins/hoptoad_notifier directory.
-
-### Remove hoptoad_notifier plugin
-
-Remove the vendor/plugins/hoptoad_notifier directory before installing the gem, or run:
-
- script/plugin remove hoptoad_notifier
-
-Non-rails apps using Bundler
-----------------------------
-There is an undocumented dependency in `activesupport` where the `i18n` gem is
-required only if the core classes extensions are used (`active_support/core_ext`).
-This can lead to a confusing `LoadError` exception when using Airbrake. Until
-this is fixed in `activesupport` the workaround is to add `i18n` to the Gemfile
-for your Sinatra/Rack/pure ruby application:
-
- gem 'i18n'
- gem 'airbrake'
-
-Rack
-----
-
-In order to use airbrake in a non-Rails rack app, just load
-airbrake, configure your API key, and use the Airbrake::Rack
-middleware:
-
- require 'rack'
- require 'airbrake'
-
- Airbrake.configure do |config|
- config.api_key = 'my_api_key'
- end
-
- app = Rack::Builder.app do
- run lambda { |env| raise "Rack down" }
- end
-
- use Airbrake::Rack
- run app
-
-Sinatra
--------
-
-Using airbrake in a Sinatra app is just like a Rack app:
-
- require 'sinatra'
- require 'airbrake'
-
- Airbrake.configure do |config|
- config.api_key = 'my api key'
- end
-
- use Airbrake::Rack
-
- get '/' do
- raise "Sinatra has left the building"
- end
-
-Usage
------
-
-For the most part, Airbrake works for itself.
-
-It intercepts the exception middleware calls, sends notifications and continues the middleware call chain.
-
-If you want to log arbitrary things which you've rescued yourself from a
-controller, you can do something like this:
-
- ...
- rescue => ex
- notify_airbrake(ex)
- flash[:failure] = 'Encryptions could not be rerouted, try again.'
- end
- ...
-
-The `#notify_airbrake` call will send the notice over to Airbrake for later
-analysis. While in your controllers you use the `notify_airbrake` method, anywhere
-else in your code, use `Airbrake.notify`.
-
-To perform custom error processing after Airbrake has been notified, define the
-instance method `#rescue_action_in_public_without_airbrake(exception)` in your
-controller.
-
-Informing the User
-------------------
-
-The airbrake gem is capable of telling the user information about the error that just happened
-via the user_information option. They can give this error number in bug reports, for example.
-By default, if your 500.html contains the text
-
- <!-- AIRBRAKE ERROR -->
-
-then that comment will be replaced with the text "Airbrake Error [errnum]". You can modify the text
-of the informer by setting `config.user_information`. Airbrake will replace "{{ error_id }}" with the
-ID of the error that is returned from Airbrake.
-
- Airbrake.configure do |config|
- ...
- config.user_information = "<p>Tell the devs that it was <strong>{{ error_id }}</strong>'s fault.</p>"
- end
-
-You can also turn the middleware that handles this completely off by setting `config.user_information` to false.
-
-Note that this feature is reading the error id from `env['airbrake.error_id']`. When the exception is caught
-automatically in a controller, Airbrake sets that value. If you're, however, calling the Airbrake methods like
-`Airbrake#notify` or `Airbrake#notify_or_ignore`, please make sure you set that value. So the proper way of calling the
-"manual" methods would be `env['airbrake.error_id'] = Airbrake.notify_or_ignore(...)`.
-
-Tracking deployments in Airbrake
---------------------------------
-
-Paying Airbrake plans support the ability to track deployments of your application in Airbrake.
-By notifying Airbrake of your application deployments, all errors are resolved when a deploy occurs,
-so that you'll be notified again about any errors that reoccur after a deployment.
-
-Additionally, it's possible to review the errors in Airbrake that occurred before and after a deploy.
-
-When Airbrake is installed as a gem, you need to add
-
- require 'airbrake/capistrano'
-
-to your deploy.rb
-
-If you don't use Capistrano, then you can use the following rake task from your
-deployment process to notify Airbrake:
-
- rake airbrake:deploy TO=#{rails_env} REVISION=#{current_revision} REPO=#{repository} USER=#{local_user}
-
-Going beyond exceptions
------------------------
-
-You can also pass a hash to `Airbrake.notify` method and store whatever you want,
-not just an exception. And you can also use it anywhere, not just in
-controllers:
-
- begin
- params = {
- # params that you pass to a method that can throw an exception
- }
- my_unpredicable_method(params)
- rescue => e
- Airbrake.notify(
- :error_class => "Special Error",
- :error_message => "Special Error: #{e.message}",
- :parameters => params
- )
- end
-
-While in your controllers you use the `notify_airbrake` method, anywhere else in
-your code, use `Airbrake.notify`. Airbrake will get all the information
-about the error itself. As for a hash, these are the keys you should pass:
-
-* `:error_class` - Use this to group similar errors together. When Airbrake catches an exception it sends the class name of that exception object.
-* `:error_message` - This is the title of the error you see in the errors list. For exceptions it is "#{exception.class.name}: #{exception.message}"
-* `:parameters` - While there are several ways to send additional data to Airbrake, passing a Hash as :parameters as in the example above is the most common use case. When Airbrake catches an exception in a controller, the actual HTTP client request parameters are sent using this key.
-
-Airbrake merges the hash you pass with these default options:
-
- {
- :api_key => Airbrake.api_key,
- :error_message => 'Notification',
- :backtrace => caller,
- :parameters => {},
- :session => {}
- }
-
-You can override any of those parameters.
-
-### Sending shell environment variables when "Going beyond exceptions"
-
-One common request we see is to send shell environment variables along with
-manual exception notification. We recommend sending them along with CGI data
-or Rack environment (:cgi_data or :rack_env keys, respectively.)
-
-See Airbrake::Notice#initialize in lib/airbrake/notice.rb for
-more details.
-
-Filtering
----------
-
-You can specify a whitelist of errors that Airbrake will not report on. Use
-this feature when you are so apathetic to certain errors that you don't want
-them even logged.
-
-This filter will only be applied to automatic notifications, not manual
-notifications (when #notify is called directly).
-
-Airbrake ignores the following exceptions by default:
-
- ActiveRecord::RecordNotFound
- ActionController::RoutingError
- ActionController::InvalidAuthenticityToken
- CGI::Session::CookieStore::TamperedWithCookie
- ActionController::UnknownAction
- AbstractController::ActionNotFound
- Mongoid::Errors::DocumentNotFound
-
-
-To ignore errors in addition to those, specify their names in your Airbrake
-configuration block.
-
- Airbrake.configure do |config|
- config.api_key = '1234567890abcdef'
- config.ignore << "ActiveRecord::IgnoreThisError"
- end
-
-To ignore *only* certain errors (and override the defaults), use the #ignore_only attribute.
-
- Airbrake.configure do |config|
- config.api_key = '1234567890abcdef'
- config.ignore_only = ["ActiveRecord::IgnoreThisError"] # or [] to ignore no exceptions.
- end
-
-To ignore certain user agents, add in the #ignore_user_agent attribute as a
-string or regexp:
-
- Airbrake.configure do |config|
- config.api_key = '1234567890abcdef'
- config.ignore_user_agent << /Ignored/
- config.ignore_user_agent << 'IgnoredUserAgent'
- end
-
-To ignore exceptions based on other conditions, use #ignore_by_filter:
-
- Airbrake.configure do |config|
- config.api_key = '1234567890abcdef'
- config.ignore_by_filter do |exception_data|
- true if exception_data[:error_class] == "RuntimeError"
- end
- end
-
-To replace sensitive information sent to the Airbrake service with [FILTERED] use #params_filters:
-
- Airbrake.configure do |config|
- config.api_key = '1234567890abcdef'
- config.params_filters << "credit_card_number"
- end
-
-Note that, when rescuing exceptions within an ActionController method,
-airbrake will reuse filters specified by #filter_parameter_logging.
-
-Testing
--------
-
-When you run your tests, you might notice that the Airbrake service is recording
-notices generated using #notify when you don't expect it to. You can
-use code like this in your test_helper.rb or spec_helper.rb files to redefine
-that method so those errors are not reported while running tests.
-
- module Airbrake
- def self.notify(exception, opts = {})
- # do nothing.
- end
- end
-
-Proxy Support
--------------
-
-The notifier supports using a proxy, if your server is not able to directly reach the Airbrake servers. To configure the proxy settings, added the following information to your Airbrake configuration block.
-
- Airbrake.configure do |config|
- config.proxy_host = proxy.host.com
- config.proxy_port = 4038
- config.proxy_user = foo # optional
- config.proxy_pass = bar # optional
Supported Rails versions
------------------------
-See SUPPORTED_RAILS_VERSIONS for a list of official supported versions of
+See **SUPPORTED_RAILS_VERSIONS** for a list of official supported versions of
Rails.
-Please open up a support ticket ( http://help.airbrake.io ) or submit a new github issue
-if you're using a version of Rails that is listed above and the notifier is
-not working properly.
-
-Javascript Notifer
-------------------
-
-To automatically include the Javascript node on every page, use this helper method from your layouts:
-
- <%= airbrake_javascript_notifier %>
-
-It's important to insert this very high in the markup, above all other javascript. Example:
-
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf8">
- <%= airbrake_javascript_notifier %>
- <!-- more javascript -->
- </head>
- <body>
- ...
- </body>
- </html>
-
-This helper will automatically use the API key, host, and port specified in the configuration.
-
-The Javascript notifier tends to send much more notifications than the base Rails project.
-If you want to receive them into a separate Airbrake project, specify its
-API key in the `js_api_key` option.
-
- config.js_api_key = 'another-projects-api-key'
-
-To test the Javascript notifier in development environment, overwrite (temporarily) the development_environments option:
+Airbrake wiki pages
+------------------------
+Our wiki contains a lot of additional information about Airbrake configuration. Please browse the wiki when finished reading this
+README:
- Airbrake.configure do |config|
- # ...
- config.development_environments = []
- end
+https://github.com/airbrake/airbrake/wiki
Development
-----------
-See TESTING.md for instructions on how to run the tests.
+See `TESTING.md` for instructions on how to run the tests.
Credits
-------
![thoughtbot](http://thoughtbot.com/images/tm/logo.png)
-Airbrake is maintained and funded by [airbrake.io](http://airbrake.io)
+Airbrake is maintained and funded by [airbrake.io](http://airbrake.io).
Thank you to all [the contributors](https://github.com/airbrake/airbrake/contributors)!
View
4 README_FOR_HEROKU_ADDON.md
@@ -7,8 +7,8 @@ Send your application errors to our hosted service and reclaim your inbox.
----------------------------
To use Airbrake on Heroku, install the Airbrake add-on:
- $ heroku addons:add airbrake:basic # This adds the the basic plan.
- # If you'd like another plan, specify that instead.
+ $ heroku addons:add airbrake:developer # If you'd like another plan, specify that instead.
+ # Check https://addons.heroku.com/airbrake for a full list of plans.
2. Including the Airbrake notifier in your application
--------------------------------------------------
View
2  Rakefile
@@ -12,7 +12,7 @@ rescue LoadError
end
require './lib/airbrake/version'
-FEATURES = ["sinatra","rack","metal","user_informer"]
+FEATURES = ["sinatra","rack","metal","user_informer","rake"]
desc 'Default: run unit tests.'
task :default do
View
1  SUPPORTED_RAILS_VERSIONS
@@ -1 +0,0 @@
-3.0.0
View
8 TESTING.md
@@ -22,6 +22,14 @@ prefer to run it against specific version hit
rake cucumber:rails:<VERSION>
+You can also specify to test only against the certain frameworks we
+support (rack, rake and sinatra) by executing
+
+ rake cucumber:<FRAMEWORK>
+
+This tasks are not printed out with `rake -T` since they're added
+dynamically in the Rakefile.
+
For Maintainers
================
View
5 airbrake.gemspec
@@ -8,12 +8,13 @@ Gem::Specification.new do |s|
s.summary = %q{Send your application errors to our hosted service and reclaim your inbox.}
s.require_paths = ["lib"]
+ s.executables << "airbrake"
s.files = Dir["{generators/**/*,lib/**/*,rails/**/*,resources/*,script/*}"] +
%w(airbrake.gemspec CHANGELOG Gemfile Guardfile INSTALL MIT-LICENSE Rakefile README_FOR_HEROKU_ADDON.md README.md TESTING.md SUPPORTED_RAILS_VERSIONS install.rb)
s.test_files = Dir.glob("{test,spec,features}/**/*")
s.add_runtime_dependency("builder")
- s.add_runtime_dependency("activesupport")
+ s.add_runtime_dependency("girl_friday")
s.add_development_dependency("bundler")
s.add_development_dependency("bourne", ">= 1.0")
@@ -22,7 +23,7 @@ Gem::Specification.new do |s|
s.add_development_dependency("nokogiri", "~> 1.5.0")
s.add_development_dependency("rspec", "~> 2.6.0")
s.add_development_dependency("sham_rack", "~> 1.3.0")
- s.add_development_dependency("shoulda", "~> 2.11.3")
+ s.add_development_dependency("shoulda", "~> 2.11.3")
s.add_development_dependency("capistrano", "~> 2.8.0")
s.add_development_dependency("aruba")
s.add_development_dependency("appraisal")
View
12 bin/airbrake
@@ -0,0 +1,12 @@
+#!/usr/bin/env ruby
+
+require "airbrake"
+require "airbrake/cli/runner"
+require "net/http"
+require "uri"
+
+args = ARGV.dup
+command = args.shift.strip rescue nil
+options = ARGV[1..-1]
+
+Runner.run!(command, options)
View
4 features/rack.feature
@@ -3,11 +3,13 @@ Feature: Use the notifier in a plain Rack app
Scenario: Rescue and exception in a Rack app
Given the following Rack app:
"""
+ require 'logger'
require 'rack'
require 'airbrake'
Airbrake.configure do |config|
config.api_key = 'my_api_key'
+ config.logger = Logger.new STDOUT
end
app = Rack::Builder.app do
@@ -21,12 +23,14 @@ Feature: Use the notifier in a plain Rack app
Scenario: Ignore user agents
Given the following Rack app:
"""
+ require 'logger'
require 'rack'
require 'airbrake'
Airbrake.configure do |config|
config.api_key = 'my_api_key'
config.ignore_user_agent << /ignore/
+ config.logger = Logger.new STDOUT
end
class Mock
View
68 features/rails.feature
@@ -85,6 +85,7 @@ Feature: Install the Gem in a Rails application
And I route "/test/index" to "test#index"
And I perform a request to "http://example.com:123/test/index?param=value"
Then I should receive a Airbrake notification
+ Then I should see "test"
Scenario: The gem should not be considered a framework gem
When I configure the Airbrake shim
@@ -139,6 +140,8 @@ Feature: Install the Gem in a Rails application
And I route "/test/index" to "test#index"
And I perform a request to "http://example.com:123/test/index?param=value"
Then I should receive a Airbrake notification
+ Then I should not see "red23"
+ And I should see "FILTERED"
Scenario: Filtering session in a controller
When I configure the Airbrake shim
@@ -156,6 +159,8 @@ Feature: Install the Gem in a Rails application
And I route "/test/index" to "test#index"
And I perform a request to "http://example.com:123/test/index?param=value"
Then I should receive a Airbrake notification
+ Then I should not see "blue42"
+ And I should see "FILTERED"
Scenario: Filtering session and params based on Rails parameter filters
When I configure the Airbrake shim
@@ -170,6 +175,9 @@ Feature: Install the Gem in a Rails application
And I route "/test/index" to "test#index"
And I perform a request to "http://example.com:123/test/index?param=value"
Then I should receive a Airbrake notification
+ And I should not see "red23"
+ And I should not see "blue42"
+ And I should see "FILTERED"
Scenario: Notify airbrake within the controller
When I configure the Airbrake shim
@@ -183,15 +191,73 @@ Feature: Install the Gem in a Rails application
And I route "/test/index" to "test#index"
And I perform a request to "http://example.com:123/test/index?param=value"
Then I should receive a Airbrake notification
+ And I should see "test"
- Scenario: Reporting 404s
+ Scenario: Reporting 404s should be disabled by default
When I configure the Airbrake shim
And I run the airbrake generator with "-k myapikey"
And I configure the notifier to use the following configuration lines:
"""
config.ignore_only = []
"""
+ And I configure usage of Airbrake
+ And I perform a request to "http://example.com:123/this/route/does/not/exist"
+ Then I should see "The page you were looking for doesn't exist."
+ And I should not receive a Airbrake notification
+
+ Scenario: Reporting 404s should work when configured properly
+ When I configure the Airbrake shim
+ And I configure usage of Airbrake
+ When I configure the notifier to use the following configuration lines:
+ """
+ config.ignore_only = []
+ """
And I perform a request to "http://example.com:123/this/route/does/not/exist"
Then I should see "404"
And I should see "Not Found"
And I should receive a Airbrake notification
+
+ Scenario: reporting over SSL with utf8 check should work
+ Given PENDING I fix this one
+ When I configure the Airbrake shim
+ And I configure usage of Airbrake
+ When I configure the notifier to use the following configuration lines:
+ """
+ config.secure = true
+ """
+ And I define a response for "TestController#index":
+ """
+ raise RuntimeError, "some message"
+ """
+ And I route "/test/index" to "test#index"
+ And I perform a request to "http://example.com:123/test/index?utf8=✓"
+ Then I should receive a Airbrake notification
+
+ Scenario: It should also send the user details
+ When I configure the Airbrake shim
+ And I configure usage of Airbrake
+ And I define a response for "TestController#index":
+ """
+ raise RuntimeError, "some message"
+ """
+ And I route "/test/index" to "test#index"
+ And I have set up authentication system in my app that uses "current_user"
+ And I perform a request to "http://example.com:123/test/index"
+ Then I should receive a Airbrake notification
+ And the Airbrake notification should contain user details
+ When I have set up authentication system in my app that uses "current_member"
+ And I perform a request to "http://example.com:123/test/index"
+ Then I should receive a Airbrake notification
+ And the Airbrake notification should contain user details
+
+ Scenario: It should log the notice when failure happens
+ When Airbrake server is not responding
+ And I configure usage of Airbrake
+ And I define a response for "TestController#index":
+ """
+ raise RuntimeError, "some message"
+ """
+ And I route "/test/index" to "test#index"
+ And I perform a request to "http://example.com:123/test/index?param=value"
+ Then I should see "Notice details:"
+ And I should see "some message"
View
50 features/step_definitions/rails_application_steps.rb
@@ -7,6 +7,24 @@
#@terminal.build_and_install_gem(File.join(PROJECT_ROOT, "#{gem_name}.gemspec"))
#end
+Given /^PENDING/ do
+ pending
+end
+
+Given /^Airbrake server is not responding$/ do
+ bundle_gem("sham_rack")
+ content = <<-CONTENT
+ require 'sham_rack'
+
+ Airbrake.configuration.logger = Logger.new STDOUT
+
+ ShamRack.at("api.airbrake.io") {["500", { "Content-type" => "text/xml" }, ["Internal server error"]]}
+
+ CONTENT
+ target = File.join(rails_root, 'config', 'initializers', 'airbrake_shim.rb')
+ File.open(target,"w") { |f| f.write content }
+end
+
When /^I generate a new Rails application$/ do
rails3 = version_string =~ /^3/
@@ -17,9 +35,6 @@
rails_create_command = ''
end
-
- #@terminal.run(%{BUNDLE_GEMFILE=#{File.join(PROJECT_ROOT,"gemfiles",version_string.strip + ".gemfile")} bundle exec rails #{rails_create_command} rails_root})
-
step %{I run `bundle exec rails #{rails_create_command} rails_root --skip-bundle`}
if rails_root_exists?
@@ -99,7 +114,6 @@
if bundler_manages_gems?
append_to_gemfile("gem 'sham_rack'")
end
-
shim_file = File.join(PROJECT_ROOT, 'features', 'support', 'airbrake_shim.rb.template')
if rails_supports_initializers?
target = File.join(rails_root, 'config', 'initializers', 'airbrake_shim.rb')
@@ -110,6 +124,8 @@
file.write IO.read(shim_file)
end
end
+ target = File.join(rails_root, 'config', 'initializers', 'airbrake_shim.rb')
+ FileUtils.cp(shim_file, target)
end
When /^I configure the notifier to use "([^\"]*)" as an API key$/ do |api_key|
@@ -387,3 +403,29 @@ def rails_non_initializer_airbrake_config_file
step %{I configure my application to require the "airbrake" gem}
step %{I run the airbrake generator with "-k myapikey"}
end
+
+
+When /^I have set up authentication system in my app that uses "([^\"]*)"$/ do |current_user|
+ application_controller = File.join(rails_root, 'app', 'controllers', "application_controller.rb")
+ definition =
+ """
+ class ApplicationController < ActionController::Base
+ def consider_all_requests_local; false; end
+ def local_request?; false; end
+
+ # this is the ultimate authentication system, devise is history
+ def #{current_user}
+ Struct.new(:attributes).new({:id => 1,:name => 'Bender',:email => 'bender@beer.com',:username => 'b3nd0r'})
+ end
+ end
+ """
+ File.open(application_controller, "w") {|file| file.puts definition }
+end
+
+Then /^the Airbrake notification should contain user details$/ do
+ Then %{I should see "Bender"}
+ And %{I should see "bender@beer.com"}
+ And %{I should see "<id>1</id>"}
+ And %{I should see "b3nd0r"}
+end
+
View
5 features/support/airbrake_shim.rb.template
@@ -1,11 +1,16 @@
require 'sham_rack'
+Airbrake.configuration.logger = Logger.new STDOUT if defined?(Airbrake)
+
ShamRack.at("api.airbrake.io") do |env|
response = <<-end_xml
<notice>
<id>b6817316-9c45-ed26-45eb-780dbb86aadb</id>
<url>http://airbrake.io/locate/b6817316-9c45-ed26-45eb-780dbb86aadb</url>
</notice>
+
+Request:
+#{env["rack.input"].read}
end_xml
["200 OK", { "Content-type" => "text/xml" }, [response]]
end
View
2  features/support/rake/Rakefile
@@ -44,7 +44,7 @@ task :airbrake_not_yet_configured do
end
module Airbrake
- def self.notify(*args)
+ def self.notify_or_ignore(*args)
# TODO if you need to check more params, you'll have to use json.dump or something
$stderr.puts "airbrake #{args[1][:component]}"
end
View
2  generators/airbrake/lib/rake_commands.rb
@@ -2,7 +2,7 @@
def rake(cmd, opts = {})
logger.rake "rake #{cmd}"
unless system("rake #{cmd}")
- logger.rake "#{cmd} failed. Rolling back"
+ logger.rake "#{cmd} failed. Rolling back"
command(:destroy).invoke!
end
end
View
2  generators/airbrake/templates/airbrake_tasks.rake
@@ -1,7 +1,7 @@
# Don't load anything when running the gems:* tasks.
# Otherwise, airbrake will be considered a framework gem.
# https://thoughtbot.lighthouseapp.com/projects/14221/tickets/629
-unless ARGV.any? {|a| a =~ /^gems/}
+unless ARGV.any? {|a| a =~ /^gems/}
Dir[File.join(Rails.root, 'vendor', 'gems', 'airbrake-*')].each do |vendored_notifier|
$: << File.join(vendored_notifier, 'lib')
View
24 lib/airbrake.rb
@@ -1,13 +1,8 @@
+require "girl_friday"
require 'net/http'
require 'net/https'
require 'rubygems'
-begin
- require 'active_support'
- require 'active_support/core_ext'
-rescue LoadError
- require 'activesupport'
- require 'activesupport/core_ext'
-end
+require 'airbrake/extensions/blank'
require 'airbrake/version'
require 'airbrake/configuration'
require 'airbrake/notice'
@@ -19,7 +14,7 @@
require 'airbrake/railtie' if defined?(Rails::Railtie)
module Airbrake
- API_VERSION = "2.2"
+ API_VERSION = "2.3"
LOG_PREFIX = "** [Airbrake] "
HEADERS = {
@@ -51,6 +46,11 @@ def report_response_body(response)
write_verbose_log("Response from Airbrake: \n#{response}")
end
+ # Prints out the details about the notice that wasn't sent to server
+ def report_notice(notice)
+ write_verbose_log("Notice details: \n#{notice}")
+ end
+
# Returns the Ruby version, Rails version, and current Rails environment
def environment_info
info = "[Ruby: #{RUBY_VERSION}]"
@@ -60,7 +60,7 @@ def environment_info
# Writes out the given message to the #logger
def write_verbose_log(message)
- logger.info LOG_PREFIX + message if logger
+ logger.debug LOG_PREFIX + message if logger
end
# Look for the Rails logger currently defined
@@ -131,7 +131,11 @@ def build_lookup_hash_for(exception, options = {})
def send_notice(notice)
if configuration.public?
- sender.send_to_airbrake(notice.to_xml)
+ if configuration.async?
+ configuration.async.call(notice)
+ else
+ sender.send_to_airbrake(notice)
+ end
end
end
View
8 lib/airbrake/backtrace.rb
@@ -77,6 +77,14 @@ def inspect
"<Backtrace: " + lines.collect { |line| line.inspect }.join(", ") + ">"
end
+ def to_s
+ content = []
+ lines.each do |line|
+ content << line
+ end
+ content.join("\n")
+ end
+
def ==(other)
if other.respond_to?(:lines)
lines == other.lines
View
2  lib/airbrake/capistrano.rb
@@ -7,6 +7,7 @@ def self.load_into(configuration)
configuration.load do
after "deploy", "airbrake:deploy"
after "deploy:migrations", "airbrake:deploy"
+ after "deploy:cold", "airbrake:deploy"
namespace :airbrake do
desc <<-DESC
@@ -41,4 +42,3 @@ def self.load_into(configuration)
if Capistrano::Configuration.instance
Airbrake::Capistrano.load_into(Capistrano::Configuration.instance)
end
-
View
68 lib/airbrake/cli/client.rb
@@ -0,0 +1,68 @@
+require File.expand_path( "../runner", __FILE__)
+
+module Client
+ extend self
+
+ def options
+ Runner.options
+ end
+
+ def fetch_projects
+ uri = URI.parse "http://#{options.account}.airbrake.io"\
+ "/data_api/v1/projects.xml?auth_token=#{options.auth_token}"
+ http = Net::HTTP.new(uri.host,uri.port)
+ request = Net::HTTP::Get.new(uri.request_uri)
+ response = http.request(request)
+ response.body
+ end
+
+ def create_project
+ uri = URI.parse "http://#{options.account}.airbrake.io"\
+ "/data_api/v1/projects.xml"
+ http = Net::HTTP.new(uri.host,uri.port)
+ request = Net::HTTP::Post.new(uri.request_uri)
+ request.set_form_data('project[name]' => options.name,'auth_token' => options.auth_token)
+ response = http.request(request)
+ response.body
+
+ print_project_response(response.body)
+ end
+
+ def create_deploy
+ uri = URI.parse "http://airbrake.io"\
+ "/projects/1/deploys.xml"
+ http = Net::HTTP.new(uri.host,uri.port)
+ request = Net::HTTP::Post.new(uri.request_uri)
+ request.set_form_data('deploy[rails_env]' => options.rails_env,"api_key" => options.api_key)
+ response = http.request(request)
+ puts response.body
+ end
+
+ def print_projects
+ factory = ProjectFactory.new
+ projects = fetch_projects
+ factory.create_projects_from_xml(projects)
+ abort "No projects were fetched. Did you provide the correct auth token?" if projects.match(/error/m)
+ puts "\nProjects\n" + "".rjust(63,"#")
+ factory.projects.each do |project|
+ puts project
+ end
+ puts
+ end
+
+ def print_project_response(response)
+ case response
+ when /errors/
+ puts "Error creating project: #{response.gsub("\n","").scan(/.*<error[^>]*>(.*?)<\/error>.*/).last.first.gsub(/\s{1,}/," ")}"
+ when /project/
+ project = Project.new(:id => response[/<id[^>]*>(.*?)<\/id>/,1],
+ :name => response[/<name[^>]*>(.*?)<\/name>/,1],
+ :api_key => response[/<api-key[^>]*>(.*?)<\/api-key>/,1])
+ puts "\nProject details\n" + "".rjust(63,"#")
+ puts project
+ else
+ puts "Unexpected error. Please try again!\n"
+ puts response
+ end
+ end
+end
View
41 lib/airbrake/cli/options.rb
@@ -0,0 +1,41 @@
+class Options
+
+ ATTRIBUTES = [:error, :message, :api_key, :host, :port, :auth_token, :name, :account, :rails_env]
+
+ ATTRIBUTES.each do |attribute|
+ attr_reader attribute
+ end
+
+ private
+
+ # You should not write to this from outside
+ ATTRIBUTES.each do |attribute|
+ attr_writer attribute
+ end
+
+ public
+
+ # Parses all the options passed and stores them in attributes
+ def initialize(array = [])
+ opts = Hash[*array]
+ self.error = opts.delete("-e") || opts.delete("--error") { RuntimeError }
+ self.message = opts.delete("-m") || opts.delete("--message") { "I've made a huge mistake" }
+ self.api_key = opts.delete("-k") || opts.delete("--api-key") || config_from_file.api_key || ENV["AIRBRAKE_API_KEY"]
+ self.host = opts.delete("-h") || opts.delete("--host") || config_from_file.host
+ self.port = opts.delete("-p") || opts.delete("--port") || config_from_file.port
+ self.auth_token = opts.delete("-t") || opts.delete("--auth-token") || ENV["AIRBRAKE_AUTH_TOKEN"]
+ self.name = opts.delete("-n") || opts.delete("--name")
+ self.account = opts.delete("-a") || opts.delete("--account") || ENV["AIRBRAKE_ACCOUNT"]
+ self.rails_env = opts.delete("-E") || opts.delete("--rails-env") || ENV["RAILS_ENV"] || "production"
+ opts
+ end
+
+ # Fallback to read from the initializer
+ def config_from_file
+ begin
+ load "config/initializers/airbrake.rb"
+ rescue LoadError
+ end
+ Airbrake.configuration
+ end
+end
View
30 lib/airbrake/cli/printer.rb
@@ -0,0 +1,30 @@
+module Printer
+ def self.print(collection)
+ collection.each do |element|
+ puts element
+ end
+ end
+
+ def self.print_usage
+ puts <<-USAGE
+Usage: airbrake [COMMAND] [OPTION]...
+Commands:
+ raise # Raise an exception specified by ERROR and MESSAGE.
+ list # List all the projects for given AUTH_TOKEN and ACCOUNT.
+ create # Create a project with the given NAME.
+ deploy # Send a new deployment notification to a project that matches the API_KEY.
+
+Options:
+ -e, [--error=ERROR] # Error class to raise. Default: RuntimeError
+ -m, [--message=MESSAGE] # Error message. Default: "I've made a huge mistake"
+ -k, [--api-key=API_KEY] # Api key of your Airbrake application.
+ -h, [--host=HOST] # URL of the Airbrake API server. Default: api.airbrake.io
+ -p, [--port=PORT] # Port of the Airbrake API server. Default: 80
+ -t, [--auth-token=AUTH_TOKEN] # The auth token used for API requests.
+ -a, [--account=ACCOUNT] # The account used for API requests.
+ -n, [--name=NAME] # The name of the project you're trying to create.
+ -E, [--rails-env=NAME] # The name of the environment you're deploying to. Default: production
+ -h, [--help] # Show this usage
+ USAGE
+ end
+end
View
17 lib/airbrake/cli/project.rb
@@ -0,0 +1,17 @@
+class Project
+ attr_writer :name, :id, :api_key
+
+ def initialize(attributes = {})
+ attributes.keys.each do |key|
+ instance_variable_set("@#{key}",attributes[key])
+ end
+ end
+
+ def to_s
+ "#{@name}".rjust(20) + "(#{@id}):".rjust(10) + " #{@api_key}"
+ end
+
+ def valid?
+ @name && @id && @api_key
+ end
+end
View
36 lib/airbrake/cli/project_factory.rb
@@ -0,0 +1,36 @@
+require File.expand_path( "../project", __FILE__)
+# Responsible for creating projects when needed.
+# Creates them from XML received.
+class ProjectFactory
+ def initialize
+ @project = Project.new
+ @projects = []
+ end
+
+ def project
+ @project
+ end
+
+ def create_projects_from_xml(xml)
+ xml.split("\n").each do |line|
+ /<name[^>]*>(?<name>.*?)<\/name>/ =~ line
+ project.name = name.capitalize if name
+ /<id[^>]*>(?<id>.*?)<\/id>/ =~ line
+ project.id = id if id
+ /<api-key[^>]*>(?<api_key>.*?)<\/api-key>/ =~ line
+ project.api_key = api_key if api_key
+ check_project
+ end
+ end
+
+ def check_project
+ if @project.valid?
+ projects << @project
+ @project = Project.new
+ end
+ end
+
+ def projects
+ @projects
+ end
+end
View
48 lib/airbrake/cli/runner.rb
@@ -0,0 +1,48 @@
+require File.expand_path( "../project_factory", __FILE__)
+require File.expand_path( "../options", __FILE__)
+require File.expand_path( "../validator", __FILE__)
+require File.expand_path( "../printer", __FILE__)
+require File.expand_path( "../client", __FILE__)
+
+module Runner
+ extend Validator
+
+ extend self
+
+ attr_accessor :options
+
+ def run!(command, cli_options = {})
+
+ self.options = Options.new(cli_options)
+
+ case command
+ when 'raise'
+ validates :api_key
+ Airbrake.configure do |c|
+ c.api_key = options.api_key
+ c.host = options.host if options.host
+ c.port = options.port if options.port
+ end
+ exception_id = Airbrake.notify(:error_class => options.error,
+ :error_message => "#{options.error}: #{options.message}",
+ :cgi_data => ENV)
+ abort "Error sending exception to Airbrake server. Try again later." unless exception_id
+ puts "Exception sent successfully: http://airbrake.io/locate/#{exception_id}"
+
+ when "list"
+ validates :auth_token, :account
+ Client.print_projects
+
+ when "create"
+ validates :auth_token, :account
+ Client.create_project
+
+ when "deploy"
+ validates :api_key
+ Client.create_deploy
+
+ else
+ Printer.print_usage
+ end
+ end
+end
View
8 lib/airbrake/cli/validator.rb
@@ -0,0 +1,8 @@
+module Validator
+ def validates(*attributes)
+ attributes.each do |attribute|
+ abort "You didn't provide #{attribute.to_s.upcase}"\
+ " so no API request was made." unless Runner.options.send(attribute)
+ end
+ end
+end
View
52 lib/airbrake/configuration.rb
@@ -7,12 +7,12 @@ class Configuration
:http_open_timeout, :http_read_timeout, :ignore, :ignore_by_filters,
:ignore_user_agent, :notifier_name, :notifier_url, :notifier_version,
:params_filters, :project_root, :port, :protocol, :proxy_host,
- :proxy_pass, :proxy_port, :proxy_user, :secure, :use_system_ssl_cert_chain,
- :framework, :user_information, :rescue_rake_exceptions].freeze
+ :proxy_pass, :proxy_port, :proxy_user, :secure, :use_system_ssl_cert_chain,
+ :framework, :user_information, :rescue_rake_exceptions, :rake_environment_filters].freeze
# The API key for your project, found on the project edit form.
attr_accessor :api_key
-
+
# If you're using the Javascript notifier and would want to separate
# Javascript notifications into another Airbrake project, specify
# its APi key here.
@@ -28,7 +28,7 @@ class Configuration
# +true+ for https connections, +false+ for http connections.
attr_accessor :secure
-
+
# +true+ to use whatever CAs OpenSSL has installed on your system. +false+ to use the ca-bundle.crt file included in Airbrake itself (reccomended and default)
attr_accessor :use_system_ssl_cert_chain
@@ -60,6 +60,10 @@ class Configuration
# A list of filters for ignoring exceptions. See #ignore_by_filter.
attr_reader :ignore_by_filters
+ # A list of environment keys that will be ignored from what is sent to Airbrake server
+ # Empty by default and used only in rake handler
+ attr_reader :rake_environment_filters
+
# A list of exception classes to ignore. The array can be appended to.
attr_reader :ignore
@@ -100,11 +104,17 @@ class Configuration
# (boolean or nil; set to nil to catch exceptions when rake isn't running from a terminal; default is nil)
attr_accessor :rescue_rake_exceptions
+ # User attributes that are being captured
+ attr_accessor :user_attributes
+
+
DEFAULT_PARAMS_FILTERS = %w(password password_confirmation).freeze
+ DEFAULT_USER_ATTRIBUTES = %w(id name username email).freeze
+
DEFAULT_BACKTRACE_FILTERS = [
lambda { |line|
- if defined?(Airbrake.configuration.project_root) && Airbrake.configuration.project_root.to_s != ''
+ if defined?(Airbrake.configuration.project_root) && Airbrake.configuration.project_root.to_s != ''
line.sub(/#{Airbrake.configuration.project_root}/, "[PROJECT_ROOT]")
else
line
@@ -151,6 +161,8 @@ def initialize
@framework = 'Standalone'
@user_information = 'Airbrake Error {{error_id}}'
@rescue_rake_exceptions = nil
+ @user_attributes = DEFAULT_USER_ATTRIBUTES.dup
+ @rake_environment_filters = []
end
# Takes a block and adds it to the list of backtrace filters. When the filters
@@ -239,6 +251,23 @@ def protocol
end
end
+ # Should Airbrake send notifications asynchronously
+ # (boolean, nil or callable; default is nil).
+ # Can be used as callable-setter when block provided.
+ def async(&block)
+ if block_given?
+ @async = block
+ end
+ @async
+ end
+ alias_method :async?, :async
+
+ def async=(use_default_or_this)
+ @async = use_default_or_this == true ?
+ default_async_processor :
+ use_default_or_this
+ end
+
def js_api_key
@js_api_key || self.api_key
end
@@ -247,11 +276,6 @@ def js_notifier=(*args)
warn '[AIRBRAKE] config.js_notifier has been deprecated and has no effect. You should use <%= airbrake_javascript_notifier %> directly at the top of your layouts. Be sure to place it before all other javascript.'
end
- def environment_filters
- warn 'config.environment_filters has been deprecated and has no effect.'
- []
- end
-
def ca_bundle_path
if use_system_ssl_cert_chain? && File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE)
OpenSSL::X509::DEFAULT_CERT_FILE
@@ -276,6 +300,12 @@ def default_port
end
end
+ # Async notice delivery defaults to girl friday
+ def default_async_processor
+ queue = GirlFriday::WorkQueue.new(nil, :size => 3) do |notice|
+ Airbrake.sender.send_to_airbrake(notice)
+ end
+ lambda {|notice| queue << notice}
+ end
end
-
end
View
73 lib/airbrake/extensions/blank.rb
@@ -0,0 +1,73 @@
+# stolen from ActiveSupport
+
+class Object
+ unless method_defined?(:blank?)
+ def blank?
+ respond_to?(:empty?) ? empty? : !self
+ end
+ end
+
+ unless method_defined?(:present?)
+ def present?
+ !blank?
+ end
+ end
+
+ unless method_defined?(:presence)
+ def presence
+ self if present?
+ end
+ end
+end
+
+class NilClass
+ unless method_defined?(:blank?)
+ def blank?
+ true
+ end
+ end
+end
+
+class FalseClass
+ unless method_defined?(:blank?)
+ def blank?
+ true
+ end
+ end
+end
+
+class TrueClass
+ unless method_defined?(:blank?)
+ def blank?
+ false
+ end
+ end
+end
+
+class Array
+ unless method_defined?(:blank?)
+ alias_method :blank?, :empty?
+ end
+end
+
+class Hash
+ unless method_defined?(:blank?)
+ alias_method :blank?, :empty?
+ end
+end
+
+class String
+ unless method_defined?(:blank?)
+ def blank?
+ self !~ /[^[:space:]]/
+ end
+ end
+end
+
+class Numeric
+ unless method_defined?(:blank?)
+ def blank?
+ false
+ end
+ end
+end
View
60 lib/airbrake/notice.rb
@@ -4,6 +4,21 @@
module Airbrake
class Notice
+ class << self
+ def attr_reader_with_tracking(*names)
+ attr_readers.concat(names)
+ attr_reader_without_tracking(*names)
+ end
+
+ alias_method :attr_reader_without_tracking, :attr_reader
+ alias_method :attr_reader, :attr_reader_with_tracking
+
+
+ def attr_readers
+ @attr_readers ||= []
+ end
+ end
+
# The exception that caused this notice, if any
attr_reader :exception
@@ -69,6 +84,23 @@ class Notice
# The host name where this error occurred (if any)
attr_reader :hostname
+ # Details about the user who experienced the error
+ attr_reader :user
+
+ private
+
+ # Private writers for all the attributes
+ attr_writer :exception, :api_key, :backtrace, :error_class, :error_message,
+ :backtrace_filters, :parameters, :params_filters,
+ :session_data, :project_root, :url, :ignore,
+ :ignore_by_filters, :notifier_name, :notifier_url, :notifier_version,
+ :component, :action, :cgi_data, :environment_name, :hostname, :user
+
+ # Arguments given in the initializer
+ attr_accessor :args
+
+ public
+
def initialize(args)
self.args = args
self.exception = args[:exception]
@@ -96,10 +128,11 @@ def initialize(args)
self.backtrace = Backtrace.parse(exception_attribute(:backtrace, caller), :filters => self.backtrace_filters)
self.error_class = exception_attribute(:error_class) {|exception| exception.class.name }
self.error_message = exception_attribute(:error_message, 'Notification') do |exception|
- "#{exception.class.name}: #{exception.message}"
+ "#{exception.class.name}: #{args[:error_message] || exception.message}"
end
self.hostname = local_hostname
+ self.user = args[:user]
also_use_rack_params_filters
find_session_data
@@ -161,6 +194,14 @@ def to_xml
env.tag!("environment-name", environment_name)
env.tag!("hostname", hostname)
end
+ unless user.blank?
+ notice.tag!("current-user") do |u|
+ u.tag!("id",user[:id])
+ u.tag!("name",user[:name])
+ u.tag!("email",user[:email])
+ u.tag!("username",user[:username])
+ end
+ end
end
xml.to_s
end
@@ -188,14 +229,6 @@ def [](method)
private
- attr_writer :exception, :api_key, :backtrace, :error_class, :error_message,
- :backtrace_filters, :parameters, :params_filters,
- :environment_filters, :session_data, :project_root, :url, :ignore,
- :ignore_by_filters, :notifier_name, :notifier_url, :notifier_version,
- :component, :action, :cgi_data, :environment_name, :hostname
-
- # Arguments given in the initializer
- attr_accessor :args
# Gets a property named +attribute+ of an exception, either from an actual
# exception or a hash.
@@ -317,6 +350,8 @@ def xml_vars_for(builder, hash)
def rack_env(method)
rack_request.send(method) if rack_request
+ rescue
+ {:message => "failed to call #{method} on Rack::Request -- #{$!.message}"}
end
def rack_request
@@ -344,5 +379,12 @@ def local_hostname
Socket.gethostname
end
+ def to_s
+ content = []
+ self.class.attr_readers.each do |attr|
+ content << " #{attr}: #{send(attr)}"
+ end
+ content.join("\n")
+ end
end
end
View
1  lib/airbrake/rack.rb
@@ -22,7 +22,6 @@ module Airbrake
class Rack
def initialize(app)
@app = app
- Airbrake.configuration.logger ||= Logger.new STDOUT
end
def ignored_user_agent?(env)
View
27 lib/airbrake/rails/controller_methods.rb
@@ -3,12 +3,15 @@ module Rails
module ControllerMethods
def airbrake_request_data
- { :parameters => airbrake_filter_if_filtering(params.to_hash),
+ {
+ :parameters => airbrake_filter_if_filtering(params.to_hash),
:session_data => airbrake_filter_if_filtering(airbrake_session_data),
:controller => params[:controller],
:action => params[:action],
:url => airbrake_request_url,
- :cgi_data => airbrake_filter_if_filtering(request.env) }
+ :cgi_data => airbrake_filter_if_filtering(request.env),
+ :user => airbrake_current_user
+ }
end
private
@@ -20,7 +23,7 @@ def notify_airbrake(hash_or_exception)
Airbrake.notify(hash_or_exception, airbrake_request_data)
end
end
-
+
def airbrake_local_request?
if defined?(::Rails.application.config)
::Rails.application.config.consider_all_requests_local || (request.local? && (!request.env["HTTP_X_FORWARDED_FOR"]))
@@ -39,15 +42,15 @@ def airbrake_ignore_user_agent? #:nodoc:
def airbrake_filter_if_filtering(hash)
return hash if ! hash.is_a?(Hash)
-
+
if respond_to?(:filter_parameters) # Rails 2
- filter_parameters(hash)
+ filter_parameters(hash)
elsif defined?(ActionDispatch::Http::ParameterFilter) # Rails 3
ActionDispatch::Http::ParameterFilter.new(::Rails.application.config.filter_parameters).filter(hash)
else
hash
end rescue hash
-
+
end
def airbrake_session_data
@@ -68,7 +71,17 @@ def airbrake_request_url
url << request.fullpath
url
end
+
+ def airbrake_current_user
+ user = begin current_user rescue current_member end
+ user.attributes.select do |k, v|
+ Airbrake.configuration.
+ user_attributes.map(&:to_sym).
+ include? k.to_sym unless v.blank?
+ end.symbolize_keys
+ rescue NoMethodError, NameError
+ {}
+ end
end
end
end
-
View
1  lib/airbrake/rails/javascript_notifier.rb
@@ -42,7 +42,6 @@ def airbrake_javascript_notifier
end
end
-
end
end
end
View
16 lib/airbrake/rails/middleware/exceptions_catcher.rb
@@ -14,12 +14,16 @@ def skip_user_agent?(env)
end
def render_exception_with_airbrake(env,exception)
- controller = env['action_controller.instance']
- env['airbrake.error_id'] = Airbrake.
- notify_or_ignore(exception,
- controller.try(:airbrake_request_data) || :rack_env => env) unless skip_user_agent?(env)
- if defined?(controller.rescue_action_in_public_without_airbrake)
- controller.rescue_action_in_public_without_airbrake(exception)
+ begin
+ controller = env['action_controller.instance']
+ env['airbrake.error_id'] = Airbrake.
+ notify_or_ignore(exception,
+ controller.try(:airbrake_request_data) || {:rack_env => env}) unless skip_user_agent?(env)
+ if defined?(controller.rescue_action_in_public_without_airbrake)
+ controller.rescue_action_in_public_without_airbrake(exception)
+ end
+ rescue
+ # do nothing
end
render_exception_without_airbrake(env,exception)
end
View
17 lib/airbrake/rails3_tasks.rb
@@ -7,7 +7,19 @@
Rails.logger = defined?(ActiveSupport::TaggedLogging) ?
ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) :
Logger.new(STDOUT)
-
+
+ def wait_for_threads
+ # if using multiple threads, we have to wait for
+ # them to finish
+ if GirlFriday.status.empty?
+ Thread.list.each do |thread|
+ thread.join unless thread == Thread.current
+ end
+ else
+ GirlFriday.shutdown!
+ end
+ end
+
Rails.logger.level = Logger::DEBUG
Airbrake.configure(true) do |config|
config.logger = Rails.logger
@@ -80,6 +92,7 @@ class AirbrakeVerificationController < ApplicationController; end
env = Rack::MockRequest.env_for("/verify")
Rails.application.call(env)
+
+ wait_for_threads
end
end
-
View
2  lib/airbrake/railtie.rb
@@ -15,7 +15,7 @@ class Railtie < ::Rails::Railtie
config.after_initialize do
Airbrake.configure(true) do |config|
- config.logger ||= ::Rails.logger
+ config.logger ||= config.async? ? ::Logger.new(STDERR) : ::Rails.logger
config.environment_name ||= ::Rails.env
config.project_root ||= ::Rails.root
config.framework = "Rails: #{::Rails::VERSION::STRING}"
View
15 lib/airbrake/rake_handler.rb
@@ -10,10 +10,10 @@ def self.included(klass)
def display_error_message_with_airbrake(ex)
if Airbrake.sender && Airbrake.configuration &&
- (Airbrake.configuration.rescue_rake_exceptions ||
+ (Airbrake.configuration.rescue_rake_exceptions ||
(Airbrake.configuration.rescue_rake_exceptions===nil && !self.tty_output?))
- Airbrake.notify_or_ignore(ex, :component => reconstruct_command_line, :cgi_data => ENV)
+ Airbrake.notify_or_ignore(ex, :component => reconstruct_command_line, :cgi_data => environment_info)
end
display_error_message_without_airbrake(ex)
@@ -22,11 +22,17 @@ def display_error_message_with_airbrake(ex)
def reconstruct_command_line
"rake #{ARGV.join( ' ' )}"
end
-
+
+ def environment_info
+ ENV.reject do |k|
+ Airbrake.configuration.rake_environment_filters.include? k
+ end
+ end
+
# This module brings Rake 0.8.7 error handling to 0.9.0 standards
module Rake087Methods
# Method taken from Rake 0.9.0 source
- #
+ #
# Provide standard exception handling for the given block.
def standard_exception_handling
begin
@@ -63,4 +69,3 @@ class << self
include Airbrake::RakeHandler
end
end
-
View
58 lib/airbrake/sender.rb
@@ -1,7 +1,7 @@
module Airbrake
# Sends out the notice to Airbrake
class Sender
-
+
NOTICES_URI = '/notifier_api/v2/notices/'.freeze
HTTP_ERRORS = [Timeout::Error,
Errno::EINVAL,
@@ -14,15 +14,15 @@ class Sender
def initialize(options = {})
[ :proxy_host,
- :proxy_port,
- :proxy_user,
- :proxy_pass,
+ :proxy_port,
+ :proxy_user,
+ :proxy_pass,
:protocol,
- :host,
- :port,
- :secure,
- :use_system_ssl_cert_chain,
- :http_open_timeout,
+ :host,
+ :port,
+ :secure,
+ :use_system_ssl_cert_chain,
+ :http_open_timeout,
:http_read_timeout
].each do |option|
instance_variable_set("@#{option}", options[option])
@@ -31,22 +31,29 @@ def initialize(options = {})
# Sends the notice data off to Airbrake for processing.
#
- # @param [String] data The XML notice to be sent off
- def send_to_airbrake(data)
+ # @param [Notice or String] notice The notice to be sent off
+ def send_to_airbrake(notice)
+ data = notice.respond_to?(:to_xml) ? notice.to_xml : notice
http = setup_http_connection
response = begin
http.post(url.path, data, HEADERS)
rescue *HTTP_ERRORS => e
- log :error, "Unable to contact the Airbrake server. HTTP Error=#{e}"
+ log :level => :error,
+ :message => "Unable to contact the Airbrake server. HTTP Error=#{e}"
nil
end
case response
when Net::HTTPSuccess then
- log :info, "Success: #{response.class}", response
+ log :level => :info,
+ :message => "Success: #{response.class}",
+ :response => response
else
- log :error, "Failure: #{response.class}", response
+ log :level => :error,
+ :message => "Failure: #{response.class}",
+ :response => response,
+ :notice => notice
end
if response && response.respond_to?(:body)
@@ -54,7 +61,10 @@ def send_to_airbrake(data)
error_id[1] if error_id
end
rescue => e
- log :error, "[Airbrake::Sender#send_to_airbrake] Cannot send notification. Error: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
+ log :level => :error,
+ :message => "[Airbrake::Sender#send_to_airbrake] Cannot send notification. Error: #{e.class}" +
+ " - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
+
nil
end
@@ -72,23 +82,24 @@ def send_to_airbrake(data)
alias_method :secure?, :secure
alias_method :use_system_ssl_cert_chain?, :use_system_ssl_cert_chain
-
+
private
def url
URI.parse("#{protocol}://#{host}:#{port}").merge(NOTICES_URI)
end
- def log(level, message, response = nil)
- logger.send level, LOG_PREFIX + message if logger
+ def log(opts = {})
+ opts[:logger].send opts[:level], LOG_PREFIX + opts[:message] if opts[:logger]
Airbrake.report_environment_info
- Airbrake.report_response_body(response.body) if response && response.respond_to?(:body)
+ Airbrake.report_response_body(opts[:response].body) if opts[:response] && opts[:response].respond_to?(:body)
+ Airbrake.report_notice(opts[:notice]) if opts[:notice]
end
def logger
Airbrake.logger
end
-
+
def setup_http_connection
http =
Net::HTTP::Proxy(proxy_host, proxy_port, proxy_user, proxy_pass).
@@ -105,12 +116,13 @@ def setup_http_connection
else
http.use_ssl = false
end
-
+
http
rescue => e
- log :error, "[Airbrake::Sender#setup_http_connection] Failure initializing the HTTP connection.\nError: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
+ log :level => :error,
+ :message => "[Airbrake::Sender#setup_http_connection] Failure initializing the HTTP connection.\n" +
+ "Error: #{e.class} - #{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
raise e
end
-
end
end
View
13 lib/airbrake/shared_tasks.rb
@@ -1,7 +1,18 @@
namespace :airbrake do
desc "Notify Airbrake of a new deploy."
- task :deploy => :environment do
+ task :deploy do
require 'airbrake_tasks'
+
+ if defined?(Rails.root)
+ initializer_file = Rails.root.join('config', 'initializers','airbrake.rb')
+
+ if initializer_file.exist?
+ load initializer_file
+ else
+ Rake::Task[:environment].invoke
+ end
+ end
+
AirbrakeTasks.deploy(:rails_env => ENV['TO'],
:scm_revision => ENV['REVISION'],
:scm_repository => ENV['REPO'],
View
2  lib/airbrake/version.rb
@@ -1,3 +1,3 @@
module Airbrake
- VERSION = "3.1.1".freeze
+ VERSION = "3.1.6".freeze
end
View
1  lib/airbrake_tasks.rb
@@ -1,6 +1,5 @@
require 'net/http'
require 'uri'
-require 'active_support'
# Capistrano tasks for notifying Airbrake of deploys
module AirbrakeTasks
View
12 test/airbrake_2_2.xsd → test/airbrake_2_3.xsd
@@ -9,6 +9,7 @@
<xs:element name="error" type="error"/>
<xs:element name="request" type="request" minOccurs="0"/>
<xs:element name="server-environment" type="serverEnvironment"/>
+ <xs:element name="current-user" type="current-user" minOccurs="0"/>
</xs:all>
<xs:attribute name="version" type="xs:string" use="required"/>
</xs:complexType>
@@ -32,7 +33,7 @@
<xs:complexType name="backtrace">
<xs:sequence>
- <xs:element name="line" maxOccurs="unbounded">
+ <xs:element name="line" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:attribute name="file" type="xs:string" use="required"/>
<xs:attribute name="number" type="xs:string" use="required"/>
@@ -75,4 +76,13 @@
</xs:sequence>
</xs:complexType>
+ <xs:complexType name="current-user">
+ <xs:all>
+ <xs:element name="id" type="xs:string"/>
+ <xs:element name="name" type="xs:string" minOccurs="0"/>
+ <xs:element name="email" type="xs:string" minOccurs="0"/>
+ <xs:element name="username" type="xs:string" minOccurs="0"/>
+ </xs:all>
+ </xs:complexType>
+
</xs:schema>
View
6 test/airbrake_tasks_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path '../helper', __FILE__
require 'rubygems'
require File.dirname(__FILE__) + '/../lib/airbrake_tasks'
@@ -54,7 +54,7 @@ def unsuccessful_response(body = "")
@options = { :rails_env => "staging", :dry_run => false }
end
-
+
context "performing a dry run" do
setup { @output = AirbrakeTasks.deploy(@options.merge(:dry_run => true)) }
@@ -140,7 +140,7 @@ def unsuccessful_response(body = "")
before_should "post to the custom host" do
@post = stub("post", :set_form_data => nil)
@http_proxy = stub("proxy", :request => @response)
-
+
@http_proxy_class = stub("proxy_class", :new => @http_proxy)
@http_proxy_class.expects(:new).with("custom.host", 80).returns(@http_proxy)
Net::HTTP.expects(:Proxy).with(any_parameters).returns(@http_proxy_class)
View
4 test/backtrace_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path '../helper', __FILE__
class BacktraceTest < Test::Unit::TestCase
@@ -20,7 +20,7 @@ class BacktraceTest < Test::Unit::TestCase
assert_equal 'app/controllers/users_controller.rb', line.file
assert_equal 'index', line.method
end
-
+
should "parse a windows backtrace into lines" do
array = [
"C:/Program Files/Server/app/models/user.rb:13:in `magic'",
View
12 test/capistrano_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path '../helper', __FILE__
require 'capistrano/configuration'
require 'airbrake/capistrano'
@@ -7,16 +7,16 @@ class CapistranoTest < Test::Unit::TestCase
def setup
super
reset_config
-
+
@configuration = Capistrano::Configuration.new
Airbrake::Capistrano.load_into(@configuration)
@configuration.dry_run = true
end
-
+
should "define airbrake:deploy task" do
assert_not_nil @configuration.find_task('airbrake:deploy')
end
-
+
should "log when calling airbrake:deploy task" do
@configuration.set(:current_revision, '084505b1c0e0bcf1526e673bb6ac99fbcb18aecc')
@configuration.set(:repository, 'repository')
@@ -24,10 +24,10 @@ def setup
io = StringIO.new
logger = Capistrano::Logger.new(:output => io)
logger.level = Capistrano::Logger::MAX_LEVEL
-
+
@configuration.logger = logger
@configuration.find_and_execute_task('airbrake:deploy')
-
+
assert io.string.include?('** Notifying Airbrake of Deploy')
assert io.string.include?('** Airbrake Notification Complete')
end
View
4 test/catcher_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path '../helper', __FILE__
class ActionControllerCatcherTest < Test::Unit::TestCase