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,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