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,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ log/
12
+ lib/bundler/man
13
+ vendor/bundle
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
20
+ *.swp
@@ -0,0 +1,17 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.3
5
+ - 1.9.2
6
+ - ruby-head
7
+ - jruby-18mode
8
+ - jruby-19mode
9
+ - jruby-head
10
+ - rbx-18mode
11
+ - rbx-19mode
12
+ matrix:
13
+ allow_failures:
14
+ - rvm: ruby-head
15
+ - rvm: rbx-18mode
16
+ - rvm: jruby-18mode
17
+ - rvm: jruby-19mode
@@ -0,0 +1,90 @@
1
+ # Change Log
2
+
3
+ **0.8.0**
4
+ - Rename to rollbar
5
+
6
+ **0.7.1**
7
+ - Fix ratchetio:test rake task when project base controller is not called ApplicationController
8
+
9
+ **0.7.0**
10
+ - Exceptions in Rake tasks are now automatically reported.
11
+
12
+ **0.6.4**
13
+ - Bump multi_json dependency version to 1.6.0
14
+
15
+ **0.6.3**
16
+ - Bump multi_json dependency version to 1.5.1
17
+
18
+ **0.6.2**
19
+ - Added EventMachine support
20
+
21
+ **0.6.1**
22
+ - Added a log message containing a link to the instance. Copy-paste the link into your browser to view its details in Ratchet.
23
+ - Ratchetio.report_message now returns 'ignored' or 'error' instead of nil when a message is not reported for one of those reasons, for consistency with Ratchetio.report_exception.
24
+
25
+ **0.6.0**
26
+ - POSSIBLE BREAKING CHANGE: Ratchetio.report_exception now returns 'ignored', 'disabled', or 'error' instead of nil when the exception is not reported for one of those reasons. It still returns the payload upon success.
27
+ - Request data is now parsed from the rack environment instead of from within the controller, addressing issue #10.
28
+ - Add Sidekiq middleware for catching workers' exceptions
29
+ - Replaced activesupport dependency with multi_json
30
+
31
+ **0.5.5**
32
+ - Added activesupport dependency for use without Rails
33
+
34
+ **0.5.4**
35
+ - Added new default scrub params
36
+
37
+ **0.5.3**
38
+ - Add `Ratchetio.silenced`; which allows disabling reporting for a given block. See README for usage.
39
+
40
+ **0.5.2**
41
+ - Fix compat issue with delayed_job below version 3. Exceptions raised by delayed_job below version 3 will not be automatically caught; upgrade to v3 or catch and report by hand.
42
+
43
+ **0.5.1**
44
+ - Save the exception uuid in `env['ratchetio.exception_uuid']` for display in user-facing error pages.
45
+
46
+ **0.5.0**
47
+ - Add support to report exceptions raised in delayed_job.
48
+
49
+ **0.4.11**
50
+ - Allow exceptions with no backtrace (e.g. StandardError subclasses)
51
+
52
+ **0.4.10**
53
+ - Fix compatability issue with ruby 1.8
54
+
55
+ **0.4.9**
56
+ - Start including a UUID in reported exceptions
57
+ - Fix issue with scrub_fields, and add `:password_confirmation` to the default list
58
+
59
+ **0.4.8**
60
+ - Add ability to send reports asynchronously, using girl_friday or Threading by default.
61
+ - Add ability to save reports to a file (for use with ratchet-agent) instead of sending across to Ratchet servers.
62
+
63
+ **0.4.7**
64
+ - Sensitive params now scrubbed out of requests. Param name list is customizable via the `scrub_fields` config option.
65
+
66
+ **0.4.6**
67
+ - Add support to play nicely with Goalie.
68
+
69
+ **0.4.5**
70
+ - Add `default_logger` config option. It should be a lambda that will return the logger to use if no other logger is configured (i.e. no logger is set by the Railtie hook). Default: `lambda { Logger.new(STDERR) }`
71
+
72
+ **0.4.4**
73
+ - Add `enabled` runtime config flag. When `false`, no data (messages or exceptions) will be reported.
74
+
75
+ **0.4.3**
76
+ - Add RSpec test suite. A few minor code changes.
77
+
78
+ **0.4.2**
79
+ - Add "ignore" filter level to completely ignore exceptions by class.
80
+
81
+ **0.4.1**
82
+ - Recursively filter files out of the params hash. Thanks to [trisweb](https://github.com/trisweb) for the pull request.
83
+
84
+ **0.4.0**
85
+
86
+ - Breaking change to make the "person" more configurable. If you were previously relying on your `current_member` method being called to return the person object, you will need to add the following line to `config/initializers/ratchetio.rb`:
87
+
88
+ config.person_method = "current_member"
89
+
90
+ - Person id, username, and email method names are now configurable -- see README for details.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'sqlite3', :platform => [:ruby, :mswin, :mingw]
6
+ gem 'jruby-openssl', :platform => :jruby
7
+ gem 'activerecord-jdbcsqlite3-adapter', :platform => :jruby
8
+ gem 'girl_friday'
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Rollbar, Inc.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,165 @@
1
+ # Rollbar [![Build Status](https://secure.travis-ci.org/rollbar/rollbar-gem.png?branch=master)](https://travis-ci.org/rollbar/rollbar-gem)
2
+
3
+ Ruby gem for Rollbar, for reporting exceptions in Rails 3 to Rollbar. Requires a Rollbar account (you can sign up for free).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rollbar'
10
+
11
+ And then execute:
12
+
13
+ $ bundle install
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rollbar
18
+
19
+ Then, run the following command from your rails root:
20
+
21
+ $ rails generate rollbar YOUR_ROLLBAR_PROJECT_ACCESS_TOKEN
22
+
23
+ That will create the file `config/initializers/rollbar.rb`, which holds the configuration values (currently just your access token) and is all you need to use Rollbar with Rails.
24
+
25
+ To confirm that it worked, run:
26
+
27
+ $ rake rollbar:test
28
+
29
+ This will raise an exception within a test request; if it works, you'll see a stacktrace in the console, and the exception will appear in the Rollbar dashboard.
30
+
31
+ ## Manually reporting exceptions and messages
32
+
33
+ To report a caught exception to Rollbar, simply call `Rollbar.report_exception`:
34
+
35
+ ```ruby
36
+ begin
37
+ foo = bar
38
+ rescue Exception => e
39
+ Rollbar.report_exception(e)
40
+ end
41
+ ```
42
+
43
+ If you're reporting an exception in the context of a request and are in a controller, you can pass along the same request and person context as the global exception handler, like so:
44
+
45
+ ```ruby
46
+ begin
47
+ foo = bar
48
+ rescue Exception => e
49
+ Rollbar.report_exception(e, rollbar_request_data, rollbar_person_data)
50
+ end
51
+ ```
52
+
53
+ You can also log individual messages:
54
+
55
+ ```ruby
56
+ # logs at the 'warning' level. all levels: debug, info, warning, error, critical
57
+ Rollbar.report_message("Unexpected input", "warning")
58
+
59
+ # default level is "info"
60
+ Rollbar.report_message("Login successful")
61
+
62
+ # can also include additional data as a hash in the final param. :body is reserved.
63
+ Rollbar.report_message("Login successful", "info", :user => @user)
64
+ ```
65
+
66
+
67
+ ## Person tracking
68
+
69
+ Rollbar will send information about the current user (called a "person" in Rollbar parlance) along with each error report, when available. This works by calling the `current_user` controller method. The return value should be an object with an `id` method and, optionally, `username` and `email` methods.
70
+
71
+ If the gem should call a controller method besides `current_user`, add the following in `config/initializers/rollbar.rb`:
72
+
73
+ ```ruby
74
+ config.person_method = "my_current_user"
75
+ ```
76
+
77
+ If the methods to extract the `id`, `username`, and `email` from the object returned by the `person_method` have other names, configure like so in `config/initializers/rollbar.rb`:
78
+
79
+ ```ruby
80
+ config.person_id_method = "user_id" # default is "id"
81
+ config.person_username_method = "user_name" # default is "username"
82
+ config.person_email_method = "email_address" # default is "email"
83
+ ```
84
+
85
+
86
+ ## Exception level filters
87
+
88
+ By default, all exceptions reported through `Rollbar.report_exception()` are reported at the "error" level, except for the following, which are reported at "warning" level:
89
+
90
+ - ActiveRecord::RecordNotFound
91
+ - AbstractController::ActionNotFound
92
+ - ActionController::RoutingError
93
+
94
+ If you'd like to customize this list, see the example code in `config/initializers/rollbar.rb`. Supported levels: "critical", "error", "warning", "info", "debug", "ignore". Set to "ignore" to cause the exception not to be reported at all.
95
+
96
+
97
+ ## Silencing exceptions at runtime
98
+
99
+ If you just want to disable exception reporting for a single block, use `Rollbar.silenced`:
100
+
101
+ ```ruby
102
+ Rollbar.silenced {
103
+ foo = bar # will not be reported
104
+ }
105
+ ```
106
+
107
+
108
+ ## Asynchronous reporting
109
+
110
+ By default, all messages are reported synchronously. You can enable asynchronous reporting by adding the following in `config/initializers/rollbar.rb`:
111
+
112
+ ```ruby
113
+ config.use_async = true
114
+ ```
115
+
116
+ Rollbar uses [girl_friday](https://github.com/mperham/girl_friday) to handle asynchronous reporting when installed, and falls back to Threading if girl_friday is not installed.
117
+
118
+ You can supply your own handler using `config.async_handler`. The handler should schedule the payload for later processing (i.e. with a delayed_job, in a resque queue, etc.) and should itself return immediately. For example:
119
+
120
+ ```ruby
121
+ config.async_handler = Proc.new { |payload|
122
+ Thread.new { Rollbar.process_payload(payload) }
123
+ }
124
+ ```
125
+
126
+ Make sure you pass `payload` to `Rollbar.process_payload` in your own implementation.
127
+
128
+
129
+ ## Using with rollbar-agent
130
+
131
+ For even more asynchrony, you can configure the gem to write to a file instead of sending the payload to Rollbar servers directly. [rollbar-agent](https://github.com/rollbar/rollbar-agent) can then be hooked up to this file to actually send the payload across. To enable, add the following in `config/initializers/rollbar.rb`:
132
+
133
+ ```ruby
134
+ config.write_to_file = true
135
+ # optional, defaults to "#{AppName}.rollbar"
136
+ config.filepath = '/path/to/file.rollbar' #should end in '.rollbar' for use with rollbar-agent
137
+ ```
138
+
139
+ For this to work, you'll also need to set up rollbar-agent--see its docs for details.
140
+
141
+
142
+ ## Using with Goalie
143
+
144
+ If you're using [Goalie](https://github.com/obvio171/goalie) for custom error pages, you may need to explicitly add `require 'goalie'` to `config/application.rb` (in addition to `require 'goalie/rails'`) so that the monkeypatch will work. (This will be obvious if it is needed because your app won't start up: you'll see a cryptic error message about `Goalie::CustomErrorPages.render_exception` not being defined.)
145
+
146
+
147
+ ## Using with Resque
148
+
149
+ Check out [resque-ratchetio](https://github.com/CrowdFlower/resque-ratchetio) for using Rollbar as a failure backend for Resque.
150
+
151
+
152
+ ## Help / Support
153
+
154
+ If you run into any issues, please email us at `support@rollbar.com`
155
+
156
+
157
+ ## Contributing
158
+
159
+ 1. Fork it
160
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
161
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
162
+ 4. Push to the branch (`git push origin my-new-feature`)
163
+ 5. Create new Pull Request
164
+
165
+ We're using RSpec for testing. Run the test suite with `rake spec`. Tests for pull requests are appreciated but not required. (If you don't include a test, we'll write one before merging.)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ namespace :dummy do
8
+ load 'spec/dummyapp/Rakefile'
9
+ end
10
+
11
+ desc 'Run specs'
12
+ task :default => ['dummy:db:setup'] do
13
+ Rake::Task[:spec].invoke
14
+ end
data/THANKS ADDED
@@ -0,0 +1,10 @@
1
+ Huge thanks to the following contributors (by github username). For the most up-to-date list, see https://github.com/rollbar/rollbar-gem/graphs/contributors
2
+
3
+ arr-ee
4
+ JoshuaOSHickman
5
+ kavu
6
+ magnolia-fan
7
+ mipearson
8
+ trisweb
9
+ tysontate
10
+ wbond
@@ -0,0 +1,53 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/named_base'
3
+
4
+ module Rollbar
5
+ module Generators
6
+ class RollbarGenerator < ::Rails::Generators::Base
7
+ argument :access_token, :type => :string, :banner => 'access_token'
8
+
9
+ source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
10
+
11
+ def create_initializer
12
+ puts "creating initializer..."
13
+ if access_token_configured?
14
+ puts "It looks like you've already configured Rollbar."
15
+ puts "To re-create the config file, remove it first: config/initializers/rollbar.rb"
16
+ exit
17
+ end
18
+
19
+ puts "access token: " << access_token
20
+
21
+ template 'initializer.rb', 'config/initializers/rollbar.rb',
22
+ :assigns => { :access_token => access_token_expr }
23
+
24
+ # TODO run rake test task
25
+ end
26
+
27
+ #def add_options!(opt)
28
+ # opt.on('-a', '--access-token=token', String, "Your Rollbar project access token") { |v| options[:access_token] = v }
29
+ #end
30
+ #
31
+ # def manifest
32
+ # if !access_token_configured? && !options[:access_token]
33
+ # puts "access_token is required. Pass --access-token=YOUR_ACCESS_TOKEN"
34
+ # exit
35
+ # end
36
+ #
37
+ # record do |m|
38
+ # m.template 'initializer.rb', 'config/initializers/rollbar.rb',
39
+ # :assigns => { :access_token => access_token_expr }
40
+ # # TODO run rake test task
41
+ # end
42
+ # end
43
+
44
+ def access_token_expr
45
+ "'#{access_token}'"
46
+ end
47
+
48
+ def access_token_configured?
49
+ File.exists?('config/initializers/rollbar.rb')
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,28 @@
1
+ require 'rollbar/rails'
2
+ Rollbar.configure do |config|
3
+ config.access_token = <%= access_token_expr %>
4
+
5
+ # By default, Rollbar will try to call the `current_user` controller method
6
+ # to fetch the logged-in user object, and then call that object's `id`,
7
+ # `username`, and `email` methods to fetch those properties. To customize:
8
+ # config.person_method = "my_current_user"
9
+ # config.person_id_method = "my_id"
10
+ # config.person_username_method = "my_username"
11
+ # config.person_email_method = "my_email"
12
+
13
+ # Add exception class names to the exception_level_filters hash to
14
+ # change the level that exception is reported at. Note that if an exception
15
+ # has already been reported and logged the level will need to be changed
16
+ # via the rollbar interface.
17
+ # Valid levels: 'critical', 'error', 'warning', 'info', 'debug', 'ignore'
18
+ # 'ignore' will cause the exception to not be reported at all.
19
+ # config.exception_level_filters.merge!('MyCriticalException' => 'critical')
20
+
21
+ # Enable asynchronous reporting (uses girl_friday or Threading if girl_friday
22
+ # is not installed)
23
+ # config.use_async = true
24
+ # Supply your own async handler:
25
+ # config.async_handler = Proc.new { |payload|
26
+ # Thread.new { Rollbar.process_payload(payload) }
27
+ # }
28
+ end
@@ -0,0 +1,358 @@
1
+ require 'net/https'
2
+ require 'securerandom' if defined?(SecureRandom)
3
+ require 'socket'
4
+ require 'thread'
5
+ require 'uri'
6
+
7
+ require "girl_friday" if defined?(GirlFriday)
8
+
9
+ require 'rollbar/version'
10
+ require 'rollbar/configuration'
11
+ require 'rollbar/request_data_extractor'
12
+ require 'rollbar/exception_reporter'
13
+
14
+ require 'rollbar/delayed_job' if defined?(Delayed) && defined?(Delayed::Plugins)
15
+ require 'rollbar/sidekiq' if defined?(Sidekiq)
16
+ require 'rollbar/goalie' if defined?(Goalie)
17
+ require 'rollbar/rack' if defined?(Rack)
18
+ require 'rollbar/rake' if defined?(Rake)
19
+ require 'rollbar/railtie' if defined?(Rails)
20
+
21
+ module Rollbar
22
+ class << self
23
+ attr_writer :configuration
24
+ attr_reader :last_report
25
+
26
+ # Configures the gem.
27
+ #
28
+ # Call on app startup to set the `access_token` (required) and other config params.
29
+ # In a Rails app, this is called by `config/initializers/rollbar.rb` which is generated
30
+ # with `rails generate rollbar access-token-here`
31
+ #
32
+ # @example
33
+ # Rollbar.configure do |config|
34
+ # config.access_token = 'abcdefg'
35
+ # end
36
+ def configure
37
+ yield(configuration)
38
+ end
39
+
40
+ def reconfigure
41
+ @configuration = Configuration.new
42
+ yield(configuration)
43
+ end
44
+
45
+ # Returns the configuration object.
46
+ #
47
+ # @return [Rollbar::Configuration] The configuration object
48
+ def configuration
49
+ @configuration ||= Configuration.new
50
+ end
51
+
52
+ # Reports an exception to Rollbar. Returns the exception data hash.
53
+ #
54
+ # @example
55
+ # begin
56
+ # foo = bar
57
+ # rescue => e
58
+ # Rollbar.report_exception(e)
59
+ # end
60
+ #
61
+ # @param exception [Exception] The exception object to report
62
+ # @param request_data [Hash] Data describing the request. Should be the result of calling
63
+ # `rollbar_request_data`.
64
+ # @param person_data [Hash] Data describing the affected person. Should be the result of calling
65
+ # `rollbar_person_data`
66
+ def report_exception(exception, request_data = nil, person_data = nil)
67
+ return 'disabled' unless configuration.enabled
68
+ return 'ignored' if ignored?(exception)
69
+
70
+ data = exception_data(exception, filtered_level(exception))
71
+ data[:request] = request_data if request_data
72
+ data[:person] = person_data if person_data
73
+
74
+ @last_report = data
75
+
76
+ payload = build_payload(data)
77
+ schedule_payload(payload)
78
+ log_instance_link(data)
79
+ data
80
+ rescue => e
81
+ logger.error "[Rollbar] Error reporting exception to Rollbar: #{e}"
82
+ 'error'
83
+ end
84
+
85
+ # Reports an arbitrary message to Rollbar
86
+ #
87
+ # @example
88
+ # Rollbar.report_message("User login failed", 'info', :user_id => 123)
89
+ #
90
+ # @param message [String] The message body. This will be used to identify the message within
91
+ # Rollbar. For best results, avoid putting variables in the message body; pass them as
92
+ # `extra_data` instead.
93
+ # @param level [String] The level. One of: 'critical', 'error', 'warning', 'info', 'debug'
94
+ # @param extra_data [Hash] Additional data to include alongside the body. Don't use 'body' as
95
+ # it is reserved.
96
+ def report_message(message, level = 'info', extra_data = {})
97
+ return 'disabled' unless configuration.enabled
98
+
99
+ data = message_data(message, level, extra_data)
100
+ payload = build_payload(data)
101
+ schedule_payload(payload)
102
+ log_instance_link(data)
103
+ data
104
+ rescue => e
105
+ logger.error "[Rollbar] Error reporting message to Rollbar: #{e}"
106
+ 'error'
107
+ end
108
+
109
+ # Turns off reporting for the given block.
110
+ #
111
+ # @example
112
+ # Rollbar.silenced { raise }
113
+ #
114
+ # @yield Block which exceptions won't be reported.
115
+ def silenced
116
+ begin
117
+ yield
118
+ rescue => e
119
+ e.instance_variable_set(:@_rollbar_do_not_report, true)
120
+ raise
121
+ end
122
+ end
123
+
124
+ def process_payload(payload)
125
+ begin
126
+ if configuration.write_to_file
127
+ write_payload(payload)
128
+ else
129
+ send_payload(payload)
130
+ end
131
+ rescue => e
132
+ logger.error "[Rollbar] Error reporting message to Rollbar: #{e}"
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ def log_instance_link(data)
139
+ logger.info "[Rollbar] Details: #{configuration.web_base}/instance/uuid?uuid=#{data[:uuid]}"
140
+ end
141
+
142
+ def ignored?(exception)
143
+ if filtered_level(exception) == 'ignore'
144
+ return true
145
+ end
146
+
147
+ if exception.instance_variable_get(:@_rollbar_do_not_report)
148
+ return true
149
+ end
150
+
151
+ false
152
+ end
153
+
154
+ def filtered_level(exception)
155
+ configuration.exception_level_filters[exception.class.name]
156
+ end
157
+
158
+ def message_data(message, level, extra_data)
159
+ data = base_data(level)
160
+
161
+ data[:body] = {
162
+ :message => {
163
+ :body => message.to_s
164
+ }
165
+ }
166
+ data[:body][:message].merge!(extra_data)
167
+ data[:server] = server_data
168
+
169
+ data
170
+ end
171
+
172
+ def exception_data(exception, force_level = nil)
173
+ data = base_data
174
+
175
+ data[:level] = force_level if force_level
176
+
177
+ # parse backtrace
178
+ if exception.backtrace.respond_to?( :map )
179
+ frames = exception.backtrace.map { |frame|
180
+ # parse the line
181
+ match = frame.match(/(.*):(\d+)(?::in `([^']+)')?/)
182
+ { :filename => match[1], :lineno => match[2].to_i, :method => match[3] }
183
+ }
184
+ # reverse so that the order is as rollbar expects
185
+ frames.reverse!
186
+ else
187
+ frames = []
188
+ end
189
+
190
+ data[:body] = {
191
+ :trace => {
192
+ :frames => frames,
193
+ :exception => {
194
+ :class => exception.class.name,
195
+ :message => exception.message
196
+ }
197
+ }
198
+ }
199
+
200
+ data[:server] = server_data
201
+
202
+ data
203
+ end
204
+
205
+ def logger
206
+ # init if not set
207
+ unless configuration.logger
208
+ configuration.logger = configuration.default_logger.call
209
+ end
210
+ configuration.logger
211
+ end
212
+
213
+ def write_payload(payload)
214
+ if configuration.use_async
215
+ @file_semaphore.synchronize {
216
+ do_write_payload(payload)
217
+ }
218
+ else
219
+ do_write_payload(payload)
220
+ end
221
+ end
222
+
223
+ def do_write_payload(payload)
224
+ logger.info '[Rollbar] Writing payload to file'
225
+
226
+ begin
227
+ unless @file
228
+ @file = File.open(configuration.filepath, "a")
229
+ end
230
+
231
+ @file.puts payload
232
+ @file.flush
233
+ logger.info "[Rollbar] Success"
234
+ rescue IOError => e
235
+ logger.error "[Rollbar] Error opening/writing to file: #{e}"
236
+ end
237
+ end
238
+
239
+ def send_payload_using_eventmachine(payload)
240
+ req = EventMachine::HttpRequest.new(configuration.endpoint).post(:body => payload)
241
+ req.callback do
242
+ if req.response_header.status == 200
243
+ logger.info '[Rollbar] Success'
244
+ else
245
+ logger.warn "[Rollbar] Got unexpected status code from Rollbar.io api: #{req.response_header.status}"
246
+ logger.info "[Rollbar] Response: #{req.response}"
247
+ end
248
+ end
249
+ req.errback do
250
+ logger.warn "[Rollbar] Call to API failed, status code: #{req.response_header.status}"
251
+ logger.info "[Rollbar] Error's response: #{req.response}"
252
+ end
253
+ end
254
+
255
+ def send_payload(payload)
256
+ logger.info '[Rollbar] Sending payload'
257
+
258
+ if configuration.use_eventmachine
259
+ send_payload_using_eventmachine(payload)
260
+ return
261
+ end
262
+ uri = URI.parse(configuration.endpoint)
263
+ http = Net::HTTP.new(uri.host, uri.port)
264
+
265
+ if uri.scheme == 'https'
266
+ http.use_ssl = true
267
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
268
+ end
269
+
270
+ request = Net::HTTP::Post.new(uri.request_uri)
271
+ request.body = payload
272
+ response = http.request(request)
273
+
274
+ if response.code == '200'
275
+ logger.info '[Rollbar] Success'
276
+ else
277
+ logger.warn "[Rollbar] Got unexpected status code from Rollbar api: #{response.code}"
278
+ logger.info "[Rollbar] Response: #{response.body}"
279
+ end
280
+ end
281
+
282
+ def schedule_payload(payload)
283
+ logger.info '[Rollbar] Scheduling payload'
284
+
285
+ if configuration.use_async
286
+ unless configuration.async_handler
287
+ configuration.async_handler = method(:default_async_handler)
288
+ end
289
+
290
+ if configuration.write_to_file
291
+ unless @file_semaphore
292
+ @file_semaphore = Mutex.new
293
+ end
294
+ end
295
+
296
+ configuration.async_handler.call(payload)
297
+ else
298
+ process_payload(payload)
299
+ end
300
+ end
301
+
302
+ def build_payload(data)
303
+ payload = {
304
+ :access_token => configuration.access_token,
305
+ :data => data
306
+ }
307
+ MultiJson.dump(payload)
308
+ end
309
+
310
+ def base_data(level = 'error')
311
+ config = configuration
312
+ data = {
313
+ :timestamp => Time.now.to_i,
314
+ :environment => config.environment,
315
+ :level => level,
316
+ :language => 'ruby',
317
+ :framework => config.framework,
318
+ :notifier => {
319
+ :name => 'rollbar-gem',
320
+ :version => VERSION
321
+ }
322
+ }
323
+
324
+ if defined?(SecureRandom) and SecureRandom.respond_to?(:uuid)
325
+ data[:uuid] = SecureRandom.uuid
326
+ end
327
+
328
+ data
329
+ end
330
+
331
+ def server_data
332
+ config = configuration
333
+
334
+ data = {
335
+ :host => Socket.gethostname
336
+ }
337
+ data[:root] = config.root.to_s if config.root
338
+ data[:branch] = config.branch if config.branch
339
+
340
+ data
341
+ end
342
+
343
+ def default_async_handler(payload)
344
+ if defined?(GirlFriday)
345
+ unless @queue
346
+ @queue = GirlFriday::WorkQueue.new(nil, :size => 5) do |payload|
347
+ process_payload(payload)
348
+ end
349
+ end
350
+
351
+ @queue.push(payload)
352
+ else
353
+ logger.warn '[Rollbar] girl_friday not found to handle async call, falling back to Thread'
354
+ Thread.new { process_payload(payload) }
355
+ end
356
+ end
357
+ end
358
+ end