rollbar 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/.gitignore +20 -0
  2. data/.travis.yml +17 -0
  3. data/CHANGELOG.md +90 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE +22 -0
  6. data/README.md +165 -0
  7. data/Rakefile +14 -0
  8. data/THANKS +10 -0
  9. data/lib/generators/rollbar/rollbar_generator.rb +53 -0
  10. data/lib/generators/rollbar/templates/initializer.rb +28 -0
  11. data/lib/rollbar.rb +358 -0
  12. data/lib/rollbar/configuration.rb +64 -0
  13. data/lib/rollbar/delayed_job.rb +25 -0
  14. data/lib/rollbar/exception_reporter.rb +24 -0
  15. data/lib/rollbar/goalie.rb +33 -0
  16. data/lib/rollbar/middleware/rack/builder.rb +21 -0
  17. data/lib/rollbar/middleware/rack/test_session.rb +31 -0
  18. data/lib/rollbar/middleware/rails/show_exceptions.rb +26 -0
  19. data/lib/rollbar/rack.rb +9 -0
  20. data/lib/rollbar/rails.rb +22 -0
  21. data/lib/rollbar/rails/controller_methods.rb +28 -0
  22. data/lib/rollbar/railtie.rb +37 -0
  23. data/lib/rollbar/rake.rb +9 -0
  24. data/lib/rollbar/rake_tasks.rb +60 -0
  25. data/lib/rollbar/request_data_extractor.rb +115 -0
  26. data/lib/rollbar/sidekiq.rb +25 -0
  27. data/lib/rollbar/version.rb +3 -0
  28. data/rollbar.gemspec +24 -0
  29. data/spec/controllers/home_controller_spec.rb +180 -0
  30. data/spec/dummyapp/.gitignore +73 -0
  31. data/spec/dummyapp/Rakefile +7 -0
  32. data/spec/dummyapp/app/assets/javascripts/application.js +3 -0
  33. data/spec/dummyapp/app/assets/stylesheets/application.css.scss +37 -0
  34. data/spec/dummyapp/app/controllers/application_controller.rb +3 -0
  35. data/spec/dummyapp/app/controllers/home_controller.rb +25 -0
  36. data/spec/dummyapp/app/controllers/users_controller.rb +17 -0
  37. data/spec/dummyapp/app/helpers/.gitkeep +0 -0
  38. data/spec/dummyapp/app/mailers/.gitkeep +0 -0
  39. data/spec/dummyapp/app/models/.gitkeep +0 -0
  40. data/spec/dummyapp/app/models/user.rb +10 -0
  41. data/spec/dummyapp/app/views/devise/registrations/edit.html.erb +27 -0
  42. data/spec/dummyapp/app/views/devise/registrations/new.html.erb +20 -0
  43. data/spec/dummyapp/app/views/devise/shared/_links.html.erb +25 -0
  44. data/spec/dummyapp/app/views/home/cause_exception.html.erb +1 -0
  45. data/spec/dummyapp/app/views/home/index.html.erb +4 -0
  46. data/spec/dummyapp/app/views/home/report_exception.html.erb +1 -0
  47. data/spec/dummyapp/app/views/layouts/_messages.html.erb +5 -0
  48. data/spec/dummyapp/app/views/layouts/_navigation.html.erb +21 -0
  49. data/spec/dummyapp/app/views/layouts/application.html.erb +25 -0
  50. data/spec/dummyapp/app/views/users/index.html.erb +8 -0
  51. data/spec/dummyapp/app/views/users/show.html.erb +3 -0
  52. data/spec/dummyapp/config.ru +4 -0
  53. data/spec/dummyapp/config/application.rb +60 -0
  54. data/spec/dummyapp/config/boot.rb +10 -0
  55. data/spec/dummyapp/config/database.yml +25 -0
  56. data/spec/dummyapp/config/environment.rb +5 -0
  57. data/spec/dummyapp/config/environments/development.rb +37 -0
  58. data/spec/dummyapp/config/environments/production.rb +67 -0
  59. data/spec/dummyapp/config/environments/test.rb +37 -0
  60. data/spec/dummyapp/config/initializers/backtrace_silencers.rb +7 -0
  61. data/spec/dummyapp/config/initializers/devise.rb +233 -0
  62. data/spec/dummyapp/config/initializers/inflections.rb +15 -0
  63. data/spec/dummyapp/config/initializers/mime_types.rb +5 -0
  64. data/spec/dummyapp/config/initializers/rollbar.rb +20 -0
  65. data/spec/dummyapp/config/initializers/secret_token.rb +7 -0
  66. data/spec/dummyapp/config/initializers/session_store.rb +8 -0
  67. data/spec/dummyapp/config/initializers/wrap_parameters.rb +14 -0
  68. data/spec/dummyapp/config/locales/devise.en.yml +58 -0
  69. data/spec/dummyapp/config/locales/en.yml +5 -0
  70. data/spec/dummyapp/config/routes.rb +14 -0
  71. data/spec/dummyapp/db/migrate/20121121184652_devise_create_users.rb +46 -0
  72. data/spec/dummyapp/db/migrate/20121121184654_add_name_to_users.rb +5 -0
  73. data/spec/dummyapp/db/schema.rb +35 -0
  74. data/spec/dummyapp/db/seeds.rb +12 -0
  75. data/spec/dummyapp/lib/assets/.gitkeep +0 -0
  76. data/spec/dummyapp/public/404.html +26 -0
  77. data/spec/dummyapp/public/422.html +26 -0
  78. data/spec/dummyapp/public/500.html +25 -0
  79. data/spec/dummyapp/public/favicon.ico +0 -0
  80. data/spec/dummyapp/script/rails +6 -0
  81. data/spec/requests/home_spec.rb +48 -0
  82. data/spec/rollbar_spec.rb +426 -0
  83. data/spec/spec_helper.rb +35 -0
  84. data/spec/support/devise.rb +3 -0
  85. metadata +282 -0
