Skip to content
Browse files

send info about the current logged in user

  • Loading branch information...
1 parent da73f16 commit 85181e3ac425ac19f755745e1d5e47baf00c1e9e @shime shime committed Aug 3, 2012
View
18 README.md
@@ -239,6 +239,24 @@ automatically in a controller, Airbrake sets that value. If you're, however, cal
`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(...)`.
+Current user information
+------------------------
+Airbrake provides information about the current logged in user, so you
+could easily determine the user who experienced the error in your app.
+
+It uses `current_user` and `current_member` to identify the
+authenticated user, where `current_user` takes precendence.
+
+If you use different naming, please add the following lines to your
+controller:
+
+ alias_method :current_duck, :current_user
+ helper_method :current_duck
+
+Voila! You'll get information about a duck that experienced crash about
+your app.
+
+
Tracking deployments in Airbrake
--------------------------------
View
18 features/rails.feature
@@ -218,6 +218,7 @@ Feature: Install the Gem in a Rails application
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:
@@ -231,3 +232,20 @@ 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?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
View
30 features/step_definitions/rails_application_steps.rb
@@ -5,6 +5,10 @@
@terminal.build_and_install_gem(File.join(PROJECT_ROOT, "#{gem_name}.gemspec"))
end
+Given /^PENDING/ do
+ pending
+end
+
When /^I generate a new Rails application$/ do
@terminal.cd(TEMP_DIR)
@@ -431,3 +435,29 @@ def rails_non_initializer_airbrake_config_file
When %{I run the airbrake generator with "-k myapikey"}
@terminal.flush! # flush the results of setting up Airbrake (generates notification)
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
3 features/support/airbrake_shim.rb.template
@@ -6,6 +6,9 @@ ShamRack.at("api.airbrake.io") do |env|
<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
4 lib/airbrake/configuration.rb
@@ -12,7 +12,7 @@ class Configuration
# 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
View
34 lib/airbrake/notice.rb
@@ -69,6 +69,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,
+ :environment_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]
@@ -100,6 +117,7 @@ def initialize(args)
end
self.hostname = local_hostname
+ self.user = args[:user]
also_use_rack_params_filters
find_session_data
@@ -161,6 +179,14 @@ def to_xml
env.tag!("environment-name", environment_name)
env.tag!("hostname", hostname)
end
+ if user
+ 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 +214,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.
View
22 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)
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,6 +71,15 @@ def airbrake_request_url
url << request.fullpath
url
end
+
+ def airbrake_current_user
+ user = current_user || current_member
+ user.attributes.select do |k, v|
+ /^(id|name|username|email)$/ === k unless v.blank?
+ end
+ rescue NoMethodError, NameError
+ {}
+ end
end
end
end
View
160 test/notice_test.rb
@@ -24,6 +24,79 @@ def stub_request(attrs = {})
:env => { 'three' => 'four' } }.update(attrs))
end
+ def assert_accepts_exception_attribute(attribute, args = {}, &block)
+ exception = build_exception
+ block ||= lambda { exception.send(attribute) }
+ value = block.call(exception)
+
+ notice_from_exception = build_notice(args.merge(:exception => exception))
+
+ assert_equal notice_from_exception.send(attribute),
+ value,
+ "#{attribute} was not correctly set from an exception"
+
+ notice_from_hash = build_notice(args.merge(attribute => value))
+ assert_equal notice_from_hash.send(attribute),
+ value,
+ "#{attribute} was not correctly set from a hash"
+ end
+
+ def assert_serializes_hash(attribute)
+ [File.open(__FILE__), Proc.new { puts "boo!" }, Module.new].each do |object|
+ hash = {
+ :strange_object => object,
+ :sub_hash => {
+ :sub_object => object
+ },
+ :array => [object]
+ }
+ notice = build_notice(attribute => hash)
+ hash = notice.send(attribute)
+ assert_equal object.to_s, hash[:strange_object], "objects should be serialized"
+ assert_kind_of Hash, hash[:sub_hash], "subhashes should be kept"
+ assert_equal object.to_s, hash[:sub_hash][:sub_object], "subhash members should be serialized"
+ assert_kind_of Array, hash[:array], "arrays should be kept"
+ assert_equal object.to_s, hash[:array].first, "array members should be serialized"
+ end
+ end
+
+ def assert_valid_notice_document(document)
+ xsd_path = File.join(File.dirname(__FILE__), "airbrake_2_2.xsd")
+ schema = Nokogiri::XML::Schema.new(IO.read(xsd_path))
+ errors = schema.validate(document)
+ assert errors.empty?, errors.collect{|e| e.message }.join
+ end
+
+ def assert_filters_hash(attribute)
+ filters = ["abc", :def]
+ original = { 'abc' => "123", 'def' => "456", 'ghi' => "789", 'nested' => { 'abc' => '100' },
+ 'something_with_abc' => 'match the entire string'}
+ filtered = { 'abc' => "[FILTERED]",
+ 'def' => "[FILTERED]",
+ 'something_with_abc' => "match the entire string",
+ 'ghi' => "789",
+ 'nested' => { 'abc' => '[FILTERED]' } }
+
+ notice = build_notice(:params_filters => filters, attribute => original)
+
+ assert_equal(filtered,
+ notice.send(attribute))
+ end
+
+ def build_backtrace_array
+ ["app/models/user.rb:13:in `magic'",
+ "app/controllers/users_controller.rb:8:in `index'"]
+ end
+
+ def hostname
+ `hostname`.chomp
+ end
+
+ def user
+ Struct.new(:email,:id,:name).
+ new("darth@vader.com",1,"Anakin Skywalker")
+ end
+
should "set the api key" do
api_key = 'key'
notice = build_notice(:api_key => api_key)
@@ -78,6 +151,12 @@ def stub_request(attrs = {})
"backtrace was not correctly set from a hash"
end
+ should "accept user" do
+ assert_equal user.id, build_notice(:user => user).user.id
+ assert_equal user.email, build_notice(:user => user).user.email
+ assert_equal user.name, build_notice(:user => user).user.name
+ end
+
should "pass its backtrace filters for parsing" do
backtrace_array = ['my/file/backtrace:3']
exception = build_exception
@@ -316,14 +395,7 @@ def stub_request(attrs = {})
end
end
- ignored_error_classes = %w(
- ActiveRecord::RecordNotFound
- AbstractController::ActionNotFound
- ActionController::RoutingError
- ActionController::InvalidAuthenticityToken
- CGI::Session::CookieStore::TamperedWithCookie
- ActionController::UnknownAction
- )
+ ignored_error_classes = Airbrake::Configuration::IGNORE_DEFAULT
ignored_error_classes.each do |ignored_error_class|
should "ignore #{ignored_error_class} error by default" do
@@ -349,7 +421,6 @@ def stub_request(attrs = {})
end
end
-
should "ensure #to_ary is called on objects that support it" do
assert_nothing_raised do
build_notice(:session => { :object => stub(:to_ary => {}) })
@@ -369,7 +440,7 @@ def stub_request(attrs = {})
end
should "show a nice warning when rack environment exceeds rack keyspace" do
- # simulate exception for to big query
+ # simulate exception for too big query
Rack::Request.any_instance.expects(:params).raises(RangeError.new("exceeded available parameter key space"))
url = "https://subdomain.happylane.com:100/test/file.rb?var=x"
@@ -410,73 +481,4 @@ def stub_request(attrs = {})
assert_equal session_data, notice.session_data
end
-
- def assert_accepts_exception_attribute(attribute, args = {}, &block)
- exception = build_exception
- block ||= lambda { exception.send(attribute) }
- value = block.call(exception)
-
- notice_from_exception = build_notice(args.merge(:exception => exception))
-
- assert_equal notice_from_exception.send(attribute),
- value,
- "#{attribute} was not correctly set from an exception"
-
- notice_from_hash = build_notice(args.merge(attribute => value))
- assert_equal notice_from_hash.send(attribute),
- value,
- "#{attribute} was not correctly set from a hash"
- end
-
- def assert_serializes_hash(attribute)
- [File.open(__FILE__), Proc.new { puts "boo!" }, Module.new].each do |object|
- hash = {
- :strange_object => object,
- :sub_hash => {
- :sub_object => object
- },
- :array => [object]
- }
- notice = build_notice(attribute => hash)
- hash = notice.send(attribute)
- assert_equal object.to_s, hash[:strange_object], "objects should be serialized"
- assert_kind_of Hash, hash[:sub_hash], "subhashes should be kept"
- assert_equal object.to_s, hash[:sub_hash][:sub_object], "subhash members should be serialized"
- assert_kind_of Array, hash[:array], "arrays should be kept"
- assert_equal object.to_s, hash[:array].first, "array members should be serialized"
- end
- end
-
- def assert_valid_notice_document(document)
- xsd_path = File.join(File.dirname(__FILE__), "airbrake_2_2.xsd")
- schema = Nokogiri::XML::Schema.new(IO.read(xsd_path))
- errors = schema.validate(document)
- assert errors.empty?, errors.collect{|e| e.message }.join
- end
-
- def assert_filters_hash(attribute)
- filters = ["abc", :def]
- original = { 'abc' => "123", 'def' => "456", 'ghi' => "789", 'nested' => { 'abc' => '100' },
- 'something_with_abc' => 'match the entire string'}
- filtered = { 'abc' => "[FILTERED]",
- 'def' => "[FILTERED]",
- 'something_with_abc' => "match the entire string",
- 'ghi' => "789",
- 'nested' => { 'abc' => '[FILTERED]' } }
-
- notice = build_notice(:params_filters => filters, attribute => original)
-
- assert_equal(filtered,
- notice.send(attribute))
- end
-
- def build_backtrace_array
- ["app/models/user.rb:13:in `magic'",
- "app/controllers/users_controller.rb:8:in `index'"]
- end
-
- def hostname
- `hostname`.chomp
- end
-
end

0 comments on commit 85181e3

Please sign in to comment.
Something went wrong with that request. Please try again.