├── spec
├── dummyapp
│ ├── app
│ │ ├── models
│ │ │ ├── .gitkeep
│ │ │ └── user.rb
│ │ ├── helpers
│ │ │ └── .gitkeep
│ │ ├── mailers
│ │ │ └── .gitkeep
│ │ ├── views
│ │ │ ├── home
│ │ │ │ ├── report_exception.html.erb
│ │ │ │ ├── cause_exception.html.erb
│ │ │ │ └── index.html.erb
│ │ │ ├── users
│ │ │ │ ├── show.html.erb
│ │ │ │ └── index.html.erb
│ │ │ ├── layouts
│ │ │ │ ├── _messages.html.erb
│ │ │ │ ├── _navigation.html.erb
│ │ │ │ └── application.html.erb
│ │ │ └── devise
│ │ │ │ ├── registrations
│ │ │ │ ├── new.html.erb
│ │ │ │ └── edit.html.erb
│ │ │ │ └── shared
│ │ │ │ └── _links.html.erb
│ │ ├── assets
│ │ │ ├── javascripts
│ │ │ │ └── application.js
│ │ │ └── stylesheets
│ │ │ │ └── application.css.scss
│ │ └── controllers
│ │ │ ├── application_controller.rb
│ │ │ ├── users_controller.rb
│ │ │ └── home_controller.rb
│ ├── lib
│ │ └── assets
│ │ │ └── .gitkeep
│ ├── public
│ │ ├── favicon.ico
│ │ ├── 500.html
│ │ ├── 422.html
│ │ └── 404.html
│ ├── db
│ │ ├── migrate
│ │ │ ├── 20121121184654_add_name_to_users.rb
│ │ │ └── 20121121184652_devise_create_users.rb
│ │ ├── seeds.rb
│ │ └── schema.rb
│ ├── config.ru
│ ├── config
│ │ ├── environment.rb
│ │ ├── initializers
│ │ │ ├── mime_types.rb
│ │ │ ├── backtrace_silencers.rb
│ │ │ ├── session_store.rb
│ │ │ ├── secret_token.rb
│ │ │ ├── wrap_parameters.rb
│ │ │ ├── inflections.rb
│ │ │ └── rollbar.rb
│ │ ├── locales
│ │ │ ├── en.yml
│ │ │ └── devise.en.yml
│ │ ├── boot.rb
│ │ ├── routes.rb
│ │ ├── database.yml
│ │ ├── environments
│ │ │ ├── development.rb
│ │ │ ├── test.rb
│ │ │ └── production.rb
│ │ └── application.rb
│ ├── Rakefile
│ ├── script
│ │ └── rails
│ └── .gitignore
├── generators
│ └── rollbar
│ │ └── rollbar_generator_spec.rb
├── spec_helper.rb
├── delay
│ └── sidekiq_spec.rb
├── requests
│ └── home_spec.rb
├── controllers
│ └── home_controller_spec.rb
└── rollbar_spec.rb
├── lib
├── rollbar
│ ├── version.rb
│ ├── rake.rb
│ ├── rack.rb
│ ├── delay
│ │ ├── sucker_punch.rb
│ │ └── sidekiq.rb
│ ├── active_record_extension.rb
│ ├── sidekiq.rb
│ ├── middleware
│ │ ├── rack
│ │ │ ├── builder.rb
│ │ │ └── test_session.rb
│ │ └── rails
│ │ │ └── show_exceptions.rb
│ ├── delayed_job.rb
│ ├── rails.rb
│ ├── rails
│ │ └── controller_methods.rb
│ ├── exception_reporter.rb
│ ├── railtie.rb
│ ├── goalie.rb
│ ├── better_errors.rb
│ ├── capistrano.rb
│ ├── rake_tasks.rb
│ ├── configuration.rb
│ └── request_data_extractor.rb
├── generators
│ └── rollbar
│ │ ├── rollbar_generator.rb
│ │ └── templates
│ │ └── initializer.rb
└── rollbar.rb
├── Gemfile
├── .gitignore
├── Appraisals
├── gemfiles
├── rails30.gemfile
├── rails31.gemfile
├── rails32.gemfile
└── rails40.gemfile
├── Rakefile
├── UPGRADE_FROM_RATCHET.md
├── THANKS.md
├── LICENSE
├── rollbar.gemspec
├── .travis.yml
├── CHANGELOG.md
└── README.md
/spec/dummyapp/app/models/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/dummyapp/lib/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/dummyapp/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/helpers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/mailers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/rollbar/version.rb:
--------------------------------------------------------------------------------
1 | module Rollbar
2 | VERSION = "0.10.12"
3 | end
4 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/views/home/report_exception.html.erb:
--------------------------------------------------------------------------------
1 | Reported an exception.
2 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | //= require jquery
2 | //= require jquery_ujs
3 | //= require_tree .
4 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/views/home/cause_exception.html.erb:
--------------------------------------------------------------------------------
1 | This page won't be reached, since the controller method will raise a NameError.
2 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/views/users/show.html.erb:
--------------------------------------------------------------------------------
1 |
User
2 | User: <%= @user.name %>
3 | Email: <%= @user.email if @user.email %>
--------------------------------------------------------------------------------
/spec/dummyapp/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | protect_from_forgery
3 | end
4 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/views/home/index.html.erb:
--------------------------------------------------------------------------------
1 | Home
2 | <% @users.each do |user| %>
3 | User: <%=link_to user.name, user %>
4 | <% end %>
--------------------------------------------------------------------------------
/spec/dummyapp/db/migrate/20121121184654_add_name_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddNameToUsers < ActiveRecord::Migration
2 | def change
3 | add_column :users, :name, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/dummyapp/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require ::File.expand_path('../config/environment', __FILE__)
4 | run Dummyapp::Application
5 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/views/layouts/_messages.html.erb:
--------------------------------------------------------------------------------
1 | <% flash.each do |name, msg| %>
2 | <% if msg.is_a?(String) %>
3 | <%= content_tag :div, msg, :id => "flash_#{name}" %>
4 | <% end %>
5 | <% end %>
--------------------------------------------------------------------------------
/spec/dummyapp/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the rails application
2 | require File.expand_path('../application', __FILE__)
3 |
4 | # Initialize the rails application
5 | Dummy::Application.initialize!
6 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/models/user.rb:
--------------------------------------------------------------------------------
1 | class User < ActiveRecord::Base
2 | # Setup accessible (or protected) attributes for your model
3 | attr_accessible :name, :email, :password, :password_confirmation, :remember_me
4 | end
5 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/views/users/index.html.erb:
--------------------------------------------------------------------------------
1 | Users
2 |
3 | <% @users.each do |user| %>
4 | -
5 | <%= link_to user.name, user %> signed up <%= user.created_at.to_date %>
6 |
7 | <% end %>
8 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 | # Mime::Type.register_alias "text/html", :iphone
6 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Sample localization file for English. Add more files in this directory for other locales.
2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3 |
4 | en:
5 | hello: "Hello world"
6 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
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 'appraisal'
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | .bundle
4 | .config
5 | .yardoc
6 | Gemfile.lock
7 | gemfiles/*.lock
8 | InstalledFiles
9 | _yardoc
10 | coverage
11 | doc/
12 | log/
13 | lib/bundler/man
14 | vendor/bundle
15 | pkg
16 | rdoc
17 | spec/reports
18 | test/tmp
19 | test/version_tmp
20 | tmp
21 | *.swp
22 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/boot.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | gemfile = File.expand_path('../../../../Gemfile', __FILE__)
3 |
4 | if File.exist?(gemfile)
5 | ENV['BUNDLE_GEMFILE'] = gemfile
6 | require 'bundler'
7 | Bundler.setup
8 | end
9 |
10 | $:.unshift File.expand_path('../../../../lib', __FILE__)
--------------------------------------------------------------------------------
/lib/rollbar/rake.rb:
--------------------------------------------------------------------------------
1 | require 'rake'
2 |
3 | module Rake
4 | class Application
5 | alias_method :orig_display_error_message, :display_error_message
6 | def display_error_message(ex)
7 | Rollbar.report_exception(ex)
8 | orig_display_error_message(ex)
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/Appraisals:
--------------------------------------------------------------------------------
1 | appraise "rails30" do
2 | gem "rails", "3.0.20"
3 | end
4 |
5 | appraise "rails31" do
6 | gem "rails", "3.1.12"
7 | end
8 |
9 | appraise "rails32" do
10 | gem "rails", "3.2.12"
11 | end
12 |
13 | appraise "rails40" do
14 | gem "rails", "4.0.0"
15 | gem "protected_attributes"
16 | end
17 |
--------------------------------------------------------------------------------
/spec/dummyapp/Rakefile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env rake
2 | # Add your own tasks in files placed in lib/tasks ending in .rake,
3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
4 |
5 | require File.expand_path('../config/application', __FILE__)
6 |
7 | Dummy::Application.load_tasks
8 |
--------------------------------------------------------------------------------
/spec/dummyapp/script/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3 |
4 | APP_PATH = File.expand_path('../../config/application', __FILE__)
5 | require File.expand_path('../../config/boot', __FILE__)
6 | require 'rails/commands'
7 |
--------------------------------------------------------------------------------
/gemfiles/rails30.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "sqlite3", :platform=>[:ruby, :mswin, :mingw]
6 | gem "jruby-openssl", :platform=>:jruby
7 | gem "activerecord-jdbcsqlite3-adapter", :platform=>:jruby
8 | gem "appraisal"
9 | gem "rails", "3.0.20"
10 |
11 | gemspec :path=>"../"
--------------------------------------------------------------------------------
/gemfiles/rails31.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "sqlite3", :platform=>[:ruby, :mswin, :mingw]
6 | gem "jruby-openssl", :platform=>:jruby
7 | gem "activerecord-jdbcsqlite3-adapter", :platform=>:jruby
8 | gem "appraisal"
9 | gem "rails", "3.1.12"
10 |
11 | gemspec :path=>"../"
--------------------------------------------------------------------------------
/gemfiles/rails32.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "sqlite3", :platform=>[:ruby, :mswin, :mingw]
6 | gem "jruby-openssl", :platform=>:jruby
7 | gem "activerecord-jdbcsqlite3-adapter", :platform=>:jruby
8 | gem "appraisal"
9 | gem "rails", "3.2.12"
10 |
11 | gemspec :path=>"../"
--------------------------------------------------------------------------------
/lib/rollbar/rack.rb:
--------------------------------------------------------------------------------
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
10 |
--------------------------------------------------------------------------------
/gemfiles/rails40.gemfile:
--------------------------------------------------------------------------------
1 | # This file was generated by Appraisal
2 |
3 | source "https://rubygems.org"
4 |
5 | gem "sqlite3", :platform=>[:ruby, :mswin, :mingw]
6 | gem "jruby-openssl", :platform=>:jruby
7 | gem "activerecord-jdbcsqlite3-adapter", :platform=>:jruby
8 | gem "appraisal"
9 | gem "rails", "4.0.0"
10 | gem "protected_attributes"
11 |
12 | gemspec :path=>"../"
--------------------------------------------------------------------------------
/spec/dummyapp/config/routes.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.routes.draw do
2 | root :to => "home#index"
3 | resources :users do
4 | member { post :start_session }
5 | end
6 |
7 | get "/cause_exception" => "home#cause_exception"
8 | match "/report_exception" => "home#report_exception", :via => [:get, :post, :put]
9 | get "/current_user" => "home#current_user"
10 | end
11 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/controllers/users_controller.rb:
--------------------------------------------------------------------------------
1 | class UsersController < ApplicationController
2 | before_filter :authenticate_user!
3 |
4 | def index
5 | @users = User.all
6 | end
7 |
8 | def show
9 | @user = User.find(params[:id])
10 | end
11 |
12 | def start_session
13 | @user = User.find(params[:id])
14 | cookies[:session_id] = @user.encrypted_password
15 | end
16 |
17 | end
18 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env rake
2 | require 'rubygems'
3 | require 'bundler/setup'
4 | require 'bundler/gem_tasks'
5 | require 'rspec/core/rake_task'
6 | require 'appraisal'
7 |
8 | RSpec::Core::RakeTask.new(:spec)
9 |
10 | desc 'Run specs'
11 | task :default do
12 | ENV['LOCAL'] = '1'
13 | Rake::Task[:spec].invoke
14 |
15 | Rake::Task[:spec].reenable
16 |
17 | ENV['LOCAL'] = '0'
18 | Rake::Task[:spec].invoke
19 | end
--------------------------------------------------------------------------------
/lib/rollbar/delay/sucker_punch.rb:
--------------------------------------------------------------------------------
1 | require 'sucker_punch'
2 |
3 | module Rollbar
4 | module Delay
5 | class SuckerPunch
6 | def self.handle(payload)
7 | @@sucker_punch_worker ||= self.new
8 | @@sucker_punch_worker.async.perform payload
9 | end
10 |
11 | include ::SuckerPunch::Job
12 |
13 | def perform(*args)
14 | Rollbar.process_payload(*args)
15 | end
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Dummy::Application.config.session_store :cookie_store, :key => '_dummy_session'
4 |
5 | # Use the database for sessions instead of the cookie-based default,
6 | # which shouldn't be used to store highly confidential information
7 | # (create the session table with "rails generate session_migration")
8 | # Dummy::Application.config.session_store :active_record_store
9 |
--------------------------------------------------------------------------------
/lib/rollbar/active_record_extension.rb:
--------------------------------------------------------------------------------
1 | module Rollbar
2 | module ActiveRecordExtension
3 | extend ActiveSupport::Concern
4 |
5 | def report_validation_errors_to_rollbar
6 | self.errors.full_messages.each do |error|
7 | logger.info "[Rollbar] Reporting form validation error: #{error} for #{self.to_s}"
8 | Rollbar.report_message("Form Validation Error: #{error} for #{self.to_s}")
9 | end
10 | end
11 | end
12 | end
13 |
14 | ActiveRecord::Base.send(:include, Rollbar::ActiveRecordExtension)
15 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/initializers/secret_token.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 | # Make sure the secret is at least 30 characters and all random,
6 | # no regular words or you'll be exposed to dictionary attacks.
7 | Dummy::Application.config.secret_token = '61f244779bab3ceba188492a31fb02b0a975fb64b93e217c03966ef065f5f1aba3b145c165defe0f8256e45cc6c526a60c9780a506a16e730815a3f812b5f9e9'
8 |
--------------------------------------------------------------------------------
/lib/rollbar/delay/sidekiq.rb:
--------------------------------------------------------------------------------
1 | require 'sidekiq'
2 |
3 | module Rollbar
4 | module Delay
5 | class Sidekiq
6 | OPTIONS = { 'queue' => 'rollbar', 'class' => self.name }.freeze
7 |
8 | def self.handle(payload)
9 | item = Rollbar.configuration.use_sidekiq.is_a?(Hash) ? OPTIONS.merge(Rollbar.configuration.use_sidekiq) : OPTIONS
10 |
11 | ::Sidekiq::Client.push item.merge('args' => [payload])
12 | end
13 |
14 | include ::Sidekiq::Worker
15 |
16 | def perform(*args)
17 | Rollbar.process_payload(*args)
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/lib/rollbar/sidekiq.rb:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | module Rollbar
4 | class Sidekiq
5 | def call(worker, msg, queue)
6 | begin
7 | yield
8 | rescue => e
9 | msg.delete('backtrace')
10 | msg.delete('error_backtrace')
11 | msg.delete('error_message')
12 | msg.delete('error_class')
13 |
14 | Rollbar.report_exception(e, :params => msg)
15 | raise
16 | end
17 | end
18 | end
19 | end
20 |
21 | Sidekiq.configure_server do |config|
22 | config.server_middleware do |chain|
23 | chain.add Rollbar::Sidekiq
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/lib/rollbar/middleware/rack/builder.rb:
--------------------------------------------------------------------------------
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
22 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 | #
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | if defined?(wrap_parameters)
9 | wrap_parameters :format => [:json]
10 | end
11 | end
12 |
13 | # Disable root element in JSON by default.
14 | ActiveSupport.on_load(:active_record) do
15 | self.include_root_in_json = false
16 | end
17 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format
4 | # (all these examples are active by default):
5 | # ActiveSupport::Inflector.inflections do |inflect|
6 | # inflect.plural /^(ox)$/i, '\1en'
7 | # inflect.singular /^(ox)en/i, '\1'
8 | # inflect.irregular 'person', 'people'
9 | # inflect.uncountable %w( fish sheep )
10 | # end
11 | #
12 | # These inflection rules are supported but not enabled by default:
13 | # ActiveSupport::Inflector.inflections do |inflect|
14 | # inflect.acronym 'RESTful'
15 | # end
16 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/views/layouts/_navigation.html.erb:
--------------------------------------------------------------------------------
1 | <%= link_to "Dummyapp", root_path, :class => 'brand' %>
2 |
3 | <% if user_signed_in? %>
4 | -
5 | <%= link_to 'Logout', destroy_user_session_path, :method=>'delete' %>
6 |
7 | <% else %>
8 | -
9 | <%= link_to 'Login', new_user_session_path %>
10 |
11 | <% end %>
12 | <% if user_signed_in? %>
13 | -
14 | <%= link_to 'Edit account', edit_user_registration_path %>
15 |
16 | <% else %>
17 | -
18 | <%= link_to 'Sign up', new_user_registration_path %>
19 |
20 | <% end %>
21 |
--------------------------------------------------------------------------------
/lib/rollbar/middleware/rack/test_session.rb:
--------------------------------------------------------------------------------
1 | module Rollbar
2 | module Middleware
3 | module Rack
4 | module TestSession
5 | include ExceptionReporter
6 |
7 | def env_for_with_rollbar(path, env)
8 | env_for_without_rollbar(path, 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, :env_for_without_rollbar, :env_for)
16 | base.send(:alias_method, :env_for, :env_for_with_rollbar)
17 | end
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/dummyapp/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
7 | # Mayor.create(name: 'Emanuel', city: cities.first)
8 | puts 'SETTING UP DEFAULT USER LOGIN'
9 | user = User.create! :name => 'First User', :email => 'user@example.com'
10 | puts 'New user created: ' << user.name
11 | user2 = User.create! :name => 'Second User', :email => 'user2@example.com'
12 | puts 'New user created: ' << user2.name
13 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/views/devise/registrations/new.html.erb:
--------------------------------------------------------------------------------
1 | Sign up
2 |
3 | <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
4 | <%= devise_error_messages! %>
5 | <%= f.label :name %>
6 | <%= f.text_field :name %>
7 |
8 | <%= f.label :email %>
9 | <%= f.email_field :email %>
10 |
11 | <%= f.label :password %>
12 | <%= f.password_field :password %>
13 |
14 | <%= f.label :password_confirmation %>
15 | <%= f.password_field :password_confirmation %>
16 |
17 | <%= f.submit "Sign up" %>
18 | <% end %>
19 |
20 | <%= render "devise/shared/links" %>
21 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | development:
7 | adapter: sqlite3
8 | database: db/development.sqlite3
9 | pool: 5
10 | timeout: 5000
11 |
12 | # Warning: The database defined as "test" will be erased and
13 | # re-generated from your development database when you run "rake".
14 | # Do not set this db to the same as development or production.
15 | test:
16 | adapter: sqlite3
17 | database: db/test.sqlite3
18 | pool: 5
19 | timeout: 5000
20 |
21 | production:
22 | adapter: sqlite3
23 | database: db/production.sqlite3
24 | pool: 5
25 | timeout: 5000
26 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/controllers/home_controller.rb:
--------------------------------------------------------------------------------
1 | class HomeController < ApplicationController
2 | def index
3 | @users = User.all
4 |
5 | Rollbar.report_message("Test message from controller with no data", "debug")
6 | Rollbar.report_message("Test message from controller with extra data", "debug",
7 | :foo => "bar", :num_users => @users.length)
8 | end
9 |
10 | def report_exception
11 | begin
12 | foo = bar
13 | rescue => e
14 | Rollbar.report_exception(e, rollbar_request_data, rollbar_person_data)
15 | end
16 | end
17 |
18 | def cause_exception
19 | foo = bar
20 | end
21 |
22 | def current_user
23 | User.find_by_encrypted_password(cookies[:session_id])
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/lib/rollbar/delayed_job.rb:
--------------------------------------------------------------------------------
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
26 |
--------------------------------------------------------------------------------
/spec/dummyapp/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
We're sorry, but something went wrong.
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/spec/dummyapp/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The change you wanted was rejected.
23 |
Maybe you tried to change something you didn't have access to.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/spec/dummyapp/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The page you were looking for doesn't exist.
23 |
You may have mistyped the address or the page may have moved.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/rollbar/rails.rb:
--------------------------------------------------------------------------------
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
23 |
--------------------------------------------------------------------------------
/spec/generators/rollbar/rollbar_generator_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe :rollbar do
4 | context "with no arguments" do
5 | it "outputs a help message" do
6 | subject.should output(/You'll need to add an environment variable ROLLBAR_ACCESS_TOKEN with your access token/)
7 | end
8 |
9 | it "generates a Rollbar initializer with ENV" do
10 | subject.should generate("config/initializers/rollbar.rb") { |content|
11 | content.should =~ /config.access_token = ENV\['ROLLBAR_ACCESS_TOKEN'\]/
12 | }
13 | end
14 | end
15 |
16 | with_args 'aaaabbbbccccddddeeeeffff00001111' do
17 | it "generates a Rollbar initializer with access token" do
18 | subject.should generate("config/initializers/rollbar.rb") do |content|
19 | content.should =~ /aaaabbbbccccddddeeeeffff00001111/
20 | content.should =~ /config.access_token = 'aaaabbbbccccddddeeeeffff00001111'/
21 | end
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= content_for?(:title) ? yield(:title) : "Dummyapp" %>
6 | ">
7 | <%= stylesheet_link_tag "application", :media => "all" %>
8 | <%= javascript_include_tag "application" %>
9 | <%= csrf_meta_tags %>
10 | <%= yield(:head) %>
11 |
12 |
13 |
14 |
15 | <%= render 'layouts/navigation' %>
16 | <%= render 'layouts/messages' %>
17 |
18 |
19 | <%= yield %>
20 |
21 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/UPGRADE_FROM_RATCHET.md:
--------------------------------------------------------------------------------
1 | # Upgrading from ratchetio-gem
2 |
3 | ## Required immediate steps
4 |
5 | Add this line to your application's Gemfile:
6 |
7 | gem 'rollbar', '~> 0.10.3'
8 |
9 | And remove:
10 |
11 | gem 'ratchetio'
12 |
13 | Then execute:
14 |
15 | $ bundle install
16 |
17 | Next, rename your `config/initializers/ratchetio.rb` to `config/initializers/rollbar.rb`
18 |
19 | Open `config/initializers/rollbar.rb` and change `require 'ratchetio/rails'` to `require 'rollbar/rails'`
20 |
21 | At this point the new Rollbar library should be properly integrated and begin to report exceptions to Rollbar.
22 |
23 | ## Optional steps
24 |
25 | These are not required because aliases have been set up from the Ratchetio module/functions to the respective Rollbar versions.
26 |
27 | Replace all instances of `Ratchetio` in your rails app with `Rollbar`.
28 |
29 | Replace all instances of `ratchetio_request_data` with `rollbar_request_data`.
30 |
31 | Replace all instances of `ratchetio_person_data` with `rollbar_person_data`.
32 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 |
3 | ENV['RAILS_ENV'] = 'test'
4 | require File.expand_path('../dummyapp/config/environment', __FILE__)
5 | require 'rspec/rails'
6 | require 'database_cleaner'
7 | require 'genspec'
8 |
9 | namespace :dummy do
10 | load 'spec/dummyapp/Rakefile'
11 | end
12 |
13 | Rake::Task['dummy:db:setup'].invoke
14 |
15 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
16 |
17 | RSpec.configure do |config|
18 | config.color_enabled = true
19 | config.formatter = 'documentation'
20 |
21 | config.use_transactional_fixtures = true
22 | config.order = "random"
23 |
24 | config.before(:suite) do
25 | DatabaseCleaner.strategy = :truncation
26 | end
27 |
28 | config.before(:each) do
29 | DatabaseCleaner.start
30 | DatabaseCleaner.clean
31 | end
32 |
33 | config.after(:each) do
34 | DatabaseCleaner.clean
35 | end
36 | end
37 |
38 | def reset_configuration
39 | Rollbar.reconfigure do |config|
40 | end
41 | end
42 |
43 | def local?
44 | ENV['LOCAL'] == '1'
45 | end
46 |
--------------------------------------------------------------------------------
/THANKS.md:
--------------------------------------------------------------------------------
1 | # Contributors
2 |
3 | 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
4 |
5 | - [arr-ee](https://github.com/arr-ee)
6 | - [awmichel](https://github.com/awmichel)
7 | - [dimko](https://github.com/dimko)
8 | - [dlackty](https://github.com/dlackty)
9 | - [fabsays](https://github.com/fabsays)
10 | - [firstbanco](https://github.com/firstbanco)
11 | - [ixti](https://github.com/ixti)
12 | - [jeremyvdw](https://github.com/jeremyvdw)
13 | - [johnknott](https://github.com/johnknott)
14 | - [JoshuaOSHickman](https://github.com/JoshuaOSHickman)
15 | - [juggler](https://github.com/juggler)
16 | - [kavu](https://github.com/kavu)
17 | - [magnolia-fan](https://github.com/magnolia-fan)
18 | - [mipearson](https://github.com/mipearson)
19 | - [rogercampos](https://github.com/rogercampos)
20 | - [siong1987](https://github.com/siong1987)
21 | - [trisweb](https://github.com/trisweb)
22 | - [tysontate](https://github.com/tysontate)
23 | - [wbond](https://github.com/wbond)
24 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/assets/stylesheets/application.css.scss:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the top of the
9 | * compiled file, but it's generally better to create a new file per style scope.
10 | *
11 | *= require_self
12 | *= require_tree .
13 | */
14 | .brand {
15 | float: left;
16 | padding-right: 8px;
17 | }
18 | ul.nav {
19 | list-style: none;
20 | margin: 0 0 2em;
21 | padding: 0;
22 | }
23 | ul.nav li {
24 | display: inline;
25 | }
26 | #flash_notice, #flash_alert {
27 | padding: 5px 8px;
28 | margin: 10px 0;
29 | }
30 | #flash_notice {
31 | background-color: #CFC;
32 | border: solid 1px #6C6;
33 | }
34 | #flash_alert {
35 | background-color: #FCC;
36 | border: solid 1px #C66;
37 | }
38 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/initializers/rollbar.rb:
--------------------------------------------------------------------------------
1 | require 'rollbar/rails'
2 | Rollbar.configure do |config|
3 | config.access_token = 'aaaabbbbccccddddeeeeffff00001111'
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 | end
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
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.
23 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/views/devise/registrations/edit.html.erb:
--------------------------------------------------------------------------------
1 | Edit <%= resource_name.to_s.humanize %>
2 |
3 | <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
4 | <%= devise_error_messages! %>
5 | <%= f.label :name %>
6 | <%= f.text_field :name %>
7 |
8 | <%= f.label :email %>
9 | <%= f.email_field :email %>
10 |
11 | <%= f.label :password %> (leave blank if you don't want to change it)
12 | <%= f.password_field :password, :autocomplete => "off" %>
13 |
14 | <%= f.label :password_confirmation %>
15 | <%= f.password_field :password_confirmation %>
16 |
17 | <%= f.label :current_password %> (we need your current password to confirm your changes)
18 | <%= f.password_field :current_password %>
19 |
20 | <%= f.submit "Update" %>
21 | <% end %>
22 |
23 | Cancel my account
24 |
25 | Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete %>.
26 |
27 | <%= link_to "Back", :back %>
28 |
--------------------------------------------------------------------------------
/lib/rollbar/rails/controller_methods.rb:
--------------------------------------------------------------------------------
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 | # for backwards compatabilty with the old ratchetio-gem
27 | def ratchetio_person_data
28 | rollbar_person_data
29 | end
30 |
31 | # for backwards compatabilty with the old ratchetio-gem
32 | def ratchetio_request_data
33 | rollbar_request_data
34 | end
35 |
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/lib/rollbar/middleware/rails/show_exceptions.rb:
--------------------------------------------------------------------------------
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 | key = 'action_dispatch.show_detailed_exceptions'
9 |
10 | # don't report production exceptions here as it is done below
11 | # in call_with_rollbar() when show_detailed_exception is false
12 | if not env.has_key?(key) or env[key]
13 | report_exception_to_rollbar(env, exception)
14 | end
15 | render_exception_without_rollbar(env, exception)
16 | end
17 |
18 | def call_with_rollbar(env)
19 | call_without_rollbar(env)
20 | rescue => exception
21 | # won't reach here if show_detailed_exceptions is true
22 | report_exception_to_rollbar(env, exception)
23 | raise exception
24 | end
25 |
26 | def self.included(base)
27 | base.send(:alias_method_chain, :call, :rollbar)
28 | base.send(:alias_method_chain, :render_exception, :rollbar)
29 | end
30 | end
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/spec/dummyapp/app/views/devise/shared/_links.html.erb:
--------------------------------------------------------------------------------
1 | <%- if controller_name != 'sessions' %>
2 | <%= link_to "Sign in", new_session_path(resource_name) %>
3 | <% end -%>
4 |
5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end -%>
8 |
9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' %>
10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
11 | <% end -%>
12 |
13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
15 | <% end -%>
16 |
17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
19 | <% end -%>
20 |
21 | <%- if devise_mapping.omniauthable? %>
22 | <%- resource_class.omniauth_providers.each do |provider| %>
23 | <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %>
24 | <% end -%>
25 | <% end -%>
--------------------------------------------------------------------------------
/spec/delay/sidekiq_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | begin
4 | require 'rollbar/delay/sidekiq'
5 | rescue LoadError
6 | end
7 |
8 | if defined?(Sidekiq)
9 | describe Rollbar::Delay::Sidekiq do
10 | before(:each) do
11 | reset_configuration
12 | end
13 |
14 | describe ".handler" do
15 | let(:payload) { anything }
16 |
17 | context "with default options" do
18 | before { Rollbar.configuration.use_sidekiq = true }
19 |
20 | it "enqueues to default queue" do
21 | ::Sidekiq::Client.should_receive(:push).with(Rollbar::Delay::Sidekiq::OPTIONS.merge('args' => payload))
22 | Rollbar::Delay::Sidekiq.handle(payload)
23 | end
24 | end
25 |
26 | context "with custom options" do
27 | let(:custom_config) { { 'queue' => 'custom_queue' } }
28 |
29 | before { Rollbar.configuration.use_sidekiq = custom_config }
30 |
31 | it "enqueues to custom queue" do
32 | options = Rollbar::Delay::Sidekiq::OPTIONS.merge(custom_config.merge('args' => payload))
33 | ::Sidekiq::Client.should_receive(:push).with options
34 |
35 | Rollbar::Delay::Sidekiq.handle(payload)
36 | end
37 | end
38 | end
39 | end
40 | end
--------------------------------------------------------------------------------
/rollbar.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | require File.expand_path('../lib/rollbar/version', __FILE__)
3 |
4 | Gem::Specification.new do |gem|
5 | gem.authors = ["Brian Rue"]
6 | gem.email = ["brian@rollbar.com"]
7 | gem.description = %q{Rails plugin to catch and send exceptions to Rollbar}
8 | gem.summary = %q{Reports exceptions to Rollbar}
9 | gem.homepage = "https://github.com/rollbar/rollbar-gem"
10 | gem.license = 'MIT'
11 |
12 | gem.files = `git ls-files`.split($\)
13 | gem.test_files = gem.files.grep(%r{^(spec)/})
14 | gem.name = "rollbar"
15 | gem.require_paths = ["lib"]
16 | gem.version = Rollbar::VERSION
17 |
18 | gem.add_runtime_dependency 'multi_json', '~> 1.5'
19 |
20 | gem.add_development_dependency 'rails', '>= 3.0.0'
21 | gem.add_development_dependency 'rspec-rails', '~> 2.12.0'
22 | gem.add_development_dependency 'database_cleaner', '~> 1.0.0'
23 | gem.add_development_dependency 'girl_friday', '>= 0.11.1'
24 | gem.add_development_dependency 'sucker_punch', '>= 1.0.0' if RUBY_VERSION != '1.8.7'
25 | gem.add_development_dependency 'sidekiq', '>= 2.13.0' if RUBY_VERSION != '1.8.7'
26 | gem.add_development_dependency 'genspec', '>= 0.2.7'
27 | end
28 |
--------------------------------------------------------------------------------
/lib/rollbar/exception_reporter.rb:
--------------------------------------------------------------------------------
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 |
11 | if exception_data.is_a?(Hash)
12 | env['rollbar.exception_uuid'] = exception_data[:uuid]
13 | rollbar_debug "[Rollbar] Exception uuid saved in env: #{exception_data[:uuid]}"
14 | elsif exception_data == 'disabled'
15 | rollbar_debug "[Rollbar] Exception not reported because Rollbar is disabled"
16 | elsif exception_data == 'ignored'
17 | rollbar_debug "[Rollbar] Exception not reported because it was ignored"
18 | end
19 | rescue => e
20 | rollbar_debug "[Rollbar] Exception while reporting exception to Rollbar: #{e.try(:message)}"
21 | end
22 |
23 | def rollbar_debug(message, level = :debug)
24 | if defined?(Rails)
25 | ::Rails.logger.send(level, message)
26 | else
27 | puts message
28 | end
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/lib/rollbar/railtie.rb:
--------------------------------------------------------------------------------
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 |
38 |
--------------------------------------------------------------------------------
/lib/rollbar/goalie.rb:
--------------------------------------------------------------------------------
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.is_a?(Hash)
22 | env['rollbar.exception_uuid'] = exception_data[:uuid]
23 | puts "[Rollbar] Exception uuid saved in env: #{exception_data[:uuid]}"
24 | elsif exception_data == 'disabled'
25 | puts "[Rollbar] Exception not reported because Rollbar is disabled"
26 | elsif exception_data == 'ignored'
27 | puts "[Rollbar] Exception not reported because it was ignored"
28 | end
29 |
30 | # now continue as normal
31 | orig_render_exception(env, exception)
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/lib/rollbar/better_errors.rb:
--------------------------------------------------------------------------------
1 | require 'better_errors'
2 |
3 | module BetterErrors
4 | class Middleware
5 | alias_method :orig_show_error_page, :show_error_page
6 |
7 | private
8 |
9 | def show_error_page(env)
10 | exception = @error_page.exception
11 |
12 | exception_data = nil
13 | begin
14 | controller = env['action_controller.instance']
15 | request_data = controller.try(:rollbar_request_data)
16 | person_data = controller.try(:rollbar_person_data)
17 | exception_data = Rollbar.report_exception(exception, request_data, person_data)
18 | rescue => e
19 | # TODO use logger here?
20 | puts "[Rollbar] Exception while reporting exception to Rollbar: #{e}"
21 | end
22 |
23 | # if an exception was reported, save uuid in the env
24 | # so it can be displayed to the user on the error page
25 | if exception_data.is_a?(Hash)
26 | env['rollbar.exception_uuid'] = exception_data[:uuid]
27 | puts "[Rollbar] Exception uuid saved in env: #{exception_data[:uuid]}"
28 | elsif exception_data == 'disabled'
29 | puts "[Rollbar] Exception not reported because Rollbar is disabled"
30 | elsif exception_data == 'ignored'
31 | puts "[Rollbar] Exception not reported because it was ignored"
32 | end
33 |
34 | # now continue as normal
35 | orig_show_error_page(env)
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Log error messages when you accidentally call methods on nil.
10 | config.whiny_nils = true
11 |
12 | # Show full error reports and disable caching
13 | config.consider_all_requests_local = true
14 | config.action_controller.perform_caching = false
15 |
16 | # Don't care if the mailer can't send
17 | config.action_mailer.raise_delivery_errors = false
18 |
19 | # Print deprecation notices to the Rails logger
20 | config.active_support.deprecation = :log
21 |
22 | # Only use best-standards-support built into browsers
23 | config.action_dispatch.best_standards_support = :builtin
24 |
25 | # Raise exception on mass assignment protection for Active Record models
26 | #config.active_record.mass_assignment_sanitizer = :strict
27 |
28 | # Log the query plan for queries taking more than this (works
29 | # with SQLite, MySQL, and PostgreSQL)
30 | #config.active_record.auto_explain_threshold_in_seconds = 0.5
31 |
32 | # Do not compress assets
33 | #config.assets.compress = false
34 |
35 | # Expands the lines which load the assets
36 | #config.assets.debug = true
37 | end
38 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Configure static asset server for tests with Cache-Control for performance
11 | config.serve_static_assets = true
12 | config.static_cache_control = "public, max-age=3600"
13 |
14 | # Log error messages when you accidentally call methods on nil
15 | config.whiny_nils = true
16 |
17 | # Show full error reports and disable caching
18 | config.consider_all_requests_local = ENV['LOCAL'] == '1'
19 | config.action_controller.perform_caching = false
20 |
21 | # Raise exceptions instead of rendering exception templates
22 | config.action_dispatch.show_exceptions = false
23 |
24 | # Disable request forgery protection in test environment
25 | config.action_controller.allow_forgery_protection = false
26 |
27 | # Tell Action Mailer not to deliver emails to the real world.
28 | # The :test delivery method accumulates sent emails in the
29 | # ActionMailer::Base.deliveries array.
30 | config.action_mailer.delivery_method = :test
31 |
32 | # Raise exception on mass assignment protection for Active Record models
33 | #config.active_record.mass_assignment_sanitizer = :strict
34 |
35 | # Print deprecation notices to the stderr
36 | config.active_support.deprecation = :stderr
37 | end
38 |
--------------------------------------------------------------------------------
/spec/dummyapp/db/migrate/20121121184652_devise_create_users.rb:
--------------------------------------------------------------------------------
1 | class DeviseCreateUsers < ActiveRecord::Migration
2 | def change
3 | create_table(:users) do |t|
4 | ## Database authenticatable
5 | t.string :email, :null => false, :default => ""
6 | t.string :encrypted_password, :null => false, :default => ""
7 |
8 | ## Recoverable
9 | t.string :reset_password_token
10 | t.datetime :reset_password_sent_at
11 |
12 | ## Rememberable
13 | t.datetime :remember_created_at
14 |
15 | ## Trackable
16 | t.integer :sign_in_count, :default => 0
17 | t.datetime :current_sign_in_at
18 | t.datetime :last_sign_in_at
19 | t.string :current_sign_in_ip
20 | t.string :last_sign_in_ip
21 |
22 | ## Confirmable
23 | # t.string :confirmation_token
24 | # t.datetime :confirmed_at
25 | # t.datetime :confirmation_sent_at
26 | # t.string :unconfirmed_email # Only if using reconfirmable
27 |
28 | ## Lockable
29 | # t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts
30 | # t.string :unlock_token # Only if unlock strategy is :email or :both
31 | # t.datetime :locked_at
32 |
33 | ## Token authenticatable
34 | # t.string :authentication_token
35 |
36 |
37 | t.timestamps
38 | end
39 |
40 | add_index :users, :email, :unique => true
41 | add_index :users, :reset_password_token, :unique => true
42 | # add_index :users, :confirmation_token, :unique => true
43 | # add_index :users, :unlock_token, :unique => true
44 | # add_index :users, :authentication_token, :unique => true
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/spec/dummyapp/.gitignore:
--------------------------------------------------------------------------------
1 | #----------------------------------------------------------------------------
2 | # Ignore these files when commiting to a git repository.
3 | #
4 | # See http://help.github.com/ignore-files/ for more about ignoring files.
5 | #
6 | # The original version of this file is found here:
7 | # https://github.com/RailsApps/rails-composer/blob/master/files/gitignore.txt
8 | #
9 | # Corrections? Improvements? Create a GitHub issue:
10 | # http://github.com/RailsApps/rails-composer/issues
11 | #----------------------------------------------------------------------------
12 |
13 | # bundler state
14 | /.bundle
15 | /vendor/bundle/
16 | /vendor/ruby/
17 |
18 | # minimal Rails specific artifacts
19 | db/*.sqlite3
20 | /log/*
21 | /tmp/*
22 |
23 | # various artifacts
24 | **.war
25 | *.rbc
26 | *.sassc
27 | .rspec
28 | .redcar/
29 | .sass-cache
30 | /config/config.yml
31 | /coverage.data
32 | /coverage/
33 | /db/*.javadb/
34 | /db/*.sqlite3
35 | /doc/api/
36 | /doc/app/
37 | /doc/features.html
38 | /doc/specs.html
39 | /public/cache
40 | /public/stylesheets/compiled
41 | /public/system/*
42 | /spec/tmp/*
43 | /cache
44 | /capybara*
45 | /capybara-*.html
46 | /gems
47 | /specifications
48 | rerun.txt
49 | pickle-email-*.html
50 |
51 | # If you find yourself ignoring temporary files generated by your text editor
52 | # or operating system, you probably want to add a global ignore instead:
53 | # git config --global core.excludesfile ~/.gitignore_global
54 | #
55 | # Here are some files you may want to ignore globally:
56 |
57 | # scm revert files
58 | **.orig
59 |
60 | # Mac finder artifacts
61 | .DS_Store
62 |
63 | # Netbeans project directory
64 | /nbproject/
65 |
66 | # RubyMine project files
67 | .idea
68 |
69 | # Textmate project files
70 | /*.tmproj
71 |
72 | # vim artifacts
73 | **.swp
74 |
--------------------------------------------------------------------------------
/spec/dummyapp/db/schema.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | # This file is auto-generated from the current state of the database. Instead
3 | # of editing this file, please use the migrations feature of Active Record to
4 | # incrementally modify your database, and then regenerate this schema definition.
5 | #
6 | # Note that this schema.rb definition is the authoritative source for your
7 | # database schema. If you need to create the application database on another
8 | # system, you should be using db:schema:load, not running all the migrations
9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 | # you'll amass, the slower it'll run and the greater likelihood for issues).
11 | #
12 | # It's strongly recommended to check this file into your version control system.
13 |
14 | ActiveRecord::Schema.define(:version => 20121121184654) do
15 |
16 | create_table "users", :force => true do |t|
17 | t.string "email", :default => "", :null => false
18 | t.string "encrypted_password", :default => "", :null => false
19 | t.string "reset_password_token"
20 | t.datetime "reset_password_sent_at"
21 | t.datetime "remember_created_at"
22 | t.integer "sign_in_count", :default => 0
23 | t.datetime "current_sign_in_at"
24 | t.datetime "last_sign_in_at"
25 | t.string "current_sign_in_ip"
26 | t.string "last_sign_in_ip"
27 | t.datetime "created_at", :null => false
28 | t.datetime "updated_at", :null => false
29 | t.string "name"
30 | end
31 |
32 | add_index "users", ["email"], :name => "index_users_on_email", :unique => true
33 | add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
34 |
35 | end
36 |
--------------------------------------------------------------------------------
/lib/rollbar/capistrano.rb:
--------------------------------------------------------------------------------
1 | require 'capistrano'
2 |
3 | module Rollbar
4 | module Capistrano
5 | def self.load_into(configuration)
6 | configuration.load do
7 | after 'deploy', 'rollbar:deploy'
8 | after 'deploy:migrations', 'rollbar:deploy'
9 | after 'deploy:cold', 'rollbar:deploy'
10 |
11 | namespace :rollbar do
12 | desc 'Send the deployment notification to Rollbar.'
13 | task :deploy, :except => { :no_release => true } do
14 | require 'net/http'
15 | require 'rubygems'
16 | require 'json'
17 |
18 | _cset(:rollbar_user) { ENV['USER'] || ENV['USERNAME'] }
19 | _cset(:rollbar_env) { fetch(:rails_env, 'production') }
20 | _cset(:rollbar_token) { abort("Please specify the Rollbar access token, set :rollbar_token, 'your token'") }
21 |
22 | unless configuration.dry_run
23 | uri = URI.parse('https://api.rollbar.com/api/1/deploy/')
24 |
25 | params = {
26 | :local_username => rollbar_user,
27 | :access_token => rollbar_token,
28 | :environment => rollbar_env,
29 | :revision => current_revision
30 | }
31 |
32 | request = Net::HTTP::Post.new(uri.request_uri)
33 | request.body = JSON.dump(params)
34 |
35 | Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
36 | http.request(request)
37 | end
38 | end
39 |
40 | logger.info('Rollbar notification complete')
41 | end
42 | end
43 | end
44 | end
45 | end
46 | end
47 |
48 | if Capistrano::Configuration.instance
49 | Rollbar::Capistrano.load_into(Capistrano::Configuration.instance)
50 | end
51 |
--------------------------------------------------------------------------------
/lib/generators/rollbar/rollbar_generator.rb:
--------------------------------------------------------------------------------
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', :default => :use_env_sentinel
8 |
9 | source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
10 |
11 | def create_initializer
12 | say "creating initializer..."
13 | if access_token_configured?
14 | say "It looks like you've already configured Rollbar."
15 | say "To re-create the config file, remove it first: config/initializers/rollbar.rb"
16 | exit
17 | end
18 |
19 | if access_token === :use_env_sentinel
20 | say "Generator run without an access token; assuming you want to configure using an environment variable."
21 | say "You'll need to add an environment variable ROLLBAR_ACCESS_TOKEN with your access token:"
22 | say "\n$ export ROLLBAR_ACCESS_TOKEN=yourtokenhere"
23 | say "\nIf that's not what you wanted to do:"
24 | say "\n$ rm config/initializers/rollbar.rb"
25 | say "$ rails generate rollbar yourtokenhere"
26 | say "\n"
27 | else
28 | say "access token: " << access_token
29 | end
30 |
31 | template 'initializer.rb', 'config/initializers/rollbar.rb',
32 | :assigns => { :access_token => access_token_expr }
33 |
34 | # TODO run rake test task
35 | end
36 |
37 | def access_token_expr
38 | if access_token === :use_env_sentinel
39 | "ENV['ROLLBAR_ACCESS_TOKEN']"
40 | else
41 | "'#{access_token}'"
42 | end
43 | end
44 |
45 | def access_token_configured?
46 | File.exists?('config/initializers/rollbar.rb')
47 | end
48 | end
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm:
3 | - 1.8.7
4 | - 1.9.2
5 | - 1.9.3
6 | - 2.0.0
7 | - ruby-head
8 | - jruby-18mode
9 | - jruby-19mode
10 | - jruby-head
11 | - rbx-18mode
12 | - rbx-19mode
13 | matrix:
14 | allow_failures:
15 | - rvm: ruby-head
16 | - rvm: jruby-18mode
17 | - rvm: jruby-19mode
18 | - rvm: jruby-head
19 | - rvm: rbx-18mode
20 | - rvm: rbx-19mode
21 | exclude:
22 | - rvm: 1.8.7
23 | gemfile: gemfiles/rails40.gemfile
24 | - rvm: 1.9.2
25 | gemfile: gemfiles/rails40.gemfile
26 | - rvm: ruby-head
27 | gemfile: gemfiles/rails30.gemfile
28 | - rvm: ruby-head
29 | gemfile: gemfiles/rails31.gemfile
30 | - rvm: ruby-head
31 | gemfile: gemfiles/rails40.gemfile
32 | - rvm: jruby-18mode
33 | gemfile: gemfiles/rails30.gemfile
34 | - rvm: jruby-18mode
35 | gemfile: gemfiles/rails31.gemfile
36 | - rvm: jruby-18mode
37 | gemfile: gemfiles/rails40.gemfile
38 | - rvm: jruby-19mode
39 | gemfile: gemfiles/rails30.gemfile
40 | - rvm: jruby-19mode
41 | gemfile: gemfiles/rails31.gemfile
42 | - rvm: jruby-19mode
43 | gemfile: gemfiles/rails40.gemfile
44 | - rvm: jruby-head
45 | gemfile: gemfiles/rails30.gemfile
46 | - rvm: jruby-head
47 | gemfile: gemfiles/rails31.gemfile
48 | - rvm: jruby-head
49 | gemfile: gemfiles/rails40.gemfile
50 | - rvm: rbx-18mode
51 | gemfile: gemfiles/rails30.gemfile
52 | - rvm: rbx-18mode
53 | gemfile: gemfiles/rails31.gemfile
54 | - rvm: rbx-18mode
55 | gemfile: gemfiles/rails40.gemfile
56 | - rvm: rbx-19mode
57 | gemfile: gemfiles/rails30.gemfile
58 | - rvm: rbx-19mode
59 | gemfile: gemfiles/rails31.gemfile
60 | - rvm: rbx-19mode
61 | gemfile: gemfiles/rails40.gemfile
62 | gemfile:
63 | - gemfiles/rails30.gemfile
64 | - gemfiles/rails31.gemfile
65 | - gemfiles/rails32.gemfile
66 | - gemfiles/rails40.gemfile
67 |
--------------------------------------------------------------------------------
/spec/requests/home_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe HomeController do
4 | let(:logger_mock) { double("Rails.logger").as_null_object }
5 |
6 | before(:each) do
7 | reset_configuration
8 | Rollbar.configure do |config|
9 | config.access_token = 'aaaabbbbccccddddeeeeffff00001111'
10 | config.environment = ::Rails.env
11 | config.root = ::Rails.root
12 | config.framework = "Rails: #{::Rails::VERSION::STRING}"
13 | config.logger = logger_mock
14 | end
15 | end
16 |
17 | context "with broken request" do
18 | it "should report uncaught exceptions" do
19 | # only seems to be relevant in 3.1 and 3.2
20 | if ::Rails::VERSION::STRING.starts_with? "3.1" or ::Rails::VERSION::STRING.starts_with? "3.2"
21 | expect{ get 'current_user', nil, :cookie => '8%B' }.to raise_exception
22 |
23 | Rollbar.last_report.should_not be_nil
24 |
25 | exception_info = Rollbar.last_report[:body][:trace][:exception]
26 | exception_info[:class].should == 'ArgumentError'
27 | exception_info[:message].should == 'invalid %-encoding (8%B)'
28 | end
29 | end
30 | end
31 |
32 | context "with error hiding deep inside" do
33 | let!(:cookie_method_name){ :[] }
34 | let!(:original_cookie_method){ ActionDispatch::Cookies::CookieJar.instance_method(cookie_method_name) }
35 | let!(:broken_cookie_method){ Proc.new{ |name| "1" - 1 } }
36 |
37 | before(:each) do
38 | ActionDispatch::Cookies::CookieJar.send(:define_method, cookie_method_name, broken_cookie_method)
39 | end
40 |
41 | after(:each) do
42 | ActionDispatch::Cookies::CookieJar.send(:define_method, cookie_method_name, original_cookie_method)
43 | end
44 |
45 | it "should report uncaught exceptions" do
46 | expect{ get 'current_user' }.to raise_exception
47 |
48 | exception_info = Rollbar.last_report[:body][:trace][:exception]
49 | exception_info[:class].should == 'NoMethodError'
50 | # exception_info[:message].should == 'undefined method `-\' for "1":String'
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/lib/generators/rollbar/templates/initializer.rb:
--------------------------------------------------------------------------------
1 | require 'rollbar/rails'
2 | Rollbar.configure do |config|
3 | config.access_token = <%= access_token_expr %>
4 |
5 | # Without configuration, Rollbar is enabled by in all environments.
6 | # To disable in specific environments, set config.enabled=false.
7 | # Here we'll disable in 'test':
8 | if Rails.env.test?
9 | config.enabled = false
10 | end
11 |
12 | # By default, Rollbar will try to call the `current_user` controller method
13 | # to fetch the logged-in user object, and then call that object's `id`,
14 | # `username`, and `email` methods to fetch those properties. To customize:
15 | # config.person_method = "my_current_user"
16 | # config.person_id_method = "my_id"
17 | # config.person_username_method = "my_username"
18 | # config.person_email_method = "my_email"
19 |
20 | # If you want to attach custom data to all exception and message reports,
21 | # provide a lambda like the following. It should return a hash.
22 | # config.custom_data_method = lambda { {:some_key => "some_value" } }
23 |
24 | # Add exception class names to the exception_level_filters hash to
25 | # change the level that exception is reported at. Note that if an exception
26 | # has already been reported and logged the level will need to be changed
27 | # via the rollbar interface.
28 | # Valid levels: 'critical', 'error', 'warning', 'info', 'debug', 'ignore'
29 | # 'ignore' will cause the exception to not be reported at all.
30 | # config.exception_level_filters.merge!('MyCriticalException' => 'critical')
31 |
32 | # Enable asynchronous reporting (uses girl_friday or Threading if girl_friday
33 | # is not installed)
34 | # config.use_async = true
35 | # Supply your own async handler:
36 | # config.async_handler = Proc.new { |payload|
37 | # Thread.new { Rollbar.process_payload(payload) }
38 | # }
39 |
40 | # Enable asynchronous reporting (using sucker_punch)
41 | # config.use_sucker_punch = true
42 |
43 | # Enable delayed reporting (using Sidekiq)
44 | # config.use_sidekiq = true
45 | # You can supply custom Sidekiq options:
46 | # config.use_sidekiq = { 'queue' => 'my_queue' }
47 | end
48 |
--------------------------------------------------------------------------------
/lib/rollbar/rake_tasks.rb:
--------------------------------------------------------------------------------
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 | get 'verify' => 'application#verify', :as => 'verify'
53 | end
54 |
55 | puts "Processing..."
56 | env = Rack::MockRequest.env_for("/verify")
57 | status, headers, response = Rails.application.call(env)
58 |
59 | unless status == 500
60 | puts "Test failed! You may have a configuration issue, or you could be using a gem that's blocking the test. Contact support@rollbar.com if you need help troubleshooting."
61 | end
62 | end
63 | end
64 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # Code is not reloaded between requests
5 | config.cache_classes = true
6 |
7 | # Full error reports are disabled and caching is turned on
8 | config.consider_all_requests_local = false
9 | config.action_controller.perform_caching = true
10 |
11 | # Disable Rails's static asset server (Apache or nginx will already do this)
12 | config.serve_static_assets = false
13 |
14 | # Compress JavaScripts and CSS
15 | config.assets.compress = true
16 |
17 | # Don't fallback to assets pipeline if a precompiled asset is missed
18 | config.assets.compile = false
19 |
20 | # Generate digests for assets URLs
21 | config.assets.digest = true
22 |
23 | # Defaults to nil and saved in location specified by config.assets.prefix
24 | # config.assets.manifest = YOUR_PATH
25 |
26 | # Specifies the header that your server uses for sending files
27 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
28 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
29 |
30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
31 | # config.force_ssl = true
32 |
33 | # See everything in the log (default is :info)
34 | # config.log_level = :debug
35 |
36 | # Prepend all log lines with the following tags
37 | # config.log_tags = [ :subdomain, :uuid ]
38 |
39 | # Use a different logger for distributed setups
40 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
41 |
42 | # Use a different cache store in production
43 | # config.cache_store = :mem_cache_store
44 |
45 | # Enable serving of images, stylesheets, and JavaScripts from an asset server
46 | # config.action_controller.asset_host = "http://assets.example.com"
47 |
48 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
49 | # config.assets.precompile += %w( search.js )
50 |
51 | # Disable delivery errors, bad email addresses will be ignored
52 | # config.action_mailer.raise_delivery_errors = false
53 |
54 | # Enable threaded mode
55 | # config.threadsafe!
56 |
57 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
58 | # the I18n.default_locale when a translation can not be found)
59 | config.i18n.fallbacks = true
60 |
61 | # Send deprecation notices to registered listeners
62 | config.active_support.deprecation = :notify
63 |
64 | # Log the query plan for queries taking more than this (works
65 | # with SQLite, MySQL, and PostgreSQL)
66 | # config.active_record.auto_explain_threshold_in_seconds = 0.5
67 | end
68 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | require "rails/all"
4 |
5 | Bundler.require
6 | require "rollbar"
7 |
8 | module Dummy
9 | class Application < Rails::Application
10 | # Settings in config/environments/* take precedence over those specified here.
11 | # Application configuration should go into files in config/initializers
12 | # -- all .rb files in that directory are automatically loaded.
13 |
14 | # Custom directories with classes and modules you want to be autoloadable.
15 | # config.autoload_paths += %W(#{config.root}/extras)
16 |
17 | # Only load the plugins named here, in the order given (default is alphabetical).
18 | # :all can be used as a placeholder for all plugins not explicitly named.
19 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
20 |
21 | # Activate observers that should always be running.
22 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
23 |
24 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
25 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
26 | # config.time_zone = 'Central Time (US & Canada)'
27 |
28 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
29 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
30 | # config.i18n.default_locale = :de
31 |
32 | # Configure the default encoding used in templates for Ruby 1.9.
33 | config.encoding = "utf-8"
34 |
35 | # Configure sensitive parameters which will be filtered from the log file.
36 | config.filter_parameters += [:password]
37 |
38 | # Enable escaping HTML in JSON.
39 | config.active_support.escape_html_entities_in_json = true
40 |
41 | # Use SQL instead of Active Record's schema dumper when creating the database.
42 | # This is necessary if your schema can't be completely dumped by the schema dumper,
43 | # like if you have constraints or database-specific column types
44 | # config.active_record.schema_format = :sql
45 |
46 | # Enforce whitelist mode for mass assignment.
47 | # This will create an empty whitelist of attributes available for mass-assignment for all models
48 | # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
49 | # parameters by using an attr_accessible or attr_protected declaration.
50 | #config.active_record.whitelist_attributes = true
51 |
52 | # Enable the asset pipeline
53 | #config.assets.enabled = true
54 |
55 | # Version of your assets, change this if you want to expire all your assets
56 | #config.assets.version = '1.0'
57 | end
58 | end
59 |
60 |
--------------------------------------------------------------------------------
/lib/rollbar/configuration.rb:
--------------------------------------------------------------------------------
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 :custom_data_method
10 | attr_accessor :default_logger
11 | attr_accessor :enabled
12 | attr_accessor :endpoint
13 | attr_accessor :environment
14 | attr_accessor :exception_level_filters
15 | attr_accessor :filepath
16 | attr_accessor :framework
17 | attr_accessor :logger
18 | attr_accessor :person_method
19 | attr_accessor :person_id_method
20 | attr_accessor :person_username_method
21 | attr_accessor :person_email_method
22 | attr_accessor :root
23 | attr_accessor :scrub_fields
24 | attr_accessor :use_sidekiq
25 | attr_accessor :use_sucker_punch
26 | attr_accessor :use_async
27 | attr_accessor :use_eventmachine
28 | attr_accessor :web_base
29 | attr_accessor :write_to_file
30 |
31 | attr_reader :project_gem_paths
32 |
33 | DEFAULT_ENDPOINT = 'https://api.rollbar.com/api/1/item/'
34 | DEFAULT_WEB_BASE = 'https://rollbar.com'
35 |
36 | def initialize
37 | @async_handler = nil
38 | @custom_data_method = nil
39 | @default_logger = lambda { Logger.new(STDERR) }
40 | @enabled = nil # set to true when configure is called
41 | @endpoint = DEFAULT_ENDPOINT
42 | @environment = 'production'
43 | @exception_level_filters = {
44 | 'ActiveRecord::RecordNotFound' => 'warning',
45 | 'AbstractController::ActionNotFound' => 'warning',
46 | 'ActionController::RoutingError' => 'warning'
47 | }
48 | @framework = 'Plain'
49 | @person_method = 'current_user'
50 | @person_id_method = 'id'
51 | @person_username_method = 'username'
52 | @person_email_method = 'email'
53 | @project_gems = []
54 | @scrub_fields = [:passwd, :password, :password_confirmation, :secret,
55 | :confirm_password, :password_confirmation, :secret_token]
56 | @use_async = false
57 | @use_sidekiq = false
58 | @use_sucker_punch = false
59 | @use_eventmachine = false
60 | @web_base = DEFAULT_WEB_BASE
61 | @write_to_file = false
62 | end
63 |
64 | def use_sidekiq=(value)
65 | if value
66 | require 'rollbar/delay/sidekiq' if defined?(Sidekiq)
67 | @use_async = true
68 | @use_sidekiq = value
69 | @async_handler = Rollbar::Delay::Sidekiq.method(:handle)
70 | end
71 | end
72 |
73 | def use_sucker_punch=(value)
74 | if value
75 | require 'rollbar/delay/sucker_punch' if defined?(SuckerPunch)
76 | @use_async = true
77 | @async_handler = Rollbar::Delay::SuckerPunch.method(:handle)
78 | end
79 | end
80 |
81 | def use_eventmachine=(value)
82 | require 'em-http-request' if value
83 | @use_eventmachine = value
84 | end
85 |
86 | def project_gems=(gems)
87 | @project_gem_paths = []
88 |
89 | gems.each { |name|
90 | begin
91 | spec = Gem::Specification.find_by_name(name.to_s)
92 | gem_root = spec.gem_dir
93 |
94 | @project_gem_paths.push gem_root
95 | rescue Gem::LoadError
96 | puts "[Rollbar] #{name} gem not found"
97 | end
98 | }
99 | end
100 |
101 | # allow params to be read like a hash
102 | def [](option)
103 | send(option)
104 | end
105 | end
106 | end
107 |
--------------------------------------------------------------------------------
/spec/dummyapp/config/locales/devise.en.yml:
--------------------------------------------------------------------------------
1 | # Additional translations at https://github.com/plataformatec/devise/wiki/I18n
2 |
3 | en:
4 | errors:
5 | messages:
6 | expired: "has expired, please request a new one"
7 | not_found: "not found"
8 | already_confirmed: "was already confirmed, please try signing in"
9 | not_locked: "was not locked"
10 | not_saved:
11 | one: "1 error prohibited this %{resource} from being saved:"
12 | other: "%{count} errors prohibited this %{resource} from being saved:"
13 |
14 | devise:
15 | failure:
16 | already_authenticated: 'You are already signed in.'
17 | unauthenticated: 'You need to sign in or sign up before continuing.'
18 | unconfirmed: 'You have to confirm your account before continuing.'
19 | locked: 'Your account is locked.'
20 | invalid: 'Invalid email or password.'
21 | invalid_token: 'Invalid authentication token.'
22 | timeout: 'Your session expired, please sign in again to continue.'
23 | inactive: 'Your account was not activated yet.'
24 | sessions:
25 | signed_in: 'Signed in successfully.'
26 | signed_out: 'Signed out successfully.'
27 | passwords:
28 | send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.'
29 | updated: 'Your password was changed successfully. You are now signed in.'
30 | updated_not_active: 'Your password was changed successfully.'
31 | send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
32 | no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided."
33 | confirmations:
34 | send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.'
35 | send_paranoid_instructions: 'If your email address exists in our database, you will receive an email with instructions about how to confirm your account in a few minutes.'
36 | confirmed: 'Your account was successfully confirmed. You are now signed in.'
37 | registrations:
38 | signed_up: 'Welcome! You have signed up successfully.'
39 | signed_up_but_unconfirmed: 'A message with a confirmation link has been sent to your email address. Please open the link to activate your account.'
40 | signed_up_but_inactive: 'You have signed up successfully. However, we could not sign you in because your account is not yet activated.'
41 | signed_up_but_locked: 'You have signed up successfully. However, we could not sign you in because your account is locked.'
42 | updated: 'You updated your account successfully.'
43 | update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and click on the confirm link to finalize confirming your new email address."
44 | destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.'
45 | unlocks:
46 | send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
47 | unlocked: 'Your account has been unlocked successfully. Please sign in to continue.'
48 | send_paranoid_instructions: 'If your account exists, you will receive an email with instructions about how to unlock it in a few minutes.'
49 | omniauth_callbacks:
50 | success: 'Successfully authenticated from %{kind} account.'
51 | failure: 'Could not authenticate you from %{kind} because "%{reason}".'
52 | mailer:
53 | confirmation_instructions:
54 | subject: 'Confirmation instructions'
55 | reset_password_instructions:
56 | subject: 'Reset password instructions'
57 | unlock_instructions:
58 | subject: 'Unlock Instructions'
59 |
--------------------------------------------------------------------------------
/lib/rollbar/request_data_extractor.rb:
--------------------------------------------------------------------------------
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 | rack_req = Rack::Request.new(env)
15 |
16 | sensitive_params = sensitive_params_list(env)
17 | request_params = rollbar_filtered_params(sensitive_params, rollbar_request_params(env))
18 | get_params = rollbar_filtered_params(sensitive_params, rollbar_get_params(rack_req))
19 | post_params = rollbar_filtered_params(sensitive_params, rollbar_post_params(rack_req))
20 | cookies = rollbar_filtered_params(sensitive_params, rollbar_request_cookies(rack_req))
21 | session = rollbar_filtered_params(sensitive_params, env['rack.session.options'])
22 |
23 | params = request_params.merge(get_params).merge(post_params)
24 |
25 | {
26 | :params => params,
27 | :url => rollbar_url(env),
28 | :user_ip => rollbar_user_ip(env),
29 | :headers => rollbar_headers(env),
30 | :cookies => cookies,
31 | :session => session,
32 | :method => rollbar_request_method(env)
33 | }
34 | end
35 |
36 | private
37 |
38 | def rollbar_request_method(env)
39 | env['REQUEST_METHOD'] || env[:method]
40 | end
41 |
42 | def rollbar_headers(env)
43 | env.keys.grep(/^HTTP_/).map do |header|
44 | name = header.gsub(/^HTTP_/, '').split('_').map(&:capitalize).join('-')
45 | # exclude cookies - we already include a parsed version as request_data[:cookies]
46 | if name == 'Cookie'
47 | {}
48 | else
49 | { name => env[header] }
50 | end
51 | end.inject(:merge)
52 | end
53 |
54 | def rollbar_url(env)
55 | scheme = env['rack.url_scheme']
56 | host = env['HTTP_HOST'] || env['SERVER_NAME']
57 | path = env['ORIGINAL_FULLPATH'] || env['REQUEST_URI']
58 | unless path.nil? || path.empty?
59 | path = '/' + path.to_s if path.to_s.slice(0, 1) != '/'
60 | end
61 |
62 | [scheme, '://', host, path].join
63 | end
64 |
65 | def rollbar_user_ip(env)
66 | (env['action_dispatch.remote_ip'] || env['HTTP_X_REAL_IP'] || env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_ADDR']).to_s
67 | end
68 |
69 | def rollbar_get_params(rack_req)
70 | rack_req.GET
71 | rescue
72 | {}
73 | end
74 |
75 | def rollbar_post_params(rack_req)
76 | rack_req.POST
77 | rescue
78 | {}
79 | end
80 |
81 | def rollbar_request_params(env)
82 | route = ::Rails.application.routes.recognize_path(env['PATH_INFO']) rescue {}
83 | {
84 | :controller => route[:controller],
85 | :action => route[:action],
86 | :format => route[:format],
87 | }.merge(env['action_dispatch.request.parameters'] || {})
88 | end
89 |
90 | def rollbar_request_cookies(rack_req)
91 | rack_req.cookies
92 | rescue
93 | {}
94 | end
95 |
96 | def rollbar_filtered_params(sensitive_params, params)
97 | if params.nil?
98 | {}
99 | else
100 | params.to_hash.inject({}) do |result, (key, value)|
101 | if sensitive_params.include?(key.to_sym)
102 | result[key] = '*' * (value.length rescue 8)
103 | elsif value.is_a?(Hash)
104 | result[key] = rollbar_filtered_params(sensitive_params, value)
105 | elsif ATTACHMENT_CLASSES.include?(value.class.name)
106 | result[key] = {
107 | :content_type => value.content_type,
108 | :original_filename => value.original_filename,
109 | :size => value.tempfile.size
110 | } rescue 'Uploaded file'
111 | else
112 | result[key] = value
113 | end
114 | result
115 | end
116 | end
117 | end
118 |
119 | def sensitive_params_list(env)
120 | Rollbar.configuration.scrub_fields |= Array(env['action_dispatch.parameter_filter'])
121 | end
122 | end
123 | end
124 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | **0.10.12**
4 | - Exclude HTTP_COOKIE header (since cookies are already included in parsed form)
5 |
6 | **0.10.11**
7 | - Fix usage of custom Sidekiq options
8 |
9 | **0.10.10**
10 | - Add support for sucker_punch asynchronous handling
11 |
12 | **0.10.9**
13 | - Fix regression introduced in 0.10.7 when ActiveRecord is not present.
14 |
15 | **0.10.8**
16 | - Better handling of internal errors. Internal errors (errors that occur while reporting something to Rollbar) are now themselves reported to Rollbar. If that fails, a failsafe message will be reported, and if that fails, the error will be logged as it is now.
17 | - Fix bug reporting exceptions with backtraces containing frames that don't match our regex.
18 |
19 | **0.10.7**
20 | - Add ability to report form validation errors
21 | - Add MIT license to gemspec
22 |
23 | **0.10.6**
24 | - Fix json dump when rack.errors is an IO stream
25 |
26 | **0.10.5**
27 | - Add built-in support for Sidekiq as async handler
28 |
29 | **0.10.4**
30 | - Fix exception in the exception catcher when Rollbar is disabled
31 |
32 | **0.10.3**
33 | - Rework how request params are extracted so that json params are properly extracted in rails 4.0
34 | - Fix rollbar:test rake task
35 |
36 | **0.10.2**
37 | - Require hooks at configuration time instead of gem load time
38 |
39 | **0.10.1**
40 | - Fix regression in 0.10.0 reporting exceptions in development environments and rails < 3.2 apps.
41 |
42 | **0.10.0**
43 | - Fixed bug causing duplicate reports when used inside Rails in production with the default error pages. Bumping version to 0.10.0 in case this turns out to be backwards-incompatible for some use cases (i.e. for applications that were relying on the duplicate report that has now been removed).
44 |
45 | **0.9.14**
46 | - Added `custom_data_method` config option. If set, it should be a lambda that returns a hash.
47 | - Changed initializer template to disable reporting from the 'test' environment.
48 |
49 | **0.9.13**
50 | - Add test for PUT params
51 | - Parse json params when content-type is application/json
52 | - Fix concurrency issue
53 | - Remove redundant `GET` and `POST` keys from request payload (they're already included in `params`)
54 |
55 | **0.9.12**
56 | - Fix compatibility issue with Rails 4 / Ruby 2 (thanks [johnknott](https://github.com/johnknott))
57 |
58 | **0.9.11**
59 | - Provide a default environment name when used outside of Rails and none is set
60 |
61 | **0.9.10**
62 | - Add :secret_token to default scrub_fields list
63 | - Session params are now scrubbed
64 |
65 | **0.9.9**
66 | - Fix capistrano recipe on 1.9.2 ([#36](https://github.com/rollbar/rollbar-gem/pull/36))
67 | - Add example of disable "test" env to initializer template
68 |
69 | **0.9.8**
70 | - Fix bug introduced in 0.9.0 where setting `config.enabled = false` in `config/initializers/rollbar.rb` would be overwritten by subsequent calls to `Rollbar.configure` (as happens normally when using inside Rails).
71 |
72 | **0.9.7**
73 | - Use `include?` instead of `in?` for filtering (see [#34](https://github.com/rollbar/rollbar-gem/pull/34))
74 |
75 | **0.9.6**
76 | - Fix for Rails 4 support
77 |
78 | **0.9.5**
79 | - Support for configuring the access token with an environment variable.
80 |
81 | **0.9.4**
82 | - Fixed issue using rollbar-gem outside of Rails
83 | - Clarified the "details: " link log message
84 |
85 | **0.9.3**
86 | - Added configuration setting to specify gems that should be considered part of the Rollbar project, making frames from these gems show up automatically uncollapsed in tracebacks appearing on the website.
87 |
88 | **0.9.2**
89 | - Added [Capistrano integration](https://github.com/rollbar/rollbar-gem/pull/27)
90 |
91 | **0.9.1**
92 | - Add support to play nicely with Better Errors.
93 |
94 | **0.9.0**
95 | - Behavior change: start configuration as `@enabled = false`, and set to true when `configure` is called. This addresses an issue using Rollbar without the environment initialized. Such reports would always fail (since there would be no access token), but now they won't be attempted.
96 |
97 | **0.8.3**
98 | - Relax multi_json dependency to 1.5.0
99 |
100 | **0.8.2**
101 | - Adding back rake task exception reporting after fixing load order issue
102 |
103 | **0.8.1**
104 | - Reverting rake task exception reporting until we can track down a load order issue reported by a few users
105 |
106 | **0.8.0**
107 | - Rename to rollbar
108 |
109 | **0.7.1**
110 | - Fix ratchetio:test rake task when project base controller is not called ApplicationController
111 |
112 | **0.7.0**
113 | - Exceptions in Rake tasks are now automatically reported.
114 |
115 | **0.6.4**
116 | - Bump multi_json dependency version to 1.6.0
117 |
118 | **0.6.3**
119 | - Bump multi_json dependency version to 1.5.1
120 |
121 | **0.6.2**
122 | - Added EventMachine support
123 |
124 | **0.6.1**
125 | - Added a log message containing a link to the instance. Copy-paste the link into your browser to view its details in Ratchet.
126 | - 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.
127 |
128 | **0.6.0**
129 | - 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.
130 | - Request data is now parsed from the rack environment instead of from within the controller, addressing issue #10.
131 | - Add Sidekiq middleware for catching workers' exceptions
132 | - Replaced activesupport dependency with multi_json
133 |
134 | **0.5.5**
135 | - Added activesupport dependency for use without Rails
136 |
137 | **0.5.4**
138 | - Added new default scrub params
139 |
140 | **0.5.3**
141 | - Add `Ratchetio.silenced`; which allows disabling reporting for a given block. See README for usage.
142 |
143 | **0.5.2**
144 | - 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.
145 |
146 | **0.5.1**
147 | - Save the exception uuid in `env['ratchetio.exception_uuid']` for display in user-facing error pages.
148 |
149 | **0.5.0**
150 | - Add support to report exceptions raised in delayed_job.
151 |
152 | **0.4.11**
153 | - Allow exceptions with no backtrace (e.g. StandardError subclasses)
154 |
155 | **0.4.10**
156 | - Fix compatability issue with ruby 1.8
157 |
158 | **0.4.9**
159 | - Start including a UUID in reported exceptions
160 | - Fix issue with scrub_fields, and add `:password_confirmation` to the default list
161 |
162 | **0.4.8**
163 | - Add ability to send reports asynchronously, using girl_friday or Threading by default.
164 | - Add ability to save reports to a file (for use with ratchet-agent) instead of sending across to Ratchet servers.
165 |
166 | **0.4.7**
167 | - Sensitive params now scrubbed out of requests. Param name list is customizable via the `scrub_fields` config option.
168 |
169 | **0.4.6**
170 | - Add support to play nicely with Goalie.
171 |
172 | **0.4.5**
173 | - 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) }`
174 |
175 | **0.4.4**
176 | - Add `enabled` runtime config flag. When `false`, no data (messages or exceptions) will be reported.
177 |
178 | **0.4.3**
179 | - Add RSpec test suite. A few minor code changes.
180 |
181 | **0.4.2**
182 | - Add "ignore" filter level to completely ignore exceptions by class.
183 |
184 | **0.4.1**
185 | - Recursively filter files out of the params hash. Thanks to [trisweb](https://github.com/trisweb) for the pull request.
186 |
187 | **0.4.0**
188 |
189 | - 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`:
190 |
191 | config.person_method = "current_member"
192 |
193 | - Person id, username, and email method names are now configurable -- see README for details.
194 |
--------------------------------------------------------------------------------
/spec/controllers/home_controller_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe HomeController do
4 | let(:logger_mock) { double("Rails.logger").as_null_object }
5 |
6 | before(:each) do
7 | reset_configuration
8 | Rollbar::Rails.initialize
9 | Rollbar.configure do |config|
10 | config.access_token = 'aaaabbbbccccddddeeeeffff00001111'
11 | config.logger = logger_mock
12 | end
13 | end
14 |
15 | context "rollbar base_data" do
16 | it 'should have the Rails environment' do
17 | data = Rollbar.send(:base_data)
18 | data[:environment].should == ::Rails.env
19 | end
20 |
21 | it 'should have an overridden environment' do
22 | Rollbar.configure do |config|
23 | config.environment = 'dev'
24 | end
25 |
26 | data = Rollbar.send(:base_data)
27 | data[:environment].should == 'dev'
28 | end
29 | end
30 |
31 | context "rollbar controller methods with %s requests" % (local? ? 'local' : 'non-local') do
32 | # TODO run these for a a more-real request
33 | it "should build valid request data" do
34 | data = @controller.rollbar_request_data
35 | data.should have_key(:params)
36 | data.should have_key(:url)
37 | data.should have_key(:user_ip)
38 | data.should have_key(:headers)
39 | data.should have_key(:session)
40 | data.should have_key(:method)
41 | end
42 |
43 | it "should build empty person data when no one is logged-in" do
44 | data = @controller.rollbar_person_data
45 | data.should == {}
46 | end
47 |
48 | context "rollbar_filter_params" do
49 | it "should filter files" do
50 | name = "John Doe"
51 | file_hash = {
52 | :filename => "test.txt",
53 | :type => "text/plain",
54 | :head => {},
55 | :tempfile => "dummy"
56 | }
57 | file = ActionDispatch::Http::UploadedFile.new(file_hash)
58 |
59 | params = {
60 | :name => name,
61 | :a_file => file
62 | }
63 |
64 | filtered = controller.send(:rollbar_filtered_params, Rollbar.configuration.scrub_fields, params)
65 |
66 | filtered[:name].should == name
67 | filtered[:a_file].should be_a_kind_of(Hash)
68 | filtered[:a_file][:content_type].should == file_hash[:type]
69 | filtered[:a_file][:original_filename].should == file_hash[:filename]
70 | filtered[:a_file][:size].should == file_hash[:tempfile].size
71 | end
72 |
73 | it "should filter files in nested params" do
74 | name = "John Doe"
75 | file_hash = {
76 | :filename => "test.txt",
77 | :type => "text/plain",
78 | :head => {},
79 | :tempfile => "dummy"
80 | }
81 | file = ActionDispatch::Http::UploadedFile.new(file_hash)
82 |
83 | params = {
84 | :name => name,
85 | :wrapper => {
86 | :wrapper2 => {
87 | :a_file => file,
88 | :foo => "bar"
89 | }
90 | }
91 | }
92 |
93 | filtered = controller.send(:rollbar_filtered_params, Rollbar.configuration.scrub_fields, params)
94 |
95 | filtered[:name].should == name
96 | filtered[:wrapper][:wrapper2][:foo].should == "bar"
97 |
98 | filtered_file = filtered[:wrapper][:wrapper2][:a_file]
99 | filtered_file.should be_a_kind_of(Hash)
100 | filtered_file[:content_type].should == file_hash[:type]
101 | filtered_file[:original_filename].should == file_hash[:filename]
102 | filtered_file[:size].should == file_hash[:tempfile].size
103 | end
104 |
105 | it "should scrub the default scrub_fields" do
106 | params = {
107 | :passwd => "hidden",
108 | :password => "hidden",
109 | :secret => "hidden",
110 | :notpass => "visible",
111 | :secret_token => "f6805fea1cae0fb79c5e63bbdcd12bc6",
112 | }
113 |
114 | filtered = controller.send(:rollbar_filtered_params, Rollbar.configuration.scrub_fields, params)
115 |
116 | filtered[:passwd].should == "******"
117 | filtered[:password].should == "******"
118 | filtered[:secret].should == "******"
119 | filtered[:notpass].should == "visible"
120 | filtered[:secret_token].should == "*" * 32
121 | end
122 |
123 | it "should scrub custom scrub_fields" do
124 | Rollbar.configure do |config|
125 | config.scrub_fields = [:notpass, :secret]
126 | end
127 |
128 | params = {
129 | :passwd => "visible",
130 | :password => "visible",
131 | :secret => "hidden",
132 | :notpass => "hidden"
133 | }
134 |
135 | filtered = controller.send(:rollbar_filtered_params, Rollbar.configuration.scrub_fields, params)
136 |
137 | filtered[:passwd].should == "visible"
138 | filtered[:password].should == "visible"
139 | filtered[:secret].should == "******"
140 | filtered[:notpass].should == "******"
141 | end
142 | end
143 |
144 | context "rollbar_request_url" do
145 | it "should build simple http urls" do
146 | req = controller.request
147 | req.host = 'rollbar.com'
148 |
149 | controller.send(:rollbar_request_data)[:url].should == 'http://rollbar.com'
150 | end
151 | end
152 |
153 | context "rollbar_user_ip" do
154 | it "should use X-Real-Ip when set" do
155 | controller.request.env["HTTP_X_REAL_IP"] = '1.1.1.1'
156 | controller.request.env["HTTP_X_FORWARDED_FOR"] = '1.2.3.4'
157 | controller.send(:rollbar_request_data)[:user_ip].should == '1.1.1.1'
158 | end
159 |
160 | it "should use X-Forwarded-For when set" do
161 | controller.request.env["HTTP_X_FORWARDED_FOR"] = '1.2.3.4'
162 | controller.send(:rollbar_request_data)[:user_ip].should == '1.2.3.4'
163 | end
164 |
165 | it "should use the remote_addr when neither is set" do
166 | controller.send(:rollbar_request_data)[:user_ip].should == '0.0.0.0'
167 | end
168 | end
169 |
170 | end
171 |
172 | describe "GET 'index'" do
173 | it "should be successful and report two messages" do
174 | logger_mock.should_receive(:info).with('[Rollbar] Success').twice
175 | get 'index'
176 | response.should be_success
177 | end
178 | end
179 |
180 | describe "'report_exception'", :type => "request" do
181 | it "should raise a NameError and report an exception after a GET" do
182 | logger_mock.should_receive(:info).with('[Rollbar] Success').once
183 |
184 | get 'report_exception'
185 | response.should be_success
186 | end
187 |
188 | it "should raise a NameError and have PUT params in the reported exception" do
189 | logger_mock.should_receive(:info).with('[Rollbar] Success')
190 |
191 | put 'report_exception', :putparam => "putval"
192 |
193 | Rollbar.last_report.should_not be_nil
194 | Rollbar.last_report[:request][:params]["putparam"].should == "putval"
195 | end
196 |
197 | it "should raise a NameError and have JSON POST params" do
198 | logger_mock.should_receive(:info).with('[Rollbar] Success')
199 | @request.env["HTTP_ACCEPT"] = "application/json"
200 |
201 | params = {:jsonparam => 'jsonval'}.to_json
202 | post 'report_exception', params, {'CONTENT_TYPE' => 'application/json'}
203 |
204 | Rollbar.last_report.should_not be_nil
205 | Rollbar.last_report[:request][:params]['jsonparam'].should == 'jsonval'
206 | end
207 | end
208 |
209 | describe "'cause_exception'", :type => "request" do
210 | it "should raise an uncaught exception and report a message" do
211 | logger_mock.should_receive(:info).with('[Rollbar] Success').once
212 |
213 | expect { get 'cause_exception' }.to raise_exception
214 | end
215 |
216 | context 'show_exceptions' do
217 | before(:each) do
218 | if Dummy::Application.respond_to? :env_config
219 | config = Dummy::Application.env_config
220 | else
221 | config = Dummy::Application.env_defaults
222 | end
223 |
224 | config['action_dispatch.show_exceptions'] = true
225 | end
226 |
227 | after(:each) do
228 | if Dummy::Application.respond_to? :env_config
229 | config = Dummy::Application.env_config
230 | else
231 | config = Dummy::Application.env_defaults
232 | end
233 |
234 | config['action_dispatch.show_exceptions'] = false
235 | end
236 |
237 | it "middleware should catch the exception and only report to rollbar once" do
238 | logger_mock.should_receive(:info).with('[Rollbar] Success').once
239 |
240 | get 'cause_exception'
241 | end
242 | end
243 | end
244 |
245 | after(:each) do
246 | Rollbar.configure do |config|
247 | config.logger = ::Rails.logger
248 | end
249 | end
250 |
251 | end
252 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rollbar notifier for Ruby [](https://travis-ci.org/rollbar/rollbar-gem)
2 |
3 | Ruby gem for reporting exceptions, errors, and log messages to [Rollbar](https://rollbar.com).
4 |
5 |
6 |
7 | ## Installation
8 |
9 | Add this line to your application's Gemfile:
10 |
11 | gem 'rollbar'
12 |
13 | And then execute:
14 |
15 | ```bash
16 | $ bundle install
17 | ```
18 |
19 | Or install it yourself as:
20 |
21 | ```bash
22 | $ gem install rollbar
23 | ```
24 |
25 | Then, run the following command from your rails root:
26 |
27 | ```bash
28 | $ rails generate rollbar POST_SERVER_ITEM_ACCESS_TOKEN
29 | ```
30 |
31 | That will create the file ```config/initializers/rollbar.rb```, which holds the configuration values (currently just your access token). Make sure you're using the ```post_server_item``` access token.
32 |
33 | If you want to store your access token outside of your repo, run the same command without arguments:
34 |
35 | ```bash
36 | $ rails generate rollbar
37 | ```
38 |
39 | Then, create an environment variable ```ROLLBAR_ACCESS_TOKEN``` and set it to your server-side access token.
40 |
41 | ```bash
42 | $ export ROLLBAR_ACCESS_TOKEN=POST_SERVER_ITEM_ACCESS_TOKEN
43 | ```
44 |
45 | ### For Heroku users
46 |
47 | ```bash
48 | $ heroku config:add ROLLBAR_ACCESS_TOKEN=POST_SERVER_ITEM_ACCESS_TOKEN
49 | ```
50 |
51 | That's all you need to use Rollbar with Rails.
52 |
53 | ## Test your installation
54 |
55 | To confirm that it worked, run:
56 |
57 | ```bash
58 | $ rake rollbar:test
59 | ```
60 |
61 | 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.
62 |
63 | ## Reporting form validation errors
64 |
65 | To get form validation errors automatically reported to Rollbar just add the following ```after_validation``` callback to your models:
66 |
67 | ```ruby
68 | after_validation :report_validation_errors_to_rollbar
69 | ```
70 |
71 | ## Manually reporting exceptions and messages
72 |
73 | To report a caught exception to Rollbar, simply call ```Rollbar.report_exception```:
74 |
75 | ```ruby
76 | begin
77 | foo = bar
78 | rescue Exception => e
79 | Rollbar.report_exception(e)
80 | end
81 | ```
82 |
83 | 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:
84 |
85 | ```ruby
86 | begin
87 | foo = bar
88 | rescue Exception => e
89 | Rollbar.report_exception(e, rollbar_request_data, rollbar_person_data)
90 | end
91 | ```
92 |
93 | You can also log individual messages:
94 |
95 | ```ruby
96 | # logs at the 'warning' level. all levels: debug, info, warning, error, critical
97 | Rollbar.report_message("Unexpected input", "warning")
98 |
99 | # default level is "info"
100 | Rollbar.report_message("Login successful")
101 |
102 | # can also include additional data as a hash in the final param. :body is reserved.
103 | Rollbar.report_message("Login successful", "info", :user => @user)
104 | ```
105 |
106 | ## Data sanitization (scrubbing)
107 |
108 | By default, the notifier will "scrub" the following fields from requests before sending to Rollbar
109 |
110 | - ```:passwd```
111 | - ```:password```
112 | - ```:password_confirmation```
113 | - ```:secret```
114 | - ```:confirm_password```
115 | - ```:password_confirmation```
116 | - ```:secret_token```
117 |
118 | If a request contains one of these fields, the value will be replaced with a ```"*"``` before being sent.
119 |
120 | Additional fields can be scrubbed by updating ```Rollbar.configuration.scrub_fields```:
121 |
122 | ```ruby
123 | # scrub out the "user_password" field
124 | Rollbar.configuration.scrub_fields |= [:user_password]
125 | ```
126 |
127 | ## Person tracking
128 |
129 | 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.
130 |
131 | If the gem should call a controller method besides ```current_user```, add the following in ```config/initializers/rollbar.rb```:
132 |
133 | ```ruby
134 | config.person_method = "my_current_user"
135 | ```
136 |
137 | 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```:
138 |
139 | ```ruby
140 | config.person_id_method = "user_id" # default is "id"
141 | config.person_username_method = "user_name" # default is "username"
142 | config.person_email_method = "email_address" # default is "email"
143 | ```
144 |
145 | ## Including additional runtime data
146 |
147 | You can provide a lambda that will be called for each exception or message report. ```custom_data_method``` should be a lambda that takes no arguments and returns a hash.
148 |
149 | Add the following in ```config/initializers/rollbar.rb```:
150 |
151 | ```ruby
152 | config.custom_data_method = lambda {
153 | { :some_key => :some_value, :complex_key => {:a => 1, :b => [2, 3, 4]} }
154 | }
155 | ```
156 |
157 | This data will appear in the Occurrences tab and on the Occurrence Detail pages in the Rollbar interface.
158 |
159 | ## Exception level filters
160 |
161 | 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:
162 |
163 | - ```ActiveRecord::RecordNotFound```
164 | - ```AbstractController::ActionNotFound```
165 | - ```ActionController::RoutingError```
166 |
167 | 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.
168 |
169 | ## Silencing exceptions at runtime
170 |
171 | If you just want to disable exception reporting for a single block, use ```Rollbar.silenced```:
172 |
173 | ```ruby
174 | Rollbar.silenced {
175 | foo = bar # will not be reported
176 | }
177 | ```
178 |
179 | ## Asynchronous reporting
180 |
181 | By default, all messages are reported synchronously. You can enable asynchronous reporting with [girl_friday](https://github.com/mperham/girl_friday) or [sucker_punch](https://github.com/brandonhilkert/sucker_punch) or [Sidekiq](https://github.com/mperham/sidekiq).
182 |
183 | ### Using girl_friday
184 |
185 | Add the following in ```config/initializers/rollbar.rb```:
186 |
187 | ```ruby
188 | config.use_async = true
189 | ```
190 |
191 | Asynchronous reporting falls back to Threading if girl_friday is not installed.
192 |
193 | ### Using sucker_punch
194 |
195 | Add the following in ```config/initializers/rollbar.rb```:
196 |
197 | ```ruby
198 | config.use_sucker_punch = true
199 | ```
200 |
201 | ### Using Sidekiq
202 |
203 | Add the following in ```config/initializers/rollbar.rb```:
204 |
205 | ```ruby
206 | config.use_sidekiq = true
207 | ```
208 |
209 | You can also supply custom Sidekiq options:
210 |
211 | ```ruby
212 | config.use_sidekiq = { 'queue' => 'my_queue' }
213 | ```
214 |
215 | Start the redis server:
216 |
217 | ```bash
218 | $ redis-server
219 | ```
220 |
221 | Start Sidekiq from the root directory of your Rails app and declare the name of your queue. Unless you've configured otherwise, the queue name is "rollbar":
222 |
223 | ```bash
224 | $ bundle exec sidekiq -q rollbar
225 | ```
226 |
227 | ### Using another handler
228 |
229 | 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:
230 |
231 | ```ruby
232 | config.async_handler = Proc.new { |payload|
233 | Thread.new { Rollbar.process_payload(payload) }
234 | }
235 | ```
236 |
237 | Make sure you pass ```payload``` to ```Rollbar.process_payload``` in your own implementation.
238 |
239 | ## Using with rollbar-agent
240 |
241 | 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```:
242 |
243 | ```ruby
244 | config.write_to_file = true
245 | # optional, defaults to "#{AppName}.rollbar"
246 | config.filepath = '/path/to/file.rollbar' #should end in '.rollbar' for use with rollbar-agent
247 | ```
248 |
249 | For this to work, you'll also need to set up rollbar-agent--see its docs for details.
250 |
251 | ## Deploy Tracking with Capistrano
252 |
253 | Add the following to ```deploy.rb```:
254 |
255 | ```ruby
256 | require 'rollbar/capistrano'
257 | set :rollbar_token, 'POST_SERVER_ITEM_ACCESS_TOKEN'
258 | ```
259 |
260 | Available options:
261 |
262 |
263 | - rollbar_token
264 | - The same project access token as you used for the ```rails generate rollbar``` command; find it in ```config/initializers/rollbar.rb```. (It's repeated here for performance reasons, so the rails environment doesn't have to be initialized.)
265 |
266 | - rollbar_env
267 | - Deploy environment name
268 |
269 | Default: ```rails_env```
270 |
271 |
272 |
273 |
274 | For ```capistrano/multistage```, try:
275 |
276 | ```ruby
277 | set(:rollbar_env) { stage }
278 | ```
279 |
280 | ## Counting specific gems as in-project code
281 |
282 | In the Rollbar interface, stacktraces are shown with in-project code expanded and other code collapsed. Stack frames are counted as in-project if they occur in a file that is inside of the `configuration.root` (automatically set to ```Rails.root``` if you're using Rails). The collapsed sections can be expanded by clicking on them.
283 |
284 | If you want code from some specific gems to start expanded as well, you can configure this in ```config/initializers/rollbar.rb```:
285 |
286 | ```ruby
287 | Rollbar.configure do |config |
288 | config.access_token = '...'
289 | config.project_gems = ['my_custom_gem', 'my_other_gem']
290 | end
291 | ```
292 |
293 | ## Using with Goalie
294 |
295 | 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.)
296 |
297 |
298 | ## Using with Resque
299 |
300 | Check out [resque-rollbar](https://github.com/CrowdFlower/resque-rollbar) for using Rollbar as a failure backend for Resque.
301 |
302 |
303 | ## Using with Zeus
304 |
305 | Some users have reported problems with Zeus when ```rake``` was not explicitly included in their Gemfile. If the zeus server fails to start after installing the rollbar gem, try explicitly adding ```gem 'rake'``` to your ```Gemfile```. See [this thread](https://github.com/rollbar/rollbar-gem/issues/30) for more information.
306 |
307 |
308 | ## Help / Support
309 |
310 | If you run into any issues, please email us at [support@rollbar.com](mailto:support@rollbar.com)
311 |
312 |
313 | ## Contributing
314 |
315 | 1. Fork it
316 | 2. Create your feature branch (```git checkout -b my-new-feature```)
317 | 3. Commit your changes (```git commit -am 'Added some feature'```)
318 | 4. Push to the branch (```git push origin my-new-feature```)
319 | 5. Create new Pull Request
320 |
321 | 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.)
322 |
--------------------------------------------------------------------------------
/lib/rollbar.rb:
--------------------------------------------------------------------------------
1 | require 'net/https'
2 |
3 | require 'securerandom' if defined?(SecureRandom)
4 | require 'socket'
5 | require 'thread'
6 | require 'uri'
7 |
8 | require 'girl_friday' if defined?(GirlFriday)
9 | require 'sucker_punch' if defined?(SuckerPunch)
10 | require 'multi_json'
11 |
12 | require 'rollbar/version'
13 | require 'rollbar/configuration'
14 | require 'rollbar/request_data_extractor'
15 | require 'rollbar/exception_reporter'
16 | require 'rollbar/active_record_extension' if defined?(ActiveRecord)
17 |
18 | require 'rollbar/railtie' if defined?(Rails)
19 |
20 | module Rollbar
21 | class << self
22 | attr_writer :configuration
23 | attr_reader :last_report
24 |
25 | # Configures the gem.
26 | #
27 | # Call on app startup to set the `access_token` (required) and other config params.
28 | # In a Rails app, this is called by `config/initializers/rollbar.rb` which is generated
29 | # with `rails generate rollbar access-token-here`
30 | #
31 | # @example
32 | # Rollbar.configure do |config|
33 | # config.access_token = 'abcdefg'
34 | # end
35 | def configure
36 | require_hooks
37 |
38 | # if configuration.enabled has not been set yet (is still 'nil'), set to true.
39 | if configuration.enabled.nil?
40 | configuration.enabled = true
41 | end
42 | yield(configuration)
43 | end
44 |
45 | def reconfigure
46 | @configuration = Configuration.new
47 | @configuration.enabled = true
48 | yield(configuration)
49 | end
50 |
51 | def unconfigure
52 | @configuration = nil
53 | end
54 |
55 | # Returns the configuration object.
56 | #
57 | # @return [Rollbar::Configuration] The configuration object
58 | def configuration
59 | @configuration ||= Configuration.new
60 | end
61 |
62 | # Reports an exception to Rollbar. Returns the exception data hash.
63 | #
64 | # @example
65 | # begin
66 | # foo = bar
67 | # rescue => e
68 | # Rollbar.report_exception(e)
69 | # end
70 | #
71 | # @param exception [Exception] The exception object to report
72 | # @param request_data [Hash] Data describing the request. Should be the result of calling
73 | # `rollbar_request_data`.
74 | # @param person_data [Hash] Data describing the affected person. Should be the result of calling
75 | # `rollbar_person_data`
76 | def report_exception(exception, request_data = nil, person_data = nil)
77 | return 'disabled' unless configuration.enabled
78 | return 'ignored' if ignored?(exception)
79 |
80 | data = exception_data(exception, filtered_level(exception))
81 | if request_data
82 | request_data[:env].reject!{|k, v| v.is_a?(IO) } if request_data[:env]
83 | data[:request] = request_data
84 | end
85 | data[:person] = person_data if person_data
86 |
87 | @last_report = data
88 |
89 | payload = build_payload(data)
90 | schedule_payload(payload)
91 | log_instance_link(data)
92 | data
93 | rescue => e
94 | report_internal_error(e)
95 | 'error'
96 | end
97 |
98 | # Reports an arbitrary message to Rollbar
99 | #
100 | # @example
101 | # Rollbar.report_message("User login failed", 'info', :user_id => 123)
102 | #
103 | # @param message [String] The message body. This will be used to identify the message within
104 | # Rollbar. For best results, avoid putting variables in the message body; pass them as
105 | # `extra_data` instead.
106 | # @param level [String] The level. One of: 'critical', 'error', 'warning', 'info', 'debug'
107 | # @param extra_data [Hash] Additional data to include alongside the body. Don't use 'body' as
108 | # it is reserved.
109 | def report_message(message, level = 'info', extra_data = {})
110 | return 'disabled' unless configuration.enabled
111 |
112 | data = message_data(message, level, extra_data)
113 | payload = build_payload(data)
114 | schedule_payload(payload)
115 | log_instance_link(data)
116 | data
117 | rescue => e
118 | report_internal_error(e)
119 | 'error'
120 | end
121 |
122 | # Turns off reporting for the given block.
123 | #
124 | # @example
125 | # Rollbar.silenced { raise }
126 | #
127 | # @yield Block which exceptions won't be reported.
128 | def silenced
129 | begin
130 | yield
131 | rescue => e
132 | e.instance_variable_set(:@_rollbar_do_not_report, true)
133 | raise
134 | end
135 | end
136 |
137 | def process_payload(payload)
138 | begin
139 | if configuration.write_to_file
140 | write_payload(payload)
141 | else
142 | send_payload(payload)
143 | end
144 | rescue => e
145 | log_error "[Rollbar] Error processing payload: #{e}"
146 | end
147 | end
148 |
149 | private
150 |
151 | def require_hooks()
152 | require 'rollbar/delayed_job' if defined?(Delayed) && defined?(Delayed::Plugins)
153 | require 'rollbar/sidekiq' if defined?(Sidekiq)
154 | require 'rollbar/goalie' if defined?(Goalie)
155 | require 'rollbar/rack' if defined?(Rack)
156 | require 'rollbar/rake' if defined?(Rake)
157 | require 'rollbar/better_errors' if defined?(BetterErrors)
158 | end
159 |
160 | def log_instance_link(data)
161 | log_info "[Rollbar] Details: #{configuration.web_base}/instance/uuid?uuid=#{data[:uuid]} (only available if report was successful)"
162 | end
163 |
164 | def ignored?(exception)
165 | if filtered_level(exception) == 'ignore'
166 | return true
167 | end
168 |
169 | if exception.instance_variable_get(:@_rollbar_do_not_report)
170 | return true
171 | end
172 |
173 | false
174 | end
175 |
176 | def filtered_level(exception)
177 | configuration.exception_level_filters[exception.class.name]
178 | end
179 |
180 | def message_data(message, level, extra_data)
181 | data = base_data(level)
182 |
183 | data[:body] = {
184 | :message => {
185 | :body => message.to_s
186 | }
187 | }
188 | data[:body][:message].merge!(extra_data)
189 | data[:server] = server_data
190 |
191 | data
192 | end
193 |
194 | def exception_data(exception, force_level = nil)
195 | data = base_data
196 |
197 | data[:level] = force_level if force_level
198 |
199 | # parse backtrace
200 | if exception.backtrace.respond_to?( :map )
201 | frames = exception.backtrace.map { |frame|
202 | # parse the line
203 | match = frame.match(/(.*):(\d+)(?::in `([^']+)')?/)
204 | if match
205 | { :filename => match[1], :lineno => match[2].to_i, :method => match[3] }
206 | else
207 | { :filename => "", :lineno => 0, :method => frame }
208 | end
209 | }
210 | # reverse so that the order is as rollbar expects
211 | frames.reverse!
212 | else
213 | frames = []
214 | end
215 |
216 | data[:body] = {
217 | :trace => {
218 | :frames => frames,
219 | :exception => {
220 | :class => exception.class.name,
221 | :message => exception.message
222 | }
223 | }
224 | }
225 |
226 | data[:server] = server_data
227 |
228 | data
229 | end
230 |
231 | def logger
232 | # init if not set
233 | unless configuration.logger
234 | configuration.logger = configuration.default_logger.call
235 | end
236 | configuration.logger
237 | end
238 |
239 | def write_payload(payload)
240 | if configuration.use_async
241 | @file_semaphore.synchronize {
242 | do_write_payload(payload)
243 | }
244 | else
245 | do_write_payload(payload)
246 | end
247 | end
248 |
249 | def do_write_payload(payload)
250 | log_info '[Rollbar] Writing payload to file'
251 |
252 | begin
253 | unless @file
254 | @file = File.open(configuration.filepath, "a")
255 | end
256 |
257 | @file.puts payload
258 | @file.flush
259 | log_info "[Rollbar] Success"
260 | rescue IOError => e
261 | log_error "[Rollbar] Error opening/writing to file: #{e}"
262 | end
263 | end
264 |
265 | def send_payload_using_eventmachine(payload)
266 | req = EventMachine::HttpRequest.new(configuration.endpoint).post(:body => payload)
267 | req.callback do
268 | if req.response_header.status == 200
269 | log_info '[Rollbar] Success'
270 | else
271 | log_warning "[Rollbar] Got unexpected status code from Rollbar.io api: #{req.response_header.status}"
272 | log_info "[Rollbar] Response: #{req.response}"
273 | end
274 | end
275 | req.errback do
276 | log_warning "[Rollbar] Call to API failed, status code: #{req.response_header.status}"
277 | log_info "[Rollbar] Error's response: #{req.response}"
278 | end
279 | end
280 |
281 | def send_payload(payload)
282 | log_info '[Rollbar] Sending payload'
283 |
284 | if configuration.use_eventmachine
285 | send_payload_using_eventmachine(payload)
286 | return
287 | end
288 | uri = URI.parse(configuration.endpoint)
289 | http = Net::HTTP.new(uri.host, uri.port)
290 |
291 | if uri.scheme == 'https'
292 | http.use_ssl = true
293 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE
294 | end
295 |
296 | request = Net::HTTP::Post.new(uri.request_uri)
297 | request.body = payload
298 | response = http.request(request)
299 |
300 | if response.code == '200'
301 | log_info '[Rollbar] Success'
302 | else
303 | log_warning "[Rollbar] Got unexpected status code from Rollbar api: #{response.code}"
304 | log_info "[Rollbar] Response: #{response.body}"
305 | end
306 | end
307 |
308 | def schedule_payload(payload)
309 | log_info '[Rollbar] Scheduling payload'
310 |
311 | if configuration.use_async
312 | unless configuration.async_handler
313 | configuration.async_handler = method(:default_async_handler)
314 | end
315 |
316 | if configuration.write_to_file
317 | unless @file_semaphore
318 | @file_semaphore = Mutex.new
319 | end
320 | end
321 |
322 | configuration.async_handler.call(payload)
323 | else
324 | process_payload(payload)
325 | end
326 | end
327 |
328 | def build_payload(data)
329 | payload = {
330 | :access_token => configuration.access_token,
331 | :data => data
332 | }
333 | MultiJson.dump(payload)
334 | end
335 |
336 | def base_data(level = 'error')
337 | config = configuration
338 | data = {
339 | :timestamp => Time.now.to_i,
340 | :environment => config.environment,
341 | :level => level,
342 | :language => 'ruby',
343 | :framework => config.framework,
344 | :project_package_paths => config.project_gem_paths,
345 | :notifier => {
346 | :name => 'rollbar-gem',
347 | :version => VERSION
348 | }
349 | }
350 |
351 | if defined?(SecureRandom) and SecureRandom.respond_to?(:uuid)
352 | data[:uuid] = SecureRandom.uuid
353 | end
354 |
355 | unless config.custom_data_method.nil?
356 | data[:custom] = config.custom_data_method.call
357 | end
358 |
359 | data
360 | end
361 |
362 | def server_data
363 | config = configuration
364 |
365 | data = {
366 | :host => Socket.gethostname
367 | }
368 | data[:root] = config.root.to_s if config.root
369 | data[:branch] = config.branch if config.branch
370 |
371 | data
372 | end
373 |
374 | def default_async_handler(payload)
375 | if defined?(GirlFriday)
376 | unless @queue
377 | @queue = GirlFriday::WorkQueue.new(nil, :size => 5) do |payload|
378 | process_payload(payload)
379 | end
380 | end
381 |
382 | @queue.push(payload)
383 | else
384 | log_warning '[Rollbar] girl_friday not found to handle async call, falling back to Thread'
385 | Thread.new { process_payload(payload) }
386 | end
387 | end
388 |
389 | # wrappers around logger methods
390 | def log_error(message)
391 | begin
392 | logger.error message
393 | rescue => e
394 | puts "[Rollbar] Error logging error:"
395 | puts "[Rollbar] #{message}"
396 | end
397 | end
398 |
399 | def log_info(message)
400 | begin
401 | logger.info message
402 | rescue => e
403 | puts "[Rollbar] Error logging info:"
404 | puts "[Rollbar] #{message}"
405 | end
406 | end
407 |
408 | def log_warning(message)
409 | begin
410 | logger.warn message
411 | rescue => e
412 | puts "[Rollbar] Error logging warning:"
413 | puts "[Rollbar] #{message}"
414 | end
415 | end
416 |
417 | # Reports an internal error in the Rollbar library. This will be reported within the configured
418 | # Rollbar project. We'll first attempt to provide a report including the exception traceback.
419 | # If that fails, we'll fall back to a more static failsafe response.
420 | def report_internal_error(exception)
421 | log_error "[Rollbar] Reporting internal error encountered while sending data to Rollbar."
422 |
423 | begin
424 | data = exception_data(exception, 'error')
425 | rescue => e
426 | send_failsafe("error in exception_data", e)
427 | return
428 | end
429 |
430 | data[:internal] = true
431 |
432 | begin
433 | payload = build_payload(data)
434 | rescue => e
435 | send_failsafe("error in build_payload", e)
436 | return
437 | end
438 |
439 | begin
440 | schedule_payload(payload)
441 | rescue => e
442 | send_failsafe("erorr in schedule_payload", e)
443 | return
444 | end
445 |
446 | begin
447 | log_instance_link(data)
448 | rescue => e
449 | send_failsafe("error logging instance link", e)
450 | return
451 | end
452 | end
453 |
454 | def send_failsafe(message, exception)
455 | log_error "[Rollbar] Sending failsafe response."
456 | log_error "[Rollbar] #{message} #{exception}"
457 |
458 | config = configuration
459 | environment = config.environment
460 |
461 | failsafe_payload = <<-eos
462 | {"access_token": "#{config.access_token}",
463 | "data": {
464 | "level": "error",
465 | "environment": "#{config.environment}",
466 | "body": { "message": { "body": "Failsafe from rollbar-gem: #{message}" } },
467 | "notifier": { "name": "rollbar-gem", "version": "#{VERSION}" },
468 | "internal": true,
469 | "failsafe": true
470 | }
471 | }
472 | eos
473 |
474 | begin
475 | schedule_payload(failsafe_payload)
476 | rescue => e
477 | log_error "[Rollbar] Error sending failsafe : #{e}"
478 | end
479 | end
480 |
481 | end
482 |
483 | end
484 |
485 | # Setting Ratchetio as an alias to Rollbar for ratchetio-gem backwards compatibility
486 | Ratchetio = Rollbar
487 |
--------------------------------------------------------------------------------
/spec/rollbar_spec.rb:
--------------------------------------------------------------------------------
1 | require 'logger'
2 | require 'socket'
3 | require 'spec_helper'
4 | require 'girl_friday'
5 |
6 | begin
7 | require 'sucker_punch'
8 | require 'sucker_punch/testing/inline'
9 | rescue LoadError
10 | end
11 |
12 | describe Rollbar do
13 |
14 | context 'report_exception' do
15 | before(:each) do
16 | configure
17 | Rollbar.configure do |config|
18 | config.logger = logger_mock
19 | end
20 |
21 | begin
22 | foo = bar
23 | rescue => e
24 | @exception = e
25 | end
26 | end
27 |
28 | let(:logger_mock) { double("Rails.logger").as_null_object }
29 |
30 | it 'should report exceptions without person or request data' do
31 | logger_mock.should_receive(:info).with('[Rollbar] Success')
32 | Rollbar.report_exception(@exception)
33 | end
34 |
35 | it 'should not report anything when disabled' do
36 | logger_mock.should_not_receive(:info).with('[Rollbar] Success')
37 | Rollbar.configure do |config|
38 | config.enabled = false
39 | end
40 |
41 | Rollbar.report_exception(@exception)
42 |
43 | Rollbar.configure do |config|
44 | config.enabled = true
45 | end
46 | end
47 |
48 | it 'should be enabled when freshly configured' do
49 | Rollbar.configuration.enabled.should == true
50 | end
51 |
52 | it 'should not be enabled when not configured' do
53 | Rollbar.unconfigure
54 |
55 | Rollbar.configuration.enabled.should be_nil
56 | Rollbar.report_exception(@exception).should == 'disabled'
57 | end
58 |
59 | it 'should stay disabled if configure is called again' do
60 | Rollbar.unconfigure
61 |
62 | # configure once, setting enabled to false.
63 | Rollbar.configure do |config|
64 | config.enabled = false
65 | end
66 |
67 | # now configure again (perhaps to change some other values)
68 | Rollbar.configure do |config| end
69 |
70 | Rollbar.configuration.enabled.should == false
71 | Rollbar.report_exception(@exception).should == 'disabled'
72 | end
73 |
74 | it 'should report exceptions with request and person data' do
75 | logger_mock.should_receive(:info).with('[Rollbar] Success')
76 | request_data = {
77 | :params => { :foo => "bar" },
78 | :url => 'http://localhost/',
79 | :user_ip => '127.0.0.1',
80 | :headers => {},
81 | :GET => { "baz" => "boz" },
82 | :session => { :user_id => 123 },
83 | :method => "GET",
84 | }
85 | person_data = {
86 | :id => 1,
87 | :username => "test",
88 | :email => "test@example.com"
89 | }
90 | Rollbar.report_exception(@exception, request_data, person_data)
91 | end
92 |
93 | it "should work with an IO object as rack.errors" do
94 | logger_mock.should_receive(:info).with('[Rollbar] Success')
95 | request_data = {
96 | :params => { :foo => "bar" },
97 | :url => 'http://localhost/',
98 | :user_ip => '127.0.0.1',
99 | :headers => {},
100 | :GET => { "baz" => "boz" },
101 | :session => { :user_id => 123 },
102 | :method => "GET",
103 | :env => { :"rack.errors" => IO.new(2, File::WRONLY) },
104 | }
105 | person_data = {
106 | :id => 1,
107 | :username => "test",
108 | :email => "test@example.com"
109 | }
110 | Rollbar.report_exception(@exception, request_data, person_data)
111 | end
112 |
113 | it 'should ignore ignored exception classes' do
114 | saved_filters = Rollbar.configuration.exception_level_filters
115 | Rollbar.configure do |config|
116 | config.exception_level_filters = { 'NameError' => 'ignore' }
117 | end
118 |
119 | logger_mock.should_not_receive(:info)
120 | logger_mock.should_not_receive(:warn)
121 | logger_mock.should_not_receive(:error)
122 |
123 | Rollbar.report_exception(@exception)
124 |
125 | Rollbar.configure do |config|
126 | config.exception_level_filters = saved_filters
127 | end
128 | end
129 |
130 | it 'should not report exceptions when silenced' do
131 | Rollbar.should_not_receive :schedule_payload
132 |
133 | begin
134 | test_var = 1
135 | Rollbar.silenced do
136 | test_var = 2
137 | raise
138 | end
139 | rescue => e
140 | Rollbar.report_exception(e)
141 | end
142 |
143 | test_var.should == 2
144 | end
145 |
146 | it 'should report exception objects with no backtrace' do
147 | payload = nil
148 | Rollbar.stub(:schedule_payload) do |*args|
149 | payload = MultiJson.load(args[0])
150 | end
151 | Rollbar.report_exception(StandardError.new("oops"))
152 | payload["data"]["body"]["trace"]["frames"].should == []
153 | payload["data"]["body"]["trace"]["exception"]["class"].should == "StandardError"
154 | payload["data"]["body"]["trace"]["exception"]["message"].should == "oops"
155 | end
156 |
157 | it 'should return the exception data with a uuid, on platforms with SecureRandom' do
158 | if defined?(SecureRandom) and SecureRandom.respond_to?(:uuid)
159 | Rollbar.stub(:schedule_payload) do |*args| end
160 | exception_data = Rollbar.report_exception(StandardError.new("oops"))
161 | exception_data[:uuid].should_not be_nil
162 | end
163 | end
164 |
165 | it 'should report exception objects with nonstandard backtraces' do
166 | payload = nil
167 | Rollbar.stub(:schedule_payload) do |*args|
168 | payload = MultiJson.load(args[0])
169 | end
170 |
171 | class CustomException < StandardError
172 | def backtrace
173 | ["custom backtrace line"]
174 | end
175 | end
176 |
177 | exception = CustomException.new("oops")
178 |
179 | Rollbar.report_exception(exception)
180 |
181 | payload["data"]["body"]["trace"]["frames"][0]["method"].should == "custom backtrace line"
182 | end
183 | end
184 |
185 | context 'report_message' do
186 | before(:each) do
187 | configure
188 | Rollbar.configure do |config|
189 | config.logger = logger_mock
190 | end
191 | end
192 |
193 | let(:logger_mock) { double("Rails.logger").as_null_object }
194 | let(:user) { User.create(:email => 'email@example.com', :encrypted_password => '', :created_at => Time.now, :updated_at => Time.now) }
195 |
196 | it 'should report simple messages' do
197 | logger_mock.should_receive(:info).with('[Rollbar] Scheduling payload')
198 | logger_mock.should_receive(:info).with('[Rollbar] Success')
199 | Rollbar.report_message("Test message")
200 | end
201 |
202 | it 'should not report anything when disabled' do
203 | logger_mock.should_not_receive(:info).with('[Rollbar] Success')
204 | Rollbar.configure do |config|
205 | config.enabled = false
206 | end
207 |
208 | Rollbar.report_message("Test message that should be ignored")
209 |
210 | Rollbar.configure do |config|
211 | config.enabled = true
212 | end
213 | end
214 |
215 | it 'should report messages with extra data' do
216 | logger_mock.should_receive(:info).with('[Rollbar] Success')
217 | Rollbar.report_message("Test message with extra data", 'debug', :foo => "bar",
218 | :hash => { :a => 123, :b => "xyz" })
219 | end
220 |
221 | it 'should not crash with circular extra_data' do
222 | a = { :foo => "bar" }
223 | b = { :a => a }
224 | c = { :b => b }
225 | a[:c] = c
226 |
227 | logger_mock.should_receive(:error).with(/\[Rollbar\] Reporting internal error encountered while sending data to Rollbar./)
228 |
229 | Rollbar.report_message("Test message with circular extra data", 'debug', a)
230 | end
231 |
232 | it 'should be able to report form validation errors when they are present' do
233 | logger_mock.should_receive(:info).with('[Rollbar] Success')
234 | user.errors.add(:example, "error")
235 | user.report_validation_errors_to_rollbar
236 | end
237 |
238 | it 'should not report form validation errors when they are not present' do
239 | logger_mock.should_not_receive(:info).with('[Rollbar] Success')
240 | user.errors.clear
241 | user.report_validation_errors_to_rollbar
242 | end
243 |
244 | after(:each) do
245 | Rollbar.configure do |config|
246 | config.logger = ::Rails.logger
247 | end
248 | end
249 | end
250 |
251 | context 'payload_destination' do
252 | before(:each) do
253 | configure
254 | Rollbar.configure do |config|
255 | config.logger = logger_mock
256 | config.filepath = 'test.rollbar'
257 | end
258 |
259 | begin
260 | foo = bar
261 | rescue => e
262 | @exception = e
263 | end
264 | end
265 |
266 | let(:logger_mock) { double("Rails.logger").as_null_object }
267 |
268 | it 'should send the payload over the network by default' do
269 | logger_mock.should_not_receive(:info).with('[Rollbar] Writing payload to file')
270 | logger_mock.should_receive(:info).with('[Rollbar] Sending payload').once
271 | logger_mock.should_receive(:info).with('[Rollbar] Success').once
272 | Rollbar.report_exception(@exception)
273 | end
274 |
275 | it 'should save the payload to a file if set' do
276 | logger_mock.should_not_receive(:info).with('[Rollbar] Sending payload')
277 | logger_mock.should_receive(:info).with('[Rollbar] Writing payload to file').once
278 | logger_mock.should_receive(:info).with('[Rollbar] Success').once
279 |
280 | filepath = ''
281 |
282 | Rollbar.configure do |config|
283 | config.write_to_file = true
284 | filepath = config.filepath
285 | end
286 |
287 | Rollbar.report_exception(@exception)
288 |
289 | File.exist?(filepath).should == true
290 | File.read(filepath).should include test_access_token
291 | File.delete(filepath)
292 |
293 | Rollbar.configure do |config|
294 | config.write_to_file = false
295 | end
296 | end
297 | end
298 |
299 | context 'asynchronous_handling' do
300 | before(:each) do
301 | configure
302 | Rollbar.configure do |config|
303 | config.logger = logger_mock
304 | end
305 |
306 | begin
307 | foo = bar
308 | rescue => e
309 | @exception = e
310 | end
311 | end
312 |
313 | let(:logger_mock) { double("Rails.logger").as_null_object }
314 |
315 | it 'should send the payload using the default asynchronous handler girl_friday' do
316 | logger_mock.should_receive(:info).with('[Rollbar] Scheduling payload')
317 | logger_mock.should_receive(:info).with('[Rollbar] Sending payload')
318 | logger_mock.should_receive(:info).with('[Rollbar] Success')
319 |
320 | Rollbar.configure do |config|
321 | config.use_async = true
322 | GirlFriday::WorkQueue::immediate!
323 | end
324 |
325 | Rollbar.report_exception(@exception)
326 |
327 | Rollbar.configure do |config|
328 | config.use_async = false
329 | GirlFriday::WorkQueue::queue!
330 | end
331 | end
332 |
333 | it 'should send the payload using a user-supplied asynchronous handler' do
334 | logger_mock.should_receive(:info).with('Custom async handler called')
335 | logger_mock.should_receive(:info).with('[Rollbar] Sending payload')
336 | logger_mock.should_receive(:info).with('[Rollbar] Success')
337 |
338 | Rollbar.configure do |config|
339 | config.use_async = true
340 | config.async_handler = Proc.new { |payload|
341 | logger_mock.info 'Custom async handler called'
342 | Rollbar.process_payload(payload)
343 | }
344 | end
345 |
346 | Rollbar.report_exception(@exception)
347 |
348 | Rollbar.configure do |config|
349 | config.use_async = false
350 | config.async_handler = Rollbar.method(:default_async_handler)
351 | end
352 | end
353 |
354 | if defined?(SuckerPunch)
355 | it "should send the payload to sucker_punch delayer" do
356 | logger_mock.should_receive(:info).with('[Rollbar] Scheduling payload')
357 | logger_mock.should_receive(:info).with('[Rollbar] Sending payload')
358 | logger_mock.should_receive(:info).with('[Rollbar] Success')
359 |
360 | Rollbar.configure do |config|
361 | config.use_sucker_punch = true
362 | end
363 |
364 | Rollbar.report_exception(@exception)
365 |
366 | Rollbar.configure do |config|
367 | config.use_async = false
368 | config.async_handler = Rollbar.method(:default_async_handler)
369 | end
370 | end
371 | end
372 |
373 | it "should send the payload to sidekiq delayer" do
374 | module Rollbar
375 | module Delay
376 | class Sidekiq
377 | end
378 | end
379 | end
380 |
381 | Rollbar::Delay::Sidekiq.should_receive(:handle).with(anything)
382 |
383 | Rollbar.configure do |config|
384 | config.use_sidekiq = { 'queue' => 'test_queue' }
385 | end
386 |
387 | Rollbar.report_exception(@exception)
388 |
389 | Rollbar.configure do |config|
390 | config.use_async = false
391 | config.async_handler = Rollbar.method(:default_async_handler)
392 | end
393 | end
394 | end
395 |
396 | context 'message_data' do
397 | before(:each) do
398 | configure
399 | @message_body = "This is a test"
400 | @level = 'debug'
401 | end
402 |
403 | it 'should build a message' do
404 | data = Rollbar.send(:message_data, @message_body, @level, {})
405 | data[:body][:message][:body].should == @message_body
406 | data[:level].should == @level
407 | data[:custom].should be_nil
408 | end
409 |
410 | it 'should accept extra_data' do
411 | user_id = 123
412 | name = "Tester"
413 |
414 | data = Rollbar.send(:message_data, @message_body, 'info',
415 | :user_id => user_id, :name => name)
416 |
417 | data[:level].should == 'info'
418 | message = data[:body][:message]
419 | message[:body].should == @message_body
420 | message[:user_id].should == user_id
421 | message[:name].should == name
422 | end
423 |
424 | it 'should build a message with custom data when configured' do
425 | Rollbar.configure do |config|
426 | config.custom_data_method = lambda { {:foo => "bar", :hello => [1, 2, 3]} }
427 | end
428 |
429 | data = Rollbar.send(:message_data, @message_body, @level, {})
430 |
431 | data[:level].should == @level
432 | data[:body][:message][:body].should == @message_body
433 | data[:custom].should_not be_nil
434 | data[:custom][:foo].should == "bar"
435 | data[:custom][:hello][2].should == 3
436 |
437 | Rollbar.configure do |config|
438 | config.custom_data_method = nil
439 | end
440 | end
441 | end
442 |
443 | context 'exception_data' do
444 | before(:each) do
445 | configure
446 | begin
447 | foo = bar
448 | rescue => e
449 | @exception = e
450 | end
451 | end
452 |
453 | it 'should accept force_level' do
454 | level = 'critical'
455 | data = Rollbar.send(:exception_data, @exception, level)
456 | data[:level].should == level
457 | end
458 |
459 | it 'should build valid exception data' do
460 | data = Rollbar.send(:exception_data, @exception)
461 |
462 | data[:level].should_not be_nil
463 | data[:custom].should be_nil
464 |
465 | trace = data[:body][:trace]
466 |
467 | frames = trace[:frames]
468 | frames.should be_a_kind_of(Array)
469 | frames.each do |frame|
470 | frame[:filename].should be_a_kind_of(String)
471 | frame[:lineno].should be_a_kind_of(Fixnum)
472 | if frame[:method]
473 | frame[:method].should be_a_kind_of(String)
474 | end
475 | end
476 |
477 | # should be NameError, but can be NoMethodError sometimes on rubinius 1.8
478 | # http://yehudakatz.com/2010/01/02/the-craziest-fing-bug-ive-ever-seen/
479 | trace[:exception][:class].should match(/^(NameError|NoMethodError)$/)
480 | trace[:exception][:message].should match(/^(undefined local variable or method `bar'|undefined method `bar' on an instance of)/)
481 | end
482 |
483 | it 'should include custom data when configured' do
484 | Rollbar.configure do |config|
485 | config.custom_data_method = lambda { {:foo => "baz", :hello => [4, 5, 6]} }
486 | end
487 |
488 | data = Rollbar.send(:exception_data, @exception)
489 | data[:body][:trace].should_not be_nil
490 | data[:custom][:foo].should == "baz"
491 | data[:custom][:hello][2].should == 6
492 |
493 | Rollbar.configure do |config|
494 | config.custom_data_method = nil
495 | end
496 | end
497 |
498 | end
499 |
500 | context 'logger' do
501 | before(:each) do
502 | reset_configuration
503 | end
504 |
505 | it 'should have use the Rails logger when configured to do so' do
506 | configure
507 | Rollbar.send(:logger).should == ::Rails.logger
508 | end
509 |
510 | it 'should use the default_logger when no logger is set' do
511 | logger = Logger.new(STDERR)
512 | Rollbar.configure do |config|
513 | config.default_logger = lambda { logger }
514 | end
515 | Rollbar.send(:logger).should == logger
516 | end
517 |
518 | it 'should have a default default_logger' do
519 | Rollbar.send(:logger).should_not be_nil
520 | end
521 |
522 | after(:each) do
523 | reset_configuration
524 | end
525 | end
526 |
527 | context 'build_payload' do
528 | it 'should build valid json' do
529 | json = Rollbar.send(:build_payload, {:foo => {:bar => "baz"}})
530 | hash = MultiJson.load(json)
531 | hash["data"]["foo"]["bar"].should == "baz"
532 | end
533 | end
534 |
535 | context 'base_data' do
536 | before(:each) { configure }
537 |
538 | it 'should have the correct notifier name' do
539 | Rollbar.send(:base_data)[:notifier][:name].should == 'rollbar-gem'
540 | end
541 |
542 | it 'should have the correct notifier version' do
543 | Rollbar.send(:base_data)[:notifier][:version].should == Rollbar::VERSION
544 | end
545 |
546 | it 'should have all the required keys' do
547 | data = Rollbar.send(:base_data)
548 | data[:timestamp].should_not be_nil
549 | data[:environment].should_not be_nil
550 | data[:level].should_not be_nil
551 | data[:language].should == 'ruby'
552 | data[:framework].should match(/^Rails/)
553 | end
554 |
555 | it 'should have a default production environment' do
556 | data = Rollbar.send(:base_data)
557 | data[:environment].should == 'production'
558 | end
559 |
560 | it 'should have an overridden environment' do
561 | Rollbar.configure do |config|
562 | config.environment = 'overridden'
563 | end
564 |
565 | data = Rollbar.send(:base_data)
566 | data[:environment].should == 'overridden'
567 | end
568 |
569 | it 'should not have custom data under default configuration' do
570 | data = Rollbar.send(:base_data)
571 | data[:custom].should be_nil
572 | end
573 |
574 | it 'should have custom data when custom_data_method is configured' do
575 | Rollbar.configure do |config|
576 | config.custom_data_method = lambda { {:a => 1, :b => [2, 3, 4]} }
577 | end
578 |
579 | data = Rollbar.send(:base_data)
580 | data[:custom].should_not be_nil
581 | data[:custom][:a].should == 1
582 | data[:custom][:b][2].should == 4
583 | end
584 | end
585 |
586 | context 'server_data' do
587 | it 'should have the right hostname' do
588 | Rollbar.send(:server_data)[:host] == Socket.gethostname
589 | end
590 |
591 | it 'should have root and branch set when configured' do
592 | configure
593 | Rollbar.configure do |config|
594 | config.root = '/path/to/root'
595 | config.branch = 'master'
596 | end
597 |
598 | data = Rollbar.send(:server_data)
599 | data[:root].should == '/path/to/root'
600 | data[:branch].should == 'master'
601 | end
602 | end
603 |
604 | context "project_gems" do
605 | it "should include gem paths for specified project gems in the payload" do
606 | gems = ['rack', 'rspec-rails']
607 | gem_paths = []
608 |
609 | Rollbar.configure do |config|
610 | config.project_gems = gems
611 | end
612 |
613 | gems.each {|gem|
614 | gem_paths.push(Gem::Specification.find_by_name(gem).gem_dir)
615 | }
616 |
617 | data = Rollbar.send(:message_data, 'test', 'info', {})
618 | data[:project_package_paths].kind_of?(Array).should == true
619 | data[:project_package_paths].length.should == gem_paths.length
620 |
621 | data[:project_package_paths].each_with_index{|path, index|
622 | path.should == gem_paths[index]
623 | }
624 | end
625 | end
626 |
627 | context "report_internal_error" do
628 | it "should not crash when given an exception object" do
629 | begin
630 | 1 / 0
631 | rescue => e
632 | Rollbar.send(:report_internal_error, e)
633 | end
634 | end
635 | end
636 |
637 | context "send_failsafe" do
638 | it "should not crash when given a message and exception" do
639 | begin
640 | 1 / 0
641 | rescue => e
642 | Rollbar.send(:send_failsafe, "test failsafe", e)
643 | end
644 | end
645 |
646 | it "should not crash when given all nils" do
647 | Rollbar.send(:send_failsafe, nil, nil)
648 | end
649 | end
650 |
651 | context "request_data_extractor" do
652 | before(:each) do
653 | class DummyClass
654 | end
655 | @dummy_class = DummyClass.new
656 | @dummy_class.extend(Rollbar::RequestDataExtractor)
657 | end
658 |
659 | context "rollbar_headers" do
660 | it "should not include cookies" do
661 | env = {"HTTP_USER_AGENT" => "test", "HTTP_COOKIE" => "cookie"}
662 | headers = @dummy_class.send(:rollbar_headers, env)
663 | headers.should have_key "User-Agent"
664 | headers.should_not have_key "Cookie"
665 | end
666 | end
667 | end
668 |
669 | # configure with some basic params
670 | def configure
671 | Rollbar.reconfigure do |config|
672 | # special test access token
673 | config.access_token = test_access_token
674 | config.logger = ::Rails.logger
675 | config.root = ::Rails.root
676 | config.framework = "Rails: #{::Rails::VERSION::STRING}"
677 | end
678 | end
679 |
680 | def test_access_token
681 | 'aaaabbbbccccddddeeeeffff00001111'
682 | end
683 |
684 | end
685 |
--------------------------------------------------------------------------------