@@ -0,0 +1,64 @@
1
+ require 'logger'
2
+
3
+ module Rollbar
4
+ class Configuration
5
+
6
+ attr_accessor :access_token
7
+ attr_accessor :async_handler
8
+ attr_accessor :branch
9
+ attr_accessor :default_logger
10
+ attr_accessor :enabled
11
+ attr_accessor :endpoint
12
+ attr_accessor :environment
13
+ attr_accessor :exception_level_filters
14
+ attr_accessor :filepath
15
+ attr_accessor :framework
16
+ attr_accessor :logger
17
+ attr_accessor :person_method
18
+ attr_accessor :person_id_method
19
+ attr_accessor :person_username_method
20
+ attr_accessor :person_email_method
21
+ attr_accessor :root
22
+ attr_accessor :scrub_fields
23
+ attr_accessor :use_async
24
+ attr_accessor :use_eventmachine
25
+ attr_accessor :web_base
26
+ attr_accessor :write_to_file
27
+
28
+ DEFAULT_ENDPOINT = 'https://api.rollbar.com/api/1/item/'
29
+ DEFAULT_WEB_BASE = 'https://rollbar.com'
30
+
31
+ def initialize
32
+ @async_handler = nil
33
+ @default_logger = lambda { Logger.new(STDERR) }
34
+ @enabled = true
35
+ @endpoint = DEFAULT_ENDPOINT
36
+ @exception_level_filters = {
37
+ 'ActiveRecord::RecordNotFound' => 'warning',
38
+ 'AbstractController::ActionNotFound' => 'warning',
39
+ 'ActionController::RoutingError' => 'warning'
40
+ }
41
+ @framework = 'Plain'
42
+ @person_method = 'current_user'
43
+ @person_id_method = 'id'
44
+ @person_username_method = 'username'
45
+ @person_email_method = 'email'
46
+ @scrub_fields = [:passwd, :password, :password_confirmation, :secret,
47
+ :confirm_password, :password_confirmation]
48
+ @use_async = false
49
+ @web_base = DEFAULT_WEB_BASE
50
+ @write_to_file = false
51
+ @use_eventmachine = false
52
+ end
53
+
54
+ def use_eventmachine=(value)
55
+ require 'em-http-request' if value
56
+ @use_eventmachine = value
57
+ end
58
+
59
+ # allow params to be read like a hash
60
+ def [](option)
61
+ send(option)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,25 @@
1
+ # based on http://bit.ly/VGdfVI
2
+
3
+ module Delayed
4
+ module Plugins
5
+ class Rollbar < Plugin
6
+ module ReportErrors
7
+ def error(job, error)
8
+ # send the job object as the 'request data'
9
+ ::Rollbar.report_exception(error, job)
10
+ super if defined?(super)
11
+ end
12
+ end
13
+
14
+ callbacks do |lifecycle|
15
+ lifecycle.before(:invoke_job) do |job|
16
+ payload = job.payload_object
17
+ payload = payload.object if payload.is_a? Delayed::PerformableMethod
18
+ payload.extend ReportErrors
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ Delayed::Worker.plugins << Delayed::Plugins::Rollbar
@@ -0,0 +1,24 @@
1
+ module Rollbar
2
+ module ExceptionReporter
3
+ include RequestDataExtractor
4
+
5
+ def report_exception_to_rollbar(env, exception)
6
+ rollbar_debug "[Rollbar] Reporting exception: #{exception.try(:message)}", :error
7
+ request_data = extract_request_data_from_rack(env)
8
+ person_data = extract_person_data_from_controller(env)
9
+ exception_data = Rollbar.report_exception(exception, request_data, person_data)
10
+ env['rollbar.exception_uuid'] = exception_data[:uuid]
11
+ rollbar_debug "[Rollbar] Exception uuid saved in env: #{exception_data[:uuid]}"
12
+ rescue => e
13
+ rollbar_debug "[Rollbar] Exception while reporting exception to Rollbar: #{e.try(:message)}"
14
+ end
15
+
16
+ def rollbar_debug(message, level = :debug)
17
+ if defined?(Rails)
18
+ ::Rails.logger.send(level, message)
19
+ else
20
+ puts message
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,33 @@
1
+ module Goalie
2
+ class CustomErrorPages
3
+ alias_method :orig_render_exception, :render_exception
4
+
5
+ private
6
+
7
+ def render_exception(env, exception)
8
+ exception_data = nil
9
+ begin
10
+ controller = env['action_controller.instance']
11
+ request_data = controller.try(:rollbar_request_data)
12
+ person_data = controller.try(:rollbar_person_data)
13
+ exception_data = Rollbar.report_exception(exception, request_data, person_data)
14
+ rescue => e
15
+ # TODO use logger here?
16
+ puts "[Rollbar] Exception while reporting exception to Rollbar: #{e}"
17
+ end
18
+
19
+ # if an exception was reported, save uuid in the env
20
+ # so it can be displayed to the user on the error page
21
+ if exception_data
22
+ begin
23
+ env['rollbar.exception_uuid'] = exception_data[:uuid]
24
+ rescue => e
25
+ puts "[Rollbar] Exception saving uuid in env: #{e}"
26
+ end
27
+ end
28
+
29
+ # now continue as normal
30
+ orig_render_exception(env, exception)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ module Rollbar
2
+ module Middleware
3
+ module Rack
4
+ module Builder
5
+ include ExceptionReporter
6
+
7
+ def call_with_rollbar(env)
8
+ call_without_rollbar(env)
9
+ rescue => exception
10
+ report_exception_to_rollbar(env, exception)
11
+ raise exception
12
+ end
13
+
14
+ def self.included(base)
15
+ base.send(:alias_method, :call_without_rollbar, :call)
16
+ base.send(:alias_method, :call, :call_with_rollbar)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,31 @@
1
+ module Rollbar
2
+ module Middleware
3
+ module Rack
4
+ module TestSession
5
+ include ExceptionReporter
6
+
7
+ def process_request_with_rollbar(uri, env, &block)
8
+ process_request_without_rollbar(uri, env, &block)
9
+ rescue => exception
10
+ report_exception_to_rollbar(env, exception)
11
+ raise exception
12
+ end
13
+
14
+ def env_for_with_rollbar(path, env)
15
+ env_for_without_rollbar(path, env)
16
+ rescue => exception
17
+ report_exception_to_rollbar(env, exception)
18
+ raise exception
19
+ end
20
+
21
+ def self.included(base)
22
+ base.send(:alias_method, :process_request_without_rollbar, :process_request)
23
+ base.send(:alias_method, :process_request, :process_request_with_rollbar)
24
+
25
+ base.send(:alias_method, :env_for_without_rollbar, :env_for)
26
+ base.send(:alias_method, :env_for, :env_for_with_rollbar)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ module Rollbar
2
+ module Middleware
3
+ module Rails
4
+ module ShowExceptions
5
+ include ExceptionReporter
6
+
7
+ def render_exception_with_rollbar(env, exception)
8
+ report_exception_to_rollbar(env, exception)
9
+ render_exception_without_rollbar(env, exception)
10
+ end
11
+
12
+ def call_with_rollbar(env)
13
+ call_without_rollbar(env)
14
+ rescue => exception
15
+ report_exception_to_rollbar(env, exception)
16
+ raise exception
17
+ end
18
+
19
+ def self.included(base)
20
+ base.send(:alias_method_chain, :render_exception, :rollbar)
21
+ base.send(:alias_method_chain, :call, :rollbar)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ if defined?(Rack::Builder)
2
+ require 'rollbar/middleware/rack/builder'
3
+ Rack::Builder.send(:include, Rollbar::Middleware::Rack::Builder)
4
+ end
5
+
6
+ if defined?(Rack::Test::Session)
7
+ require 'rollbar/middleware/rack/test_session'
8
+ Rack::Test::Session.send(:include, Rollbar::Middleware::Rack::TestSession)
9
+ end
@@ -0,0 +1,22 @@
1
+ require 'rollbar'
2
+
3
+ module Rollbar
4
+ module Rails
5
+ def self.initialize
6
+ rails_logger = if defined?(::Rails.logger)
7
+ ::Rails.logger
8
+ elsif defined?(RAILS_DEFAULT_LOGGER)
9
+ RAILS_DEFAULT_LOGGER
10
+ end
11
+
12
+ Rollbar.configure do |config|
13
+ config.logger = rails_logger
14
+ config.environment = defined?(::Rails.env) && ::Rails.env || defined?(RAILS_ENV) && RAILS_ENV
15
+ config.root = defined?(::Rails.root) && ::Rails.root || defined?(RAILS_ROOT) && RAILS_ROOT
16
+ config.framework = defined?(::Rails.version) && "Rails: #{::Rails.version}" || defined?(::Rails::VERSION::STRING) && "Rails: #{::Rails::VERSION::STRING}"
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ Rollbar::Rails.initialize
@@ -0,0 +1,28 @@
1
+ module Rollbar
2
+ module Rails
3
+ module ControllerMethods
4
+ include RequestDataExtractor
5
+
6
+ def rollbar_person_data
7
+ user = send(Rollbar.configuration.person_method)
8
+ # include id, username, email if non-empty
9
+ if user
10
+ {
11
+ :id => (user.send(Rollbar.configuration.person_id_method) rescue nil),
12
+ :username => (user.send(Rollbar.configuration.person_username_method) rescue nil),
13
+ :email => (user.send(Rollbar.configuration.person_email_method) rescue nil)
14
+ }
15
+ else
16
+ {}
17
+ end
18
+ rescue NoMethodError, NameError
19
+ {}
20
+ end
21
+
22
+ def rollbar_request_data
23
+ extract_request_data_from_rack(request.env)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,37 @@
1
+ require 'rails'
2
+ require 'rollbar'
3
+
4
+ module Rollbar
5
+ class Railtie < ::Rails::Railtie
6
+ rake_tasks do
7
+ require 'rollbar/rake_tasks'
8
+ end
9
+
10
+ config.after_initialize do
11
+ Rollbar.configure do |config|
12
+ config.logger ||= ::Rails.logger
13
+ config.environment ||= ::Rails.env
14
+ config.root ||= ::Rails.root
15
+ config.framework = "Rails: #{::Rails::VERSION::STRING}"
16
+ config.filepath ||= ::Rails.application.class.parent_name + '.rollbar'
17
+ end
18
+
19
+ ActiveSupport.on_load(:action_controller) do
20
+ # lazily load action_controller methods
21
+ require 'rollbar/rails/controller_methods'
22
+ include Rollbar::Rails::ControllerMethods
23
+ end
24
+
25
+ if defined?(ActionDispatch::DebugExceptions)
26
+ # Rails 3.2.x
27
+ require 'rollbar/middleware/rails/show_exceptions'
28
+ ActionDispatch::DebugExceptions.send(:include, Rollbar::Middleware::Rails::ShowExceptions)
29
+ elsif defined?(ActionDispatch::ShowExceptions)
30
+ # Rails 3.0.x and 3.1.x
31
+ require 'rollbar/middleware/rails/show_exceptions'
32
+ ActionDispatch::ShowExceptions.send(:include, Rollbar::Middleware::Rails::ShowExceptions)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,9 @@
1
+ module Rake
2
+ class Application
3
+ alias_method :orig_display_error_message, :display_error_message
4
+ def display_error_message(ex)
5
+ Rollbar.report_exception(ex)
6
+ orig_display_error_message(ex)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,60 @@
1
+ require 'rollbar'
2
+
3
+ namespace :rollbar do
4
+ desc "Verify your gem installation by sending a test exception to Rollbar"
5
+ task :test => [:environment] do
6
+ Rails.logger = defined?(ActiveSupport::TaggedLogging) ?
7
+ ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) :
8
+ Logger.new(STDOUT)
9
+
10
+ Rails.logger.level = Logger::DEBUG
11
+ Rollbar.configure do |config|
12
+ config.logger = Rails.logger
13
+ end
14
+
15
+ class RollbarTestingException < RuntimeError; end
16
+
17
+ unless Rollbar.configuration.access_token
18
+ puts "Rollbar needs an access token configured. Check the README for instructions."
19
+ exit
20
+ end
21
+
22
+ begin
23
+ require './app/controllers/application_controller'
24
+ rescue LoadError
25
+ end
26
+
27
+ unless defined?(ApplicationController)
28
+ puts "No ApplicationController found, using ActionController::Base instead"
29
+ class ApplicationController < ActionController::Base; end
30
+ end
31
+
32
+ puts "Setting up the controller."
33
+ class ApplicationController
34
+ prepend_before_filter :test_rollbar
35
+ def test_rollbar
36
+ puts "Raising RollbarTestingException to simulate app failure."
37
+ raise RollbarTestingException.new, 'Testing rollbar with "rake rollbar:test". If you can see this, it works.'
38
+ end
39
+
40
+ def verify
41
+ end
42
+
43
+ def logger
44
+ nil
45
+ end
46
+ end
47
+
48
+ class RollbarTestController < ApplicationController; end
49
+
50
+ Rails.application.routes_reloader.execute_if_updated
51
+ Rails.application.routes.draw do
52
+ match 'verify' => 'application#verify', :as => 'verify'
53
+ end
54
+
55
+ puts "Processing..."
56
+ env = Rack::MockRequest.env_for("/verify")
57
+
58
+ Rails.application.call(env)
59
+ end
60
+ end
@@ -0,0 +1,115 @@
1
+ module Rollbar
2
+ module RequestDataExtractor
3
+ ATTACHMENT_CLASSES = %w[
4
+ ActionDispatch::Http::UploadedFile
5
+ Rack::Multipart::UploadedFile
6
+ ].freeze
7
+
8
+ def extract_person_data_from_controller(env)
9
+ controller = env['action_controller.instance']
10
+ person_data = controller ? controller.try(:rollbar_person_data) : {}
11
+ end
12
+
13
+ def extract_request_data_from_rack(env)
14
+ sensitive_params = sensitive_params_list(env)
15
+ request_params = rollbar_request_params(env)
16
+ cookies = rollbar_filtered_params(sensitive_params, rollbar_request_cookies(env))
17
+ get_params = rollbar_filtered_params(sensitive_params, rollbar_get_params(env))
18
+ post_params = rollbar_filtered_params(sensitive_params, rollbar_post_params(env))
19
+
20
+ {
21
+ :params => get_params.merge(post_params).merge(request_params),
22
+ :url => rollbar_url(env),
23
+ :user_ip => rollbar_user_ip(env),
24
+ :headers => rollbar_headers(env),
25
+ :GET => get_params,
26
+ :POST => post_params,
27
+ :cookies => cookies,
28
+ :session => env['rack.session.options'],
29
+ :method => rollbar_request_method(env)
30
+ }
31
+ end
32
+
33
+ private
34
+
35
+ def rollbar_request_method(env)
36
+ env['REQUEST_METHOD'] || env[:method]
37
+ end
38
+
39
+ def rollbar_headers(env)
40
+ env.keys.grep(/^HTTP_/).map do |header|
41
+ name = header.gsub(/^HTTP_/, '').split('_').map(&:capitalize).join('-')
42
+ { name => env[header] }
43
+ end.inject(:merge)
44
+ end
45
+
46
+ def rollbar_url(env)
47
+ scheme = env['rack.url_scheme']
48
+ host = env['HTTP_HOST'] || env['SERVER_NAME']
49
+ path = env['ORIGINAL_FULLPATH'] || env['REQUEST_URI']
50
+ unless path.nil? || path.empty?
51
+ path = '/' + path.to_s if path.to_s.slice(0, 1) != '/'
52
+ end
53
+
54
+ [scheme, '://', host, path].join
55
+ end
56
+
57
+ def rollbar_user_ip(env)
58
+ (env['action_dispatch.remote_ip'] || env['HTTP_X_REAL_IP'] || env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_ADDR']).to_s
59
+ end
60
+
61
+ def rollbar_request_params(env)
62
+ route = ::Rails.application.routes.recognize_path(env['PATH_INFO']) rescue {}
63
+ {
64
+ :controller => route[:controller],
65
+ :action => route[:action],
66
+ :format => route[:format],
67
+ }
68
+ end
69
+
70
+ def rollbar_get_params(env)
71
+ rack_request(env).GET
72
+ rescue
73
+ {}
74
+ end
75
+
76
+ def rollbar_post_params(env)
77
+ rack_request(env).POST
78
+ rescue
79
+ {}
80
+ end
81
+
82
+ def rollbar_request_cookies(env)
83
+ rack_request(env).cookie
84
+ rescue
85
+ {}
86
+ end
87
+
88
+ def rack_request(env)
89
+ @rack_request ||= Rack::Request.new(env)
90
+ end
91
+
92
+ def rollbar_filtered_params(sensitive_params, params)
93
+ params.inject({}) do |result, (key, value)|
94
+ if key.to_sym.in?(sensitive_params)
95
+ result[key] = '*' * (value.length rescue 8)
96
+ elsif value.is_a?(Hash)
97
+ result[key] = rollbar_filtered_params(sensitive_params, value)
98
+ elsif value.class.name.in?(ATTACHMENT_CLASSES)
99
+ result[key] = {
100
+ :content_type => value.content_type,
101
+ :original_filename => value.original_filename,
102
+ :size => value.tempfile.size
103
+ } rescue 'Uploaded file'
104
+ else
105
+ result[key] = value
106
+ end
107
+ result
108
+ end
109
+ end
110
+
111
+ def sensitive_params_list(env)
112
+ Rollbar.configuration.scrub_fields |= Array(env['action_dispatch.parameter_filter'])
113
+ end
114
+ end
115
+ end