rollbar 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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