├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .rspec ├── Appraisals ├── CHANGELOG.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── examples └── sinatras_raygun.rb ├── gemfiles ├── rails_6.gemfile ├── rails_7.gemfile └── rails_7_sidekiq_6.gemfile ├── lib ├── generators │ └── raygun │ │ └── install_generator.rb ├── raygun.rb ├── raygun │ ├── affected_user.rb │ ├── breadcrumbs.rb │ ├── breadcrumbs │ │ ├── breadcrumb.rb │ │ └── store.rb │ ├── client.rb │ ├── configuration.rb │ ├── demo_exception.rb │ ├── error.rb │ ├── error_subscriber.rb │ ├── javascript_tracker.rb │ ├── middleware │ │ ├── breadcrumbs_store_initializer.rb │ │ ├── javascript_exception_tracking.rb │ │ ├── rack_exception_interceptor.rb │ │ └── rails_insert_affected_user.rb │ ├── railtie.rb │ ├── services │ │ └── apply_whitelist_filter_to_payload.rb │ ├── sidekiq.rb │ └── version.rb ├── raygun4ruby.rb ├── resque │ └── failure │ │ └── raygun.rb └── tasks │ └── raygun.tasks ├── raygun4ruby.gemspec ├── spec ├── features │ └── javascript_spec.rb ├── rails_applications │ ├── 6.1.4 │ │ ├── Gemfile │ │ ├── README.md │ │ ├── Rakefile │ │ ├── app │ │ │ ├── assets │ │ │ │ ├── config │ │ │ │ │ └── manifest.js │ │ │ │ ├── images │ │ │ │ │ └── .keep │ │ │ │ └── stylesheets │ │ │ │ │ └── application.css │ │ │ ├── channels │ │ │ │ └── application_cable │ │ │ │ │ ├── channel.rb │ │ │ │ │ └── connection.rb │ │ │ ├── controllers │ │ │ │ ├── application_controller.rb │ │ │ │ ├── concerns │ │ │ │ │ └── .keep │ │ │ │ └── home_controller.rb │ │ │ ├── helpers │ │ │ │ └── application_helper.rb │ │ │ ├── javascript │ │ │ │ ├── channels │ │ │ │ │ ├── consumer.js │ │ │ │ │ └── index.js │ │ │ │ └── packs │ │ │ │ │ └── application.js │ │ │ ├── jobs │ │ │ │ └── application_job.rb │ │ │ ├── mailers │ │ │ │ └── application_mailer.rb │ │ │ ├── models │ │ │ │ ├── application_record.rb │ │ │ │ └── concerns │ │ │ │ │ └── .keep │ │ │ └── views │ │ │ │ ├── home │ │ │ │ ├── index.html.erb │ │ │ │ └── index.json.erb │ │ │ │ └── layouts │ │ │ │ ├── application.html.erb │ │ │ │ ├── mailer.html.erb │ │ │ │ └── mailer.text.erb │ │ ├── bin │ │ │ ├── rails │ │ │ ├── rake │ │ │ ├── setup │ │ │ ├── spring │ │ │ └── yarn │ │ ├── config.ru │ │ ├── config │ │ │ ├── application.rb │ │ │ ├── boot.rb │ │ │ ├── cable.yml │ │ │ ├── credentials.yml.enc │ │ │ ├── database.yml │ │ │ ├── environment.rb │ │ │ ├── environments │ │ │ │ ├── development.rb │ │ │ │ ├── production.rb │ │ │ │ └── test.rb │ │ │ ├── initializers │ │ │ │ ├── application_controller_renderer.rb │ │ │ │ ├── assets.rb │ │ │ │ ├── backtrace_silencers.rb │ │ │ │ ├── content_security_policy.rb │ │ │ │ ├── cookies_serializer.rb │ │ │ │ ├── filter_parameter_logging.rb │ │ │ │ ├── inflections.rb │ │ │ │ ├── mime_types.rb │ │ │ │ ├── permissions_policy.rb │ │ │ │ └── wrap_parameters.rb │ │ │ ├── locales │ │ │ │ └── en.yml │ │ │ ├── master.key │ │ │ ├── puma.rb │ │ │ ├── routes.rb │ │ │ ├── spring.rb │ │ │ └── storage.yml │ │ ├── db │ │ │ ├── seeds.rb │ │ │ └── test.sqlite3 │ │ ├── lib │ │ │ ├── assets │ │ │ │ └── .keep │ │ │ └── tasks │ │ │ │ └── .keep │ │ ├── package.json │ │ ├── public │ │ │ ├── 404.html │ │ │ ├── 422.html │ │ │ ├── 500.html │ │ │ ├── apple-touch-icon-precomposed.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── favicon.ico │ │ │ └── robots.txt │ │ └── storage │ │ │ └── .keep │ └── 7.1.3 │ │ ├── .dockerignore │ │ ├── .gitattributes │ │ ├── .gitignore │ │ ├── .ruby-version │ │ ├── Dockerfile │ │ ├── Gemfile │ │ ├── README.md │ │ ├── Rakefile │ │ ├── app │ │ ├── assets │ │ │ ├── config │ │ │ │ └── manifest.js │ │ │ ├── images │ │ │ │ └── .keep │ │ │ └── stylesheets │ │ │ │ └── application.css │ │ ├── channels │ │ │ └── application_cable │ │ │ │ ├── channel.rb │ │ │ │ └── connection.rb │ │ ├── controllers │ │ │ ├── application_controller.rb │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ └── home_controller.rb │ │ ├── helpers │ │ │ ├── application_helper.rb │ │ │ └── home_helper.rb │ │ ├── javascript │ │ │ ├── application.js │ │ │ └── controllers │ │ │ │ ├── application.js │ │ │ │ ├── hello_controller.js │ │ │ │ └── index.js │ │ ├── jobs │ │ │ └── application_job.rb │ │ ├── mailers │ │ │ └── application_mailer.rb │ │ ├── models │ │ │ ├── application_record.rb │ │ │ └── concerns │ │ │ │ └── .keep │ │ └── views │ │ │ ├── home │ │ │ ├── index.html.erb │ │ │ └── index.json.erb │ │ │ └── layouts │ │ │ ├── application.html.erb │ │ │ ├── mailer.html.erb │ │ │ └── mailer.text.erb │ │ ├── bin │ │ ├── bundle │ │ ├── docker-entrypoint │ │ ├── importmap │ │ ├── rails │ │ ├── rake │ │ └── setup │ │ ├── config.ru │ │ ├── config │ │ ├── application.rb │ │ ├── boot.rb │ │ ├── cable.yml │ │ ├── credentials.yml.enc │ │ ├── database.yml │ │ ├── environment.rb │ │ ├── environments │ │ │ ├── development.rb │ │ │ ├── production.rb │ │ │ └── test.rb │ │ ├── importmap.rb │ │ ├── initializers │ │ │ ├── assets.rb │ │ │ ├── content_security_policy.rb │ │ │ ├── filter_parameter_logging.rb │ │ │ ├── inflections.rb │ │ │ └── permissions_policy.rb │ │ ├── locales │ │ │ └── en.yml │ │ ├── puma.rb │ │ ├── routes.rb │ │ └── storage.yml │ │ ├── db │ │ └── seeds.rb │ │ ├── lib │ │ ├── assets │ │ │ └── .keep │ │ └── tasks │ │ │ └── .keep │ │ ├── public │ │ ├── 404.html │ │ ├── 422.html │ │ ├── 500.html │ │ ├── apple-touch-icon-precomposed.png │ │ ├── apple-touch-icon.png │ │ ├── favicon.ico │ │ └── robots.txt │ │ ├── storage │ │ └── .keep │ │ ├── test │ │ ├── application_system_test_case.rb │ │ ├── channels │ │ │ └── application_cable │ │ │ │ └── connection_test.rb │ │ ├── controllers │ │ │ ├── .keep │ │ │ └── home_controller_test.rb │ │ ├── fixtures │ │ │ └── files │ │ │ │ └── .keep │ │ ├── helpers │ │ │ └── .keep │ │ ├── integration │ │ │ └── .keep │ │ ├── mailers │ │ │ └── .keep │ │ ├── models │ │ │ └── .keep │ │ ├── system │ │ │ └── .keep │ │ └── test_helper.rb │ │ └── vendor │ │ ├── .keep │ │ └── javascript │ │ └── .keep ├── rails_helper.rb ├── raygun │ └── breadcrumbs │ │ ├── breadcrumb_spec.rb │ │ └── store_spec.rb ├── services │ └── apply_whitelist_filter_to_payload_spec.rb ├── spec_helper.rb └── support │ └── fake_logger.rb └── test ├── integration └── client_test.rb ├── rails_helper.rb ├── test_helper.rb └── unit ├── affected_user_test.rb ├── client_test.rb ├── configuration_test.rb ├── error_subscriber_test.rb ├── raygun_test.rb ├── resque_failure_test.rb └── sidekiq_failure_test.rb /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | name: Run tests 6 | runs-on: ubuntu-20.04 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | ruby: ["3.0", 3.1, 3.2, 3.3] 11 | gemfile: 12 | - gemfiles/rails_6.gemfile 13 | - gemfiles/rails_7.gemfile 14 | - gemfiles/rails_7_sidekiq_6.gemfile 15 | include: 16 | - ruby: "3.0" 17 | gemfile: gemfiles/rails_6.gemfile 18 | - ruby: "3.0" 19 | gemfile: gemfiles/rails_7.gemfile 20 | - ruby: 3.1 21 | gemfile: gemfiles/rails_6.gemfile 22 | - ruby: 3.1 23 | gemfile: gemfiles/rails_7.gemfile 24 | - ruby: 3.2 25 | gemfile: gemfiles/rails_6.gemfile 26 | - ruby: 3.2 27 | gemfile: gemfiles/rails_7.gemfile 28 | - ruby: 3.3 29 | gemfile: gemfiles/rails_6.gemfile 30 | - ruby: 3.3 31 | gemfile: gemfiles/rails_7.gemfile 32 | 33 | env: 34 | BUNDLE_GEMFILE: ${{ matrix.gemfile }} 35 | 36 | steps: 37 | - uses: actions/checkout@v4 38 | 39 | - uses: ruby/setup-ruby@v1 40 | with: 41 | bundler: 2.3 42 | bundler-cache: true 43 | ruby-version: ${{ matrix.ruby }} 44 | 45 | - run: bundle install 46 | 47 | - name: Run tests 48 | run: bundle exec rake 49 | -------------------------------------------------------------------------------- /.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 | lib/bundler/man 13 | pkg 14 | rdoc 15 | spec/reports 16 | test/tmp 17 | test/version_tmp 18 | tmp 19 | spec/rails_applications/*/log 20 | 21 | # JetBrains 22 | .idea 23 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise "rails-6" do 2 | gem "rails", "6.1.4" 3 | gem "sqlite3", "~> 1.4" 4 | gem "sidekiq", ">= 7" 5 | end 6 | 7 | appraise "rails-7" do 8 | gem "rails", "7.1.3" 9 | gem "sqlite3", "~> 1.4" 10 | gem "importmap-rails" 11 | gem "sidekiq", ">= 7" 12 | end 13 | 14 | appraise "rails-7-sidekiq-6" do 15 | gem "rails", "7.1.3" 16 | gem "sqlite3", "~> 1.4" 17 | gem "importmap-rails" 18 | gem "sidekiq", "~> 6.5" 19 | end -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 4.0.1 (29/07/2024): 2 | 3 | - Adds the ability to unwrap `Sidekiq::JobRetry::Handled` exceptions (or ignore them entirely) ([#185](https://github.com/MindscapeHQ/raygun4ruby/pull/185)) 4 | 5 | ## 4.0.0 (20/05/2024): 6 | 7 | - BREAKING CHANGE: Remove support for end-of-life Ruby verisons and Rails versions prior to 6.0.0 8 | - Bug fix: Fix issue with breadcrumbs not being sent to Raygun when `send_in_background` is enabled (thanks to @jjb for the bug report) 9 | - Updates testing to reflect the above 10 | - Use `Kernel.caller` when backtrace is not available (thanks to @TSMMark) 11 | 12 | ## 3.2.6 (17/03/2021): 13 | 14 | - Bug fix: Rename Testable class to DemoException to ensure it is added to the bundle ([#166](https://github.com/MindscapeHQ/raygun4ruby/pull/166)) 15 | 16 | ## 3.2.5 (15/03/2021): 17 | 18 | - Bug fix: Ensure tags passed into track_exception are not persisted ([#164](https://github.com/MindscapeHQ/raygun4ruby/pull/164)) 19 | 20 | ## 3.2.4 (11/02/2021): 21 | 22 | - Set sidekiq tag on sidekiq errors ([#161](https://github.com/MindscapeHQ/raygun4ruby/pull/161)) 23 | 24 | ## 3.2.2 (10/06/2020): 25 | 26 | - Introduce support for Raygun APM exceptions correlation ([#154](https://github.com/MindscapeHQ/raygun4ruby/pull/154)) 27 | 28 | ## 3.2.1 (25/02/2019): 29 | 30 | Bugfix: 31 | 32 | - Remove Ruby 2.3 syntax to retain support for Ruby >= 2.0 ([#148](https://github.com/MindscapeHQ/raygun4ruby/pull/148)) 33 | 34 | ## 3.2.0 (21/02/2019): 35 | 36 | Bugfix: 37 | 38 | - Fix NoMethodError Exception: undefined method `include?' for nil:NilClass in `JavascriptExceptionTracking` class. Thanks @yamanaltereh for this ([#141](https://github.com/MindscapeHQ/raygun4ruby/pull/141)) 39 | - Fix ([#145](https://github.com/MindscapeHQ/raygun4ruby/issues/145)), "raygun4ruby will load pry if it is in the gem bundle". Thanks to @eoinkelly for reporting this 40 | 41 | Feature: 42 | 43 | - If you have recorded a large number of Breadcrumbs, or just very large ones, Raygun4Ruby will now only send up to 100KB of them instead of all of them, potentially going over the 128KB payload limit Raygun accepts ([#147](https://github.com/MindscapeHQ/raygun4ruby/pull/147)) 44 | 45 | ## 3.1.1 (16/01/2019): 46 | 47 | Bugfix: 48 | 49 | - Don't attempt to modify response unless JS api key is present 50 | - Don't attempt to modify response unless it responds to indexing ([]) 51 | - See PR ([#140](https://github.com/MindscapeHQ/raygun4ruby/pull/140)) 52 | 53 | ## 3.1.0 (15/01/2019): 54 | 55 | Feature: - Ability to automatically configure Raygun4JS on the client side by injecting it into outbound HTML pages. Thanks @MikeRogers0 for this ([#138](https://github.com/MindscapeHQ/raygun4ruby/pull/138)) 56 | 57 | ## 3.0.0 (18/12/2018): 58 | 59 | Breaking changes: 60 | Parameter filters are now applied if you are using the `filter_payload_with_whitelist` functionality. Previously if this was set to true the parameter filtering was bailed out of ([#136](https://github.com/MindscapeHQ/raygun4ruby/pull/136/files)) 61 | 62 | ## 2.7.1 (11/06/2018) 63 | 64 | This is a patch release to update the required ruby version to correctly be 2.0 or greater 65 | 66 | ## 2.7.0 (19/02/2018) 67 | 68 | Features 69 | 70 | - Add configuration option to control network timeouts when sending error reports, default value is 10 seconds ([#129](https://github.com/MindscapeHQ/raygun4ruby/pull/129)) 71 | 72 | ## 2.6.0 (25/10/2017) 73 | 74 | Features 75 | 76 | - Enhanced debug logging to assist in resolving issues from support requests ([#128](https://github.com/MindscapeHQ/raygun4ruby/pull/128)) 77 | 78 | ## 2.5.0 (04/10/2017) 79 | 80 | Features 81 | 82 | - Teach tags configuration how to handle a proc to allow dynamically settings tags ([#127](https://github.com/MindscapeHQ/raygun4ruby/pull/127)) 83 | 84 | Bugfixes 85 | 86 | - Fix crash when recording breadcrumb with uninitialized store ([#126](https://github.com/MindscapeHQ/raygun4ruby/pull/126)) 87 | - Make raw data handling more robust and fix in unicorn ([#125](https://github.com/MindscapeHQ/raygun4ruby/pull/125)) 88 | - Backwards compatible affected_user_identifier_methods ([#120](https://github.com/MindscapeHQ/raygun4ruby/pull/120)) 89 | 90 | ## 2.4.1 (29/08/2017) 91 | 92 | Bugfixes 93 | 94 | - Fix crash in `Client#raw_data` method when `rack.input` buffer is missing `pos` method 95 | 96 | ## 2.4.0 (31/07/2017) 97 | 98 | Features 99 | 100 | - Add functionality to track affected user/customer in Sidekiq jobs, refer to the README for more information, under the "Affected User Tracking/Customers in Sidekiq" heading ([#121](https://github.com/MindscapeHQ/raygun4ruby/pull/121)) 101 | 102 | ## 2.3.0 (09/05/2017)" 103 | 104 | Bugfixes 105 | 106 | - Fix issue preventing affected users/customers for a crash report from showing up in the affected users/customers page ([#119](https://github.com/MindscapeHQ/raygun4ruby/pull/119)) 107 | 108 | ## 2.2.0 (05/05/2017) 109 | 110 | Features 111 | 112 | - Opt in support for sending exceptions in a background thread to not block web request thread during IO ([#117](https://github.com/MindscapeHQ/raygun4ruby/pull/117)) 113 | 114 | Bugfixes 115 | 116 | - Don't attempt to read raw data during GET requests or if rack.input buffer is empty 117 | 118 | ## 2.1.0 (27/04/2017) 119 | 120 | Features 121 | 122 | - Ability to record breadcrumbs in your code that will be sent to Raygun along with a raised exception ([#113](https://github.com/MindscapeHQ/raygun4ruby/pull/113)) 123 | 124 | ## 2.0.0 (20/04/2017) 125 | 126 | Bugfixes: 127 | 128 | - Fix broken handling of raw request body reading in Rack applications ([#116](https://github.com/MindscapeHQ/raygun4ruby/pull/116)) 129 | - This is a breaking change to how raw data was being read before so it requires a major version bump 130 | - Raw request data reading is now disabled by default and can be enabled via the `record_raw_data` configuration option 131 | 132 | Since this is a major version bump this release also deprecates ruby versions < 2.0 133 | 134 | ## 1.5.0 (16/03/2017) 135 | 136 | Features 137 | 138 | - Send utcOffset with Raygun payload to calculate local server time in Raygun dashboard ([#112](https://github.com/MindscapeHQ/raygun4ruby/pull/112)) 139 | 140 | ## 1.4.0 (14/03/2017) 141 | 142 | Features: 143 | 144 | - Raygun API url is now configurable via `Configuration.api_url` ([#111](https://github.com/MindscapeHQ/raygun4ruby/pull/111)) 145 | - Added support for `Exception#cause` to be tracked as `innerError` on Raygun. Only supported on Ruby >= 2.1 ([#107](https://github.com/MindscapeHQ/raygun4ruby/pull/107)) 146 | 147 | ## 1.3.0 (10/03/2017) 148 | 149 | Features: 150 | 151 | - Improve affected user handling to let you specify all Raygun parameters, identifier, email, first name, full name and uuid. See [README.md](https://github.com/MindscapeHQ/raygun4ruby#affected-user-tracking) for details ([#34](https://github.com/MindscapeHQ/raygun4ruby/pull/34)) 152 | - Pass a user object as the third parameter to `Raygun.track_exception` to have affected user tracking/customers for manually tracked exceptions, see the above link for more information on configuring this ([#106](https://github.com/MindscapeHQ/raygun4ruby/pull/106)) 153 | - If the exception instance responds to `:raygun_custom_data` that method will be called and the return value merged into the `custom_data` hash sent to Raygun. For convenience a `Raygun::Error` class is provided that takes this custom data as a second argument ([#101](https://github.com/MindscapeHQ/raygun4ruby/pull/101)) 154 | - Allowed `Configuration.custom_data` to be set to a proc to allow a global custom data hook for all exceptions. It is passed as arguments the exception and the environment hash ([#108](https://github.com/MindscapeHQ/raygun4ruby/pull/108)) 155 | - Added `Configuration.debug` to enable logging the reason why an exception was not reported ([#109](https://github.com/MindscapeHQ/raygun4ruby/pull/109)) 156 | 157 | ## 1.2.1 (09/03/2017) 158 | 159 | Bugfixes: 160 | 161 | - dup input hashes before applying whitelist filtering, previously this was modifying the contents of `action_dispatch.request.parameters` ([#105](https://github.com/MindscapeHQ/raygun4ruby/pull/105)) 162 | 163 | ## 1.2.0 (09/03/2017) 164 | 165 | Features: 166 | 167 | - Added two new configuration options, `filter_payload_with_whitelist` and `whitelist_payload_shape` ([#100](https://github.com/MindscapeHQ/raygun4ruby/pull/100)) 168 | - See [README.md](https://github.com/MindscapeHQ/raygun4ruby#filtering-the-payload-by-whitelist) for an example of how to use them 169 | - When raygun4ruby encounters an exception trying to track an exception it will try once to send that exception to Raygun so you are notified ([#104](https://github.com/MindscapeHQ/raygun4ruby/pull/104)) 170 | 171 | Bugfixes: 172 | 173 | - raygun4ruby will no longer crash and suppress app exceptions when the API key is not configured ([#87](https://github.com/MindscapeHQ/raygun4ruby/pull/87)) 174 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in raygun4ruby.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Nik Wakelin 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 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/setup" 2 | require "bundler/gem_tasks" 3 | 4 | require "appraisal" 5 | require "rake/testtask" 6 | 7 | namespace :test do 8 | 9 | desc "Test the basics of the adapter" 10 | Rake::TestTask.new(:units) do |t| 11 | t.test_files = FileList["test/unit/*_test.rb"] 12 | end 13 | 14 | desc "Run a test against the live API" 15 | Rake::TestTask.new(:integration) do |t| 16 | t.test_files = FileList["test/integration/*_test.rb"] 17 | end 18 | 19 | begin 20 | require 'rspec/core/rake_task' 21 | 22 | RSpec::Core::RakeTask.new(:spec) 23 | 24 | rescue LoadError 25 | end 26 | end 27 | 28 | if !ENV["APPRAISAL_INITIALIZED"] && !ENV["CI"] 29 | task default: :appraisal 30 | else 31 | task default: ["test:units", "test:spec"] 32 | end 33 | -------------------------------------------------------------------------------- /examples/sinatras_raygun.rb: -------------------------------------------------------------------------------- 1 | # NB: You'll need to install the 'sinatra' gem for this to work :) 2 | # $ gem install sinatra 3 | # $ ruby sinatras_raygun.rb 4 | 5 | require 'sinatra' 6 | require_relative '../lib/raygun4ruby' 7 | 8 | Raygun.setup do |config| 9 | config.api_key = YOUR_RAYGUN_API_KEY_HERE 10 | end 11 | 12 | use Raygun::RackExceptionInterceptor 13 | 14 | set :raise_errors, true 15 | 16 | get '/' do 17 | raise "This is an exception that will be sent to Raygun!" 18 | end -------------------------------------------------------------------------------- /gemfiles/rails_6.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rails", "6.1.4" 6 | gem "sqlite3", "~> 1.4" 7 | gem "sidekiq", ">= 7" 8 | 9 | gemspec path: "../" 10 | -------------------------------------------------------------------------------- /gemfiles/rails_7.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rails", "7.1.3" 6 | gem "sqlite3", "~> 1.4" 7 | gem "importmap-rails" 8 | gem "sidekiq", ">= 7" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails_7_sidekiq_6.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "rails", "7.1.3" 6 | gem "sqlite3", "~> 1.4" 7 | gem "importmap-rails" 8 | gem "sidekiq", "~> 6.5" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /lib/generators/raygun/install_generator.rb: -------------------------------------------------------------------------------- 1 | module Raygun 2 | class InstallGenerator < Rails::Generators::Base 3 | 4 | argument :api_key 5 | 6 | desc "This generator creates a configuration file for the Raygun ruby adapter inside config/initializers" 7 | def create_configuration_file 8 | filter_parameters = if defined?(Rails) 9 | "config.filter_parameters = Rails.application.config.filter_parameters" 10 | else 11 | "config.filter_parameters = [ :password, :card_number, :cvv ] # don't forget to filter out sensitive parameters" 12 | end 13 | initializer "raygun.rb" do 14 | <<-EOS 15 | Raygun.setup do |config| 16 | config.api_key = "#{api_key}" 17 | #{filter_parameters} 18 | 19 | # The default is Rails.env.production? 20 | # config.enable_reporting = !Rails.env.development? && !Rails.env.test? 21 | end 22 | EOS 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/raygun.rb: -------------------------------------------------------------------------------- 1 | require "concurrent" 2 | require "httparty" 3 | require "logger" 4 | require "json" 5 | require "socket" 6 | require "rack" 7 | require "ostruct" 8 | 9 | require "raygun/version" 10 | require "raygun/configuration" 11 | require "raygun/client" 12 | require "raygun/javascript_tracker" 13 | require "raygun/middleware/rack_exception_interceptor" 14 | require "raygun/middleware/breadcrumbs_store_initializer" 15 | require "raygun/middleware/javascript_exception_tracking" 16 | require "raygun/demo_exception" 17 | require "raygun/error_subscriber" 18 | require "raygun/error" 19 | require "raygun/affected_user" 20 | require "raygun/services/apply_whitelist_filter_to_payload" 21 | require "raygun/breadcrumbs/breadcrumb" 22 | require "raygun/breadcrumbs/store" 23 | require "raygun/breadcrumbs" 24 | require "raygun/railtie" if defined?(Rails) 25 | 26 | module Raygun 27 | 28 | # used to identify ourselves to Raygun 29 | CLIENT_URL = "https://github.com/MindscapeHQ/raygun4ruby" 30 | CLIENT_NAME = "Raygun4Ruby Gem" 31 | 32 | class << self 33 | include DemoException 34 | 35 | # Configuration Object (instance of Raygun::Configuration) 36 | attr_writer :configuration 37 | 38 | # List of futures that are currently running 39 | @@active_futures = [] 40 | 41 | def setup 42 | yield(configuration) 43 | 44 | log("configuration settings: #{configuration.inspect}") 45 | end 46 | 47 | def configuration 48 | @configuration ||= Configuration.new 49 | end 50 | 51 | def default_configuration 52 | configuration.defaults 53 | end 54 | 55 | def reset_configuration 56 | @configuration = Configuration.new 57 | end 58 | 59 | def configured? 60 | !!configuration.api_key 61 | end 62 | 63 | def track_exception(exception_instance, env = {}, user = nil, retries_remaining = configuration.error_report_max_attempts - 1) 64 | log('tracking exception') 65 | 66 | exception_instance.set_backtrace(caller) if exception_instance.is_a?(Exception) && exception_instance.backtrace.nil? 67 | 68 | result = if configuration.send_in_background 69 | track_exception_async(exception_instance, env, user, retries_remaining) 70 | else 71 | track_exception_sync(exception_instance, env, user, retries_remaining) 72 | end 73 | 74 | result 75 | end 76 | 77 | def track_exceptions 78 | yield 79 | rescue => e 80 | track_exception(e) 81 | end 82 | 83 | def record_breadcrumb( 84 | message: nil, 85 | category: '', 86 | level: :info, 87 | timestamp: Time.now.utc, 88 | metadata: {}, 89 | class_name: nil, 90 | method_name: nil, 91 | line_number: nil 92 | ) 93 | log('recording breadcrumb') 94 | 95 | Raygun::Breadcrumbs::Store.record( 96 | message: message, 97 | category: category, 98 | level: level, 99 | timestamp: timestamp, 100 | metadata: metadata, 101 | class_name: class_name, 102 | method_name: method_name, 103 | line_number: line_number, 104 | ) 105 | end 106 | 107 | def log(message) 108 | return unless configuration.debug 109 | 110 | configuration.logger.info("[Raygun] #{message}") if configuration.logger 111 | end 112 | 113 | def failsafe_log(message) 114 | configuration.failsafe_logger.info(message) 115 | end 116 | 117 | def deprecation_warning(message) 118 | if defined?(ActiveSupport::Deprecation) 119 | ActiveSupport::Deprecation.warn(message) 120 | else 121 | puts message 122 | end 123 | end 124 | 125 | def wait_for_futures 126 | @@active_futures.each(&:value) 127 | end 128 | 129 | private 130 | 131 | def track_exception_async(exception_instance, env, user, retries_remaining) 132 | env[:rg_breadcrumb_store] = Raygun::Breadcrumbs::Store.take_until_size(Client::MAX_BREADCRUMBS_SIZE) if Raygun::Breadcrumbs::Store.any? 133 | 134 | future = Concurrent::Future.execute { track_exception_sync(exception_instance, env, user, retries_remaining) } 135 | future.add_observer(lambda do |_, value, reason| 136 | if value == nil || !value.responds_to?(:response) || value.response.code != "202" 137 | log("unexpected response from Raygun, could indicate error: #{value.inspect}") 138 | end 139 | @@active_futures.delete(future) 140 | end, :call) 141 | @@active_futures << future 142 | 143 | future 144 | end 145 | 146 | def track_exception_sync(exception_instance, env, user, retries_remaining) 147 | if should_report?(exception_instance) 148 | log('attempting to send exception') 149 | resp = Client.new.track_exception(exception_instance, env, user) 150 | log('sent payload to api') 151 | 152 | resp 153 | end 154 | rescue Exception => e 155 | log('error sending exception to raygun, see failsafe logger for more information') 156 | 157 | if configuration.failsafe_logger 158 | failsafe_log("Problem reporting exception to Raygun: #{e.class}: #{e.message}\n\n#{e.backtrace.join("\n")}") 159 | end 160 | 161 | if retries_remaining > 0 162 | new_exception = e.exception("raygun4ruby encountered an exception processing your exception") 163 | new_exception.set_backtrace(e.backtrace) 164 | 165 | env[:custom_data] ||= {} 166 | env[:custom_data].merge!(original_stacktrace: exception_instance.backtrace, retries_remaining: retries_remaining) 167 | 168 | ::Raygun::Breadcrumbs::Store.clear 169 | 170 | track_exception(new_exception, env, user, retries_remaining - 1) 171 | else 172 | if configuration.raise_on_failed_error_report 173 | raise e 174 | else 175 | retries = configuration.error_report_max_attempts - retries_remaining 176 | if configuration.failsafe_logger 177 | failsafe_log("Gave up reporting exception to Raygun after #{retries} #{retries == 1 ? "retry" : "retries"}: #{e.class}: #{e.message}\n\n#{e.backtrace.join("\n")}") 178 | end 179 | end 180 | end 181 | end 182 | 183 | 184 | def print_api_key_warning 185 | $stderr.puts(NO_API_KEY_MESSAGE) 186 | end 187 | 188 | def should_report?(exception) 189 | if configuration.silence_reporting 190 | log('skipping reporting because Configuration.silence_reporting is enabled') 191 | 192 | return false 193 | end 194 | 195 | if configuration.ignore.flatten.include?(exception.class.to_s) 196 | log("skipping reporting of exception #{exception.class} because it is in the ignore list") 197 | 198 | return false 199 | end 200 | 201 | true 202 | end 203 | end 204 | end 205 | -------------------------------------------------------------------------------- /lib/raygun/affected_user.rb: -------------------------------------------------------------------------------- 1 | module Raygun 2 | class AffectedUser 3 | 4 | DEFAULT_MAPPING = { 5 | identifier: [ :id, :username ], 6 | email: :email, 7 | full_name: [ :full_name, :name ], 8 | first_name: :first_name, 9 | uuid: :uuid 10 | }.freeze 11 | SUPPORTED_ATTRIBUTES = DEFAULT_MAPPING.keys.freeze 12 | NAME_TO_RAYGUN_NAME_MAPPING = { 13 | identifier: :identifier, 14 | email: :email, 15 | full_name: :fullName, 16 | first_name: :firstName, 17 | uuid: :uuid 18 | }.freeze 19 | 20 | class << self 21 | def information_hash(user_object) 22 | if user_object.nil? || user_object.is_a?(String) 23 | handle_anonymous_user(user_object) 24 | else 25 | handle_known_user(user_object) 26 | end 27 | end 28 | 29 | private 30 | 31 | def handle_anonymous_user(user_object) 32 | result = { isAnonymous: true } 33 | result[:identifier] = user_object unless user_object.nil? 34 | result 35 | end 36 | 37 | def handle_known_user(user_object) 38 | SUPPORTED_ATTRIBUTES.reduce({ isAnonymous: false }) do |result, attribute| 39 | mapping = Raygun.configuration.affected_user_mapping 40 | method = mapping[attribute] 41 | 42 | value = if method.is_a? Proc 43 | method.call(user_object) 44 | else 45 | attributes = Array(method) 46 | attribute_to_use = attributes.select do |attr| 47 | user_object.respond_to?(attr, true) 48 | end.first 49 | 50 | user_object.send(attribute_to_use) unless attribute_to_use == nil 51 | end 52 | 53 | result[NAME_TO_RAYGUN_NAME_MAPPING[attribute]] = value unless value == nil 54 | result 55 | end 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/raygun/breadcrumbs.rb: -------------------------------------------------------------------------------- 1 | module Raygun 2 | module Breadcrumbs 3 | BREADCRUMB_LEVELS = [ 4 | :debug, 5 | :info, 6 | :warning, 7 | :error, 8 | :fatal 9 | ] 10 | 11 | def record_breadcrumb( 12 | message: nil, 13 | category: '', 14 | level: :info, 15 | timestamp: Time.now.utc.to_i, 16 | metadata: {}, 17 | class_name: nil, 18 | method_name: nil, 19 | line_number: nil 20 | ) 21 | class_name = class_name || self.class.name 22 | Raygun::Breadcrumbs::Store.record( 23 | message: message, 24 | category: category, 25 | level: level, 26 | timestamp: timestamp, 27 | metadata: metadata, 28 | class_name: class_name, 29 | method_name: method_name, 30 | line_number: line_number, 31 | ) 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/raygun/breadcrumbs/breadcrumb.rb: -------------------------------------------------------------------------------- 1 | module Raygun 2 | module Breadcrumbs 3 | class Breadcrumb 4 | ATTRIBUTES = [ 5 | :message, :category, :metadata, :class_name, 6 | :method_name, :line_number, :timestamp, :level, 7 | :type 8 | ] 9 | attr_accessor(*ATTRIBUTES) 10 | 11 | def build_payload 12 | payload = { 13 | message: message, 14 | category: category, 15 | level: Breadcrumbs::BREADCRUMB_LEVELS.index(level), 16 | CustomData: metadata, 17 | timestamp: timestamp, 18 | type: type 19 | } 20 | 21 | payload[:location] = "#{class_name}:#{method_name}" unless class_name == nil 22 | payload[:location] += ":#{line_number}" if payload.has_key?(:location) && line_number != nil 23 | 24 | Hash[payload.select do |k, v| 25 | v != nil 26 | end] 27 | end 28 | 29 | def size 30 | return message.length + 100 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/raygun/breadcrumbs/store.rb: -------------------------------------------------------------------------------- 1 | require_relative 'breadcrumb' 2 | 3 | module Raygun 4 | module Breadcrumbs 5 | class Store 6 | def self.initialize(with: []) 7 | Thread.current[:breadcrumbs] ||= with 8 | end 9 | 10 | def self.clear 11 | Thread.current[:breadcrumbs] = nil 12 | end 13 | 14 | def self.stored 15 | Thread.current[:breadcrumbs] 16 | end 17 | 18 | def self.record( 19 | message: nil, 20 | category: '', 21 | level: :info, 22 | timestamp: Time.now.utc.to_i, 23 | metadata: {}, 24 | class_name: nil, 25 | method_name: nil, 26 | line_number: nil 27 | ) 28 | raise ArgumentError.new('missing keyword: message') if message == nil 29 | crumb = Breadcrumb.new 30 | 31 | crumb.message = message 32 | crumb.category = category 33 | crumb.level = level 34 | crumb.metadata = metadata 35 | crumb.timestamp = timestamp 36 | crumb.type = 'manual' 37 | 38 | caller = caller_locations[1] 39 | crumb.class_name = class_name 40 | crumb.method_name = method_name || caller.label 41 | crumb.line_number = line_number || caller.lineno 42 | 43 | Thread.current[:breadcrumbs] << crumb if should_record?(crumb) 44 | end 45 | 46 | def self.any? 47 | stored != nil && stored.length > 0 48 | end 49 | 50 | def self.take_until_size(size) 51 | breadcrumb_size = 0 52 | 53 | stored.reverse.take_while do |crumb| 54 | breadcrumb_size += crumb.size 55 | 56 | breadcrumb_size < size 57 | end.reverse 58 | end 59 | 60 | private 61 | 62 | def self.should_record?(crumb) 63 | if stored.nil? 64 | if Raygun.configuration.debug 65 | Raygun.log('[Raygun.breadcrumbs] store is uninitialized while breadcrumb is being recorded, discarding breadcrumb') 66 | end 67 | 68 | return false 69 | end 70 | 71 | levels = Raygun::Breadcrumbs::BREADCRUMB_LEVELS 72 | 73 | active_level = levels.index(Raygun.configuration.breadcrumb_level) 74 | crumb_level = levels.index(crumb.level) || -1 75 | 76 | discard = crumb_level < active_level 77 | 78 | if discard && Raygun.configuration.debug 79 | Raygun.log("[Raygun.breadcrumbs] discarding breadcrumb because #{crumb.level} is below active breadcrumb level (#{Raygun.configuration.breadcrumb_level})") 80 | end 81 | 82 | !discard 83 | end 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lib/raygun/client.rb: -------------------------------------------------------------------------------- 1 | module Raygun 2 | # client for the Raygun REST APIv1 3 | # as per https://raygun.com/documentation/product-guides/crash-reporting/api/ 4 | class Client 5 | 6 | ENV_IP_ADDRESS_KEYS = %w(action_dispatch.remote_ip raygun.remote_ip REMOTE_ADDR) 7 | NO_API_KEY_MESSAGE = "[RAYGUN] Just a note, you've got no API Key configured, which means we can't report exceptions. Specify your Raygun API key using Raygun#setup (find yours at https://app.raygun.com)" 8 | MAX_BREADCRUMBS_SIZE = 100_000 9 | 10 | include HTTParty 11 | 12 | def initialize 13 | @api_key = require_api_key 14 | @headers = { 15 | "X-ApiKey" => @api_key 16 | } 17 | 18 | enable_http_proxy if Raygun.configuration.proxy_settings[:address] 19 | self.class.base_uri Raygun.configuration.api_url 20 | self.class.default_timeout(Raygun.configuration.error_report_send_timeout) 21 | end 22 | 23 | def require_api_key 24 | Raygun.configuration.api_key || print_api_key_warning 25 | end 26 | 27 | def track_exception(exception_instance, env = {}, user = nil) 28 | create_entry(build_payload_hash(exception_instance, env, user)) 29 | end 30 | 31 | private 32 | 33 | def enable_http_proxy 34 | self.class.http_proxy(Raygun.configuration.proxy_settings[:address], 35 | Raygun.configuration.proxy_settings[:port] || "80", 36 | Raygun.configuration.proxy_settings[:username], 37 | Raygun.configuration.proxy_settings[:password]) 38 | end 39 | 40 | def client_details 41 | { 42 | name: Raygun::CLIENT_NAME, 43 | version: Raygun::VERSION, 44 | clientUrl: Raygun::CLIENT_URL 45 | } 46 | end 47 | 48 | def error_details(exception) 49 | details = { 50 | className: exception.class.to_s, 51 | message: exception.message.to_s.encode('UTF-16', :undef => :replace, :invalid => :replace).encode('UTF-8'), 52 | stackTrace: (exception.backtrace || []).map { |line| stack_trace_for(line) }, 53 | } 54 | 55 | details.update(innerError: error_details(exception.cause)) if exception.respond_to?(:cause) && exception.cause 56 | 57 | details 58 | end 59 | 60 | def stack_trace_for(line) 61 | # see http://www.ruby-doc.org/core-2.0/Exception.html#method-i-backtrace 62 | file_name, line_number, method = line.split(":") 63 | { 64 | lineNumber: line_number, 65 | fileName: file_name, 66 | methodName: method ? method.gsub(/^in `(.*?)'$/, "\\1") : "(none)" 67 | } 68 | end 69 | 70 | def hostname 71 | Socket.gethostname 72 | end 73 | 74 | def version 75 | Raygun.configuration.version 76 | end 77 | 78 | def user_information(env) 79 | env["raygun.affected_user"] 80 | end 81 | 82 | def affected_user_present?(env) 83 | !!env["raygun.affected_user"] 84 | end 85 | 86 | def rack_env 87 | ENV["RACK_ENV"] 88 | end 89 | 90 | def rails_env 91 | ENV["RAILS_ENV"] 92 | end 93 | 94 | def request_information(env) 95 | Raygun.log('retrieving request information') 96 | 97 | return {} if env.nil? || env.empty? 98 | { 99 | hostName: env["SERVER_NAME"], 100 | url: env["PATH_INFO"], 101 | httpMethod: env["REQUEST_METHOD"], 102 | iPAddress: "#{ip_address_from(env)}", 103 | queryString: Rack::Utils.parse_nested_query(env["QUERY_STRING"]), 104 | headers: headers(env), 105 | form: form_params(env), 106 | rawData: raw_data(env) 107 | } 108 | end 109 | 110 | def headers(rack_env) 111 | rack_env.select { |k, v| k.to_s.start_with?("HTTP_") }.inject({}) do |hsh, (k, v)| 112 | hsh[normalize_raygun_header_key(k)] = v 113 | hsh 114 | end 115 | end 116 | 117 | def normalize_raygun_header_key(key) 118 | key.sub(/^HTTP_/, '') 119 | .sub(/_/, ' ') 120 | .split.map(&:capitalize).join(' ') 121 | .sub(/ /, '-') 122 | end 123 | 124 | def form_params(env) 125 | Raygun.log('retrieving form params') 126 | 127 | params = action_dispatch_params(env) || rack_params(env) || {} 128 | filter_params_with_blacklist(params, env["action_dispatch.parameter_filter"]) 129 | end 130 | 131 | def action_dispatch_params(env) 132 | env["action_dispatch.request.parameters"] 133 | end 134 | 135 | def rack_params(env) 136 | request = Rack::Request.new(env) 137 | request.params if env["rack.input"] 138 | end 139 | 140 | def raw_data(rack_env) 141 | Raygun.log('retrieving raw data') 142 | request = Rack::Request.new(rack_env) 143 | 144 | return unless Raygun.configuration.record_raw_data 145 | return if request.get? 146 | Raygun.log('passed raw_data checks') 147 | 148 | input = rack_env['rack.input'] 149 | 150 | if input && !request.form_data? 151 | input.rewind 152 | 153 | body = input.read(4096) || '' 154 | input.rewind 155 | 156 | body 157 | else 158 | {} 159 | end 160 | end 161 | 162 | def filter_custom_data(env) 163 | params = env.delete(:custom_data) || {} 164 | filter_params_with_blacklist(params, env["action_dispatch.parameter_filter"]) 165 | end 166 | 167 | # see https://raygun.com/documentation/product-guides/crash-reporting/api/ 168 | def build_payload_hash(exception_instance, env = {}, user = nil) 169 | Raygun.log('building payload hash') 170 | custom_data = filter_custom_data(env) || {} 171 | exception_custom_data = if exception_instance.respond_to?(:raygun_custom_data) 172 | exception_instance.raygun_custom_data 173 | else 174 | {} 175 | end 176 | 177 | tags = env.delete(:tags) || [] 178 | 179 | if rails_env 180 | tags << rails_env 181 | else 182 | tags << rack_env 183 | end 184 | 185 | combined_tags = [] 186 | 187 | if Raygun.configuration.tags.is_a?(Proc) 188 | configuration_tags = Raygun.configuration.tags.call(exception_instance, env) 189 | else 190 | configuration_tags = Raygun.configuration.tags 191 | end 192 | 193 | combined_tags.concat(configuration_tags) 194 | 195 | Raygun.log('set tags') 196 | 197 | grouping_key = env.delete(:grouping_key) 198 | correlation_id = env.delete(:correlation_id) 199 | 200 | configuration_custom_data = Raygun.configuration.custom_data 201 | configured_custom_data = if configuration_custom_data.is_a?(Proc) 202 | configuration_custom_data.call(exception_instance, env) 203 | else 204 | configuration_custom_data 205 | end 206 | 207 | Raygun.log('set custom data') 208 | 209 | error_details = { 210 | machineName: hostname, 211 | version: version, 212 | client: client_details, 213 | error: error_details(exception_instance), 214 | userCustomData: exception_custom_data.merge(custom_data).merge(configured_custom_data), 215 | tags: combined_tags.concat(tags).compact.uniq, 216 | request: request_information(env), 217 | environment: { 218 | utcOffset: Time.now.utc_offset / 3600 219 | } 220 | } 221 | 222 | # If we have breadcrumbs passed to us as context from another thread, then include them 223 | # Otherwise, use the default store (which is thread-local) 224 | ::Raygun::Breadcrumbs::Store.initialize(with: env.delete(:rg_breadcrumb_store)) if env.key?(:rg_breadcrumb_store) 225 | 226 | store = ::Raygun::Breadcrumbs::Store 227 | error_details[:breadcrumbs] = store.take_until_size(MAX_BREADCRUMBS_SIZE).map(&:build_payload) if store.any? 228 | 229 | Raygun.log('set details and breadcrumbs') 230 | 231 | error_details.merge!(groupingKey: grouping_key) if grouping_key 232 | error_details.merge!(correlationId: correlation_id) if correlation_id 233 | 234 | user_details = if affected_user_present?(env) 235 | user_information(env) 236 | elsif user != nil 237 | AffectedUser.information_hash(user) 238 | end 239 | error_details.merge!(user: user_details) unless user_details == nil 240 | 241 | Raygun.log('set user details') 242 | 243 | if Raygun.configuration.filter_payload_with_whitelist 244 | Raygun.log('filtering payload with whitelist') 245 | error_details = filter_payload_with_whitelist(error_details) 246 | end 247 | 248 | { 249 | occurredOn: Time.now.utc.iso8601, 250 | details: error_details 251 | } 252 | end 253 | 254 | def create_entry(payload_hash) 255 | Raygun.log('sending payload to api') 256 | 257 | self.class.post( 258 | "/entries", 259 | verify_peer: true, 260 | verify: true, 261 | headers: @headers, 262 | body: JSON.generate(payload_hash), 263 | ) 264 | end 265 | 266 | def filter_params_with_blacklist(params_hash = {}, extra_filter_keys = nil) 267 | filter_parameters = Raygun.configuration.filter_parameters 268 | 269 | if filter_parameters.is_a? Proc 270 | filter_parameters.call(params_hash) 271 | else 272 | filter_keys = (Array(extra_filter_keys) + filter_parameters).map(&:to_s) 273 | 274 | filter_params_with_array(params_hash, filter_keys) 275 | end 276 | end 277 | 278 | def filter_payload_with_whitelist(payload_hash) 279 | shape = Raygun.configuration.whitelist_payload_shape 280 | 281 | if shape.is_a? Proc 282 | shape.call(payload_hash) 283 | else 284 | # Always keep the client hash, so force it to true here 285 | Services::ApplyWhitelistFilterToPayload.new.call(shape.merge(client: true), payload_hash) 286 | end 287 | end 288 | 289 | def filter_params_with_array(params_hash, filter_keys) 290 | # Recursive filtering of (nested) hashes 291 | (params_hash || {}).inject({}) do |result, (k, v)| 292 | result[k] = case v 293 | when Hash 294 | filter_params_with_array(v, filter_keys) 295 | else 296 | filter_keys.any? { |fk| /#{fk}/i === k.to_s } ? "[FILTERED]" : v 297 | end 298 | result 299 | end 300 | end 301 | 302 | def ip_address_from(env_hash) 303 | ENV_IP_ADDRESS_KEYS.each do |key_to_try| 304 | return env_hash[key_to_try] unless env_hash[key_to_try].nil? || env_hash[key_to_try] == "" 305 | end 306 | "(Not Available)" 307 | end 308 | 309 | def print_api_key_warning 310 | $stderr.puts(NO_API_KEY_MESSAGE) 311 | end 312 | end 313 | end 314 | -------------------------------------------------------------------------------- /lib/raygun/configuration.rb: -------------------------------------------------------------------------------- 1 | module Raygun 2 | class Configuration 3 | 4 | def self.config_option(name) 5 | define_method(name) do 6 | read_value(name) 7 | end 8 | 9 | define_method("#{name}=") do |value| 10 | set_value(name, value) 11 | end 12 | end 13 | 14 | def self.proc_config_option(name) 15 | define_method(name) do |&block| 16 | set_value(name, block) unless block == nil 17 | read_value(name) 18 | end 19 | 20 | define_method("#{name}=") do |value| 21 | set_value(name, value) 22 | end 23 | end 24 | 25 | # Your Raygun API Key - this can be found on your dashboard at https://app.raygun.com 26 | config_option :api_key 27 | 28 | # Your JS Raygun API Key - Should be different to your api_key, it's best practice to separate your errors between front end and back end. 29 | config_option :js_api_key 30 | 31 | # Your JS Raygun API Version, defaults to latest as found on https://raygun.com/documentation/language-guides/javascript/crash-reporting/installation/ 32 | config_option :js_api_version 33 | 34 | # Array of exception classes to ignore 35 | config_option :ignore 36 | 37 | # Version to use 38 | config_option :version 39 | 40 | # Custom Data to send with each exception 41 | proc_config_option :custom_data 42 | 43 | # Tags to send with each exception 44 | proc_config_option :tags 45 | 46 | # Logger to use when we find an exception :) 47 | config_option :logger 48 | 49 | # Should we actually report exceptions to Raygun? (Usually disabled in Development mode, for instance) 50 | config_option :enable_reporting 51 | 52 | # Failsafe logger (for exceptions that happen when we're attempting to report exceptions) 53 | config_option :failsafe_logger 54 | 55 | # Which controller method should we call to find out the affected user? 56 | config_option :affected_user_method 57 | 58 | # Mapping of methods for the affected user object - which methods should we call for user information 59 | config_option :affected_user_mapping 60 | 61 | # Which parameter keys should we filter out by default? 62 | proc_config_option :filter_parameters 63 | 64 | # Should we switch to a white listing mode for keys instead of the default blacklist? 65 | config_option :filter_payload_with_whitelist 66 | 67 | # If :filter_payload_with_whitelist is true, which keys should we whitelist? 68 | proc_config_option :whitelist_payload_shape 69 | 70 | # Hash of proxy settings - :address, :port (defaults to 80), :username and :password (both default to nil) 71 | config_option :proxy_settings 72 | 73 | # Set this to true to have raygun4ruby log the reason why it skips reporting an exception 74 | config_option :debug 75 | 76 | # Override this if you wish to connect to a different Raygun API than the standard one 77 | config_option :api_url 78 | 79 | # Should Raygun include the raw request body in the payload? This will not include 80 | # form submissions and will not be filtered by the blacklist 81 | config_option :record_raw_data 82 | 83 | # Should the exceptions to Raygun be sent asynchronously? 84 | config_option :send_in_background 85 | 86 | # How long to wait for the POST request to the API server before timing out 87 | config_option :error_report_send_timeout 88 | 89 | # How many times to try sending to Raygun before giving up 90 | config_option :error_report_max_attempts 91 | 92 | # Whether or not we should raise an exception if the error reporting fails 93 | config_option :raise_on_failed_error_report 94 | 95 | # Should we register an error handler with [Rails' built in API](https://edgeguides.rubyonrails.org/error_reporting.html) 96 | config_option :register_rails_error_handler 97 | 98 | # Should we track jobs that are retried in Sidekiq (ones that raise Sidekiq::JobRetry::Handled). Set to "false" to ignore. 99 | config_option :track_retried_sidekiq_jobs 100 | 101 | # Exception classes to ignore by default 102 | IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound', 103 | 'ActionController::RoutingError', 104 | 'ActionController::InvalidAuthenticityToken', 105 | 'ActionDispatch::ParamsParser::ParseError', 106 | 'CGI::Session::CookieStore::TamperedWithCookie', 107 | 'ActionController::UnknownAction', 108 | 'AbstractController::ActionNotFound', 109 | 'Mongoid::Errors::DocumentNotFound', 110 | 'Sidekiq::JobRetry::Skip'] 111 | 112 | DEFAULT_FILTER_PARAMETERS = [ :password, :card_number, :cvv ] 113 | 114 | DEFAULT_WHITELIST_PAYLOAD_SHAPE_REQUEST = { 115 | hostName: true, 116 | url: true, 117 | httpMethod: true, 118 | iPAddress: true, 119 | queryString: true, 120 | headers: true, 121 | form: {}, # Set to empty hash so that it doesn't just filter out the whole thing, but instead filters out each individual param 122 | rawData: true 123 | }.freeze 124 | DEFAULT_WHITELIST_PAYLOAD_SHAPE = { 125 | machineName: true, 126 | version: true, 127 | error: true, 128 | userCustomData: true, 129 | tags: true, 130 | request: DEFAULT_WHITELIST_PAYLOAD_SHAPE_REQUEST 131 | }.freeze 132 | 133 | attr_reader :defaults 134 | 135 | def initialize 136 | @config_values = {} 137 | 138 | # set default attribute values 139 | @defaults = OpenStruct.new( 140 | ignore: IGNORE_DEFAULT, 141 | custom_data: {}, 142 | tags: [], 143 | enable_reporting: true, 144 | affected_user_method: :current_user, 145 | affected_user_mapping: AffectedUser::DEFAULT_MAPPING, 146 | filter_parameters: DEFAULT_FILTER_PARAMETERS, 147 | filter_payload_with_whitelist: false, 148 | whitelist_payload_shape: DEFAULT_WHITELIST_PAYLOAD_SHAPE, 149 | proxy_settings: {}, 150 | debug: false, 151 | api_url: 'https://api.raygun.com/', 152 | breadcrumb_level: :info, 153 | record_raw_data: false, 154 | send_in_background: false, 155 | error_report_send_timeout: 10, 156 | error_report_max_attempts: 1, 157 | raise_on_failed_error_report: false, 158 | track_retried_sidekiq_jobs: true 159 | ) 160 | end 161 | 162 | def [](key) 163 | read_value(key) 164 | end 165 | 166 | def []=(key, value) 167 | set_value(key, value) 168 | end 169 | 170 | def silence_reporting 171 | !enable_reporting 172 | end 173 | 174 | def silence_reporting=(value) 175 | self.enable_reporting = !value 176 | end 177 | 178 | def breadcrumb_level 179 | read_value(:breadcrumb_level) 180 | end 181 | 182 | def breadcrumb_level=(value) 183 | if Raygun::Breadcrumbs::BREADCRUMB_LEVELS.include?(value) 184 | set_value(:breadcrumb_level, value) 185 | elsif read_value(:debug) 186 | Raygun.log("[Raygun.configuration] unknown breadcrumb level: #{value} not setting") 187 | end 188 | end 189 | 190 | def affected_user_identifier_methods 191 | Raygun.deprecation_warning("Please note: You should now user config.affected_user_method_mapping.Identifier instead of config.affected_user_identifier_methods") 192 | read_value(:affected_user_mapping)[:identifier] 193 | end 194 | 195 | private 196 | 197 | def read_value(name) 198 | if @config_values.has_key?(name) 199 | @config_values[name] 200 | else 201 | @defaults.send(name) 202 | end 203 | end 204 | 205 | def set_value(name, value) 206 | @config_values[name] = value 207 | end 208 | 209 | end 210 | end 211 | -------------------------------------------------------------------------------- /lib/raygun/demo_exception.rb: -------------------------------------------------------------------------------- 1 | module Raygun 2 | 3 | class ItWorksException < StandardError; end 4 | 5 | module DemoException 6 | 7 | def track_test_exception 8 | Raygun.configuration.silence_reporting = false 9 | raise ItWorksException.new("Woohoo! Your Raygun<->Ruby connection is set up correctly") 10 | rescue ItWorksException => e 11 | response = Raygun.track_exception(e) 12 | 13 | if response.success? 14 | puts "Success! Now go check your Raygun Crash Reporting dashboard" 15 | else 16 | puts "Oh-oh, something went wrong - double check your API key" 17 | puts "API Key - " << Raygun.configuration.api_key << ")" 18 | puts "API Response - " << response 19 | end 20 | end 21 | 22 | end 23 | end -------------------------------------------------------------------------------- /lib/raygun/error.rb: -------------------------------------------------------------------------------- 1 | module Raygun 2 | class Error < StandardError 3 | attr_reader :raygun_custom_data 4 | 5 | def initialize(message, raygun_custom_data = {}) 6 | super(message) 7 | @raygun_custom_data = raygun_custom_data 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/raygun/error_subscriber.rb: -------------------------------------------------------------------------------- 1 | # Subscribes to errors using Rails' error reporting API 2 | # https://edgeguides.rubyonrails.org/error_reporting.html 3 | class Raygun::ErrorSubscriber 4 | def report(error, handled:, severity:, context:, source: nil) 5 | tags = context.delete(:tags) if context.is_a?(Hash) 6 | 7 | data = { 8 | custom_data: { 9 | "rails.error": { 10 | handled: handled, 11 | severity: severity, 12 | context: context, 13 | source: source 14 | }, 15 | }, 16 | tags: ["rails_error_reporter", *tags].compact 17 | } 18 | 19 | if source == "job.sidekiq" && defined?(Sidekiq) 20 | Raygun::SidekiqReporter.call(error, data) 21 | else 22 | Raygun.track_exception(error, data) 23 | end 24 | end 25 | end -------------------------------------------------------------------------------- /lib/raygun/javascript_tracker.rb: -------------------------------------------------------------------------------- 1 | # Client for injecting JavaScript code for tracking front end exceptions 2 | # https://raygun.com/docs/languages/javascript 3 | module Raygun 4 | class JavaScriptTracker 5 | def head_html 6 | return unless js_api_key? 7 | [ 8 | '' 15 | ].join('').html_safe 16 | end 17 | 18 | def body_html 19 | return unless js_api_key? 20 | [ 21 | '' 25 | ].join('').html_safe 26 | end 27 | 28 | private 29 | 30 | def js_api_key 31 | @js_api_key ||= Raygun.configuration.js_api_key 32 | end 33 | 34 | def js_api_version 35 | @js_api_version ||= Raygun.configuration.js_api_version ? "#{Raygun.configuration.js_api_version}/" : '' 36 | end 37 | 38 | def js_api_key? 39 | js_api_key.present? 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/raygun/middleware/breadcrumbs_store_initializer.rb: -------------------------------------------------------------------------------- 1 | module Raygun 2 | module Middleware 3 | class BreadcrumbsStoreInitializer 4 | def initialize(app) 5 | @app = app 6 | end 7 | 8 | def call(env) 9 | Breadcrumbs::Store.initialize 10 | 11 | begin 12 | @app.call(env) 13 | ensure 14 | Breadcrumbs::Store.clear 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/raygun/middleware/javascript_exception_tracking.rb: -------------------------------------------------------------------------------- 1 | module Raygun::Middleware 2 | class JavascriptExceptionTracking 3 | def initialize(app) 4 | @app = app 5 | end 6 | 7 | def call(env) 8 | status, headers, response = @app.call(env) 9 | 10 | # It's a html file, inject our JS 11 | if headers['Content-Type'] && headers['Content-Type'].include?('text/html') 12 | response = inject_javascript_to_response(response) 13 | end 14 | 15 | [status, headers, response] 16 | end 17 | 18 | def inject_javascript_to_response(response) 19 | if Raygun.configuration.js_api_key.present? 20 | if response.respond_to?('[]') 21 | response[0].gsub!('', "#{js_tracker.head_html}") 22 | response[0].gsub!('
11 | <%= yield %> 12 |', "#{js_tracker.body_html}") 23 | end 24 | 25 | if response.respond_to?(:body) # Rack::BodyProxy 26 | body = response.body 27 | body.gsub!('', "#{js_tracker.head_html}") 28 | body.gsub!('', "#{js_tracker.body_html}") 29 | end 30 | end 31 | 32 | response 33 | end 34 | 35 | private 36 | def js_tracker 37 | @js_tracker = Raygun::JavaScriptTracker.new 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/raygun/middleware/rack_exception_interceptor.rb: -------------------------------------------------------------------------------- 1 | module Raygun 2 | module Middleware 3 | class RackExceptionInterceptor 4 | 5 | def initialize(app) 6 | @app = app 7 | end 8 | 9 | def call(env) 10 | @app.call(env) 11 | rescue Exception => exception 12 | Raygun.track_exception(exception, env) if Raygun.configured? 13 | raise exception 14 | end 15 | 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/raygun/middleware/rails_insert_affected_user.rb: -------------------------------------------------------------------------------- 1 | module Raygun 2 | module Middleware 3 | # Adapted from the Rollbar approach https://github.com/rollbar/rollbar-gem/blob/master/lib/rollbar/middleware/rails/rollbar_request_store.rb 4 | class RailsInsertAffectedUser 5 | 6 | def initialize(app) 7 | @app = app 8 | end 9 | 10 | def call(env) 11 | @app.call(env) 12 | rescue Exception => exception 13 | controller = env["action_controller.instance"] 14 | affected_user_method = Raygun.configuration.affected_user_method 15 | 16 | if controller && controller.respond_to?(affected_user_method, true) 17 | user = controller.send(affected_user_method) 18 | 19 | env["raygun.affected_user"] = Raygun::AffectedUser.information_hash(user) 20 | end 21 | 22 | raise exception 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/raygun/railtie.rb: -------------------------------------------------------------------------------- 1 | require "raygun/middleware/rails_insert_affected_user" 2 | 3 | class Raygun::Railtie < Rails::Railtie 4 | initializer "raygun.configure_rails_initialization" do |app| 5 | 6 | # Thanks Airbrake: See https://github.com/rails/rails/pull/8624 7 | middleware = if defined?(ActionDispatch::DebugExceptions) 8 | if Rails::VERSION::STRING >= "5" 9 | ActionDispatch::DebugExceptions 10 | else 11 | # Rails >= 3.2.0 12 | "ActionDispatch::DebugExceptions" 13 | end 14 | else 15 | # Rails < 3.2.0 16 | "ActionDispatch::ShowExceptions" 17 | end 18 | 19 | raygun_middleware = [ 20 | Raygun::Middleware::RailsInsertAffectedUser, 21 | Raygun::Middleware::RackExceptionInterceptor, 22 | Raygun::Middleware::BreadcrumbsStoreInitializer, 23 | Raygun::Middleware::JavascriptExceptionTracking 24 | ] 25 | raygun_middleware = raygun_middleware.map(&:to_s) unless Rails::VERSION::STRING >= "5" 26 | raygun_middleware.each do |m| 27 | app.config.middleware.insert_after(middleware, m) 28 | end 29 | end 30 | 31 | config.to_prepare do 32 | Raygun.default_configuration.logger = Rails.logger 33 | Raygun.default_configuration.enable_reporting = Rails.env.production? 34 | 35 | Raygun::Railtie.setup_error_subscriber 36 | end 37 | 38 | rake_tasks do 39 | load "tasks/raygun.tasks" 40 | end 41 | 42 | def self.setup_error_subscriber 43 | if ::Rails.version.to_f >= 7.0 && Raygun.configuration.register_rails_error_handler 44 | Rails.error.subscribe(Raygun::ErrorSubscriber.new) 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/raygun/services/apply_whitelist_filter_to_payload.rb: -------------------------------------------------------------------------------- 1 | module Raygun 2 | module Services 3 | class ApplyWhitelistFilterToPayload 4 | def call(whitelist, payload) 5 | filter_hash(whitelist, payload) 6 | end 7 | 8 | private 9 | 10 | def filter_hash(whitelist, hash) 11 | # dup the input so each level of the hash is dup'd 12 | # not just the top as dup isn't deep 13 | hash = hash.dup 14 | 15 | hash.each do |k, v| 16 | unless whitelist && (whitelist[k] || whitelist[k.to_sym]) 17 | hash[k] = '[FILTERED]' 18 | end 19 | 20 | if v.is_a?(Hash) && whitelist[k].is_a?(Hash) 21 | hash[k] = filter_hash(whitelist[k], v) 22 | end 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/raygun/sidekiq.rb: -------------------------------------------------------------------------------- 1 | # Adapted from Bugsnag code, and Sidekiq Erorr Handling instructions 2 | # 3 | # SideKiq: https://github.com/sidekiq/sidekiq/wiki/Error-Handling 4 | # Bugsnag: https://github.com/bugsnag/bugsnag-ruby/blob/master/lib/bugsnag/sidekiq.rb 5 | 6 | module Raygun 7 | 8 | class SidekiqReporter 9 | def self.call(exception, context_hash = {}, config = nil) 10 | user = affected_user(context_hash) 11 | data = { 12 | custom_data: { 13 | sidekiq_context: context_hash 14 | }, 15 | tags: ['sidekiq'] 16 | } 17 | 18 | if exception.is_a?(Sidekiq::JobRetry::Handled) && exception.cause 19 | if Raygun.configuration.track_retried_sidekiq_jobs 20 | data.merge!(sidekiq_retried: true) 21 | exception = exception.cause 22 | else 23 | return false 24 | end 25 | end 26 | 27 | if exception.instance_variable_defined?(:@__raygun_correlation_id) && correlation_id = exception.instance_variable_get(:@__raygun_correlation_id) 28 | data.merge!(correlation_id: correlation_id) 29 | end 30 | ::Raygun.track_exception( 31 | exception, 32 | data, 33 | user 34 | ) 35 | end 36 | 37 | # Extracts affected user information out of a Sidekiq worker class 38 | def self.affected_user(context_hash) 39 | job = context_hash[:job] 40 | 41 | return if job.nil? || job['class'].nil? || !Module.const_defined?(job['class']) 42 | 43 | worker_class = Module.const_get(job['class']) 44 | affected_user_method = Raygun.configuration.affected_user_method 45 | 46 | return if worker_class.nil? || !worker_class.respond_to?(affected_user_method) 47 | 48 | worker_class.send(affected_user_method, job['args']) 49 | rescue => e 50 | return unless Raygun.configuration.failsafe_logger 51 | 52 | failsafe_log("Problem in sidekiq affected user tracking: #{e.class}: #{e.message}\n\n#{e.backtrace.join("\n")}") 53 | 54 | nil 55 | end 56 | end 57 | end 58 | 59 | Sidekiq.configure_server do |config| 60 | config.error_handlers << Raygun::SidekiqReporter 61 | end 62 | -------------------------------------------------------------------------------- /lib/raygun/version.rb: -------------------------------------------------------------------------------- 1 | module Raygun 2 | VERSION = "4.0.2" 3 | end 4 | -------------------------------------------------------------------------------- /lib/raygun4ruby.rb: -------------------------------------------------------------------------------- 1 | require "raygun" 2 | -------------------------------------------------------------------------------- /lib/resque/failure/raygun.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require 'resque' 3 | rescue LoadError 4 | raise "Can't find 'resque' gem. You'll need to require it before you require the Raygun Failure handler" 5 | end 6 | 7 | module Resque 8 | module Failure 9 | class Raygun < Base 10 | 11 | def save 12 | ::Raygun.track_exception(exception, 13 | custom_data: { 14 | resque: { 15 | worker: worker.to_s, 16 | queue: queue.to_s, 17 | job: payload['class'].to_s, 18 | args: payload['args'].inspect 19 | } 20 | } 21 | ) 22 | end 23 | 24 | end 25 | end 26 | end -------------------------------------------------------------------------------- /lib/tasks/raygun.tasks: -------------------------------------------------------------------------------- 1 | namespace :raygun do 2 | 3 | desc "Test Raygun integration" 4 | task :test => :environment do 5 | Raygun.track_test_exception 6 | end 7 | 8 | end -------------------------------------------------------------------------------- /raygun4ruby.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "raygun/version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "raygun4ruby" 8 | spec.version = Raygun::VERSION 9 | spec.authors = ["Mindscape", "Nik Wakelin"] 10 | spec.email = ["hello@raygun.com"] 11 | spec.description = %q{Ruby Adapter for Raygun} 12 | spec.summary = %q{This gem provides support for Ruby and Ruby on Rails for the Raygun.com error reporter} 13 | spec.homepage = "https://raygun.com" 14 | spec.license = "MIT" 15 | spec.required_ruby_version = ">= 2.0" 16 | 17 | spec.files = `git ls-files | grep -Ev "^(test)"`.split("\n") 18 | spec.test_files = `git ls-files -- test/*`.split("\n") 19 | 20 | spec.executables = [] 21 | spec.require_paths = ["lib"] 22 | 23 | spec.add_runtime_dependency "httparty", "> 0.13.7" 24 | spec.add_runtime_dependency "json" 25 | spec.add_runtime_dependency "rack" 26 | spec.add_runtime_dependency "concurrent-ruby" 27 | spec.add_runtime_dependency "ostruct" 28 | 29 | spec.add_development_dependency "bundler", ">= 2.3" 30 | spec.add_development_dependency "rake", ">= 12.3.3" 31 | spec.add_development_dependency "appraisal" 32 | spec.add_development_dependency "timecop" 33 | spec.add_development_dependency "minitest", "~> 5.11" 34 | spec.add_development_dependency "redis-namespace", ">= 1.3.1" 35 | spec.add_development_dependency "resque" 36 | spec.add_development_dependency "mocha" 37 | spec.add_development_dependency "pry" 38 | spec.add_development_dependency "webmock" 39 | 40 | spec.add_development_dependency "capybara" 41 | spec.add_development_dependency "rspec-rails" 42 | spec.add_development_dependency "launchy" 43 | spec.add_development_dependency "simplecov" 44 | end 45 | -------------------------------------------------------------------------------- /spec/features/javascript_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | feature 'JavaScript Tracking', feature: true do 4 | before { Raygun.configuration.js_api_key = nil } 5 | after { Raygun.configuration.js_api_key = nil } 6 | 7 | it "Includes the Raygun Javascript Middleware" do 8 | expect(Rails.application.config.middleware).to include(Raygun::Middleware::JavascriptExceptionTracking) 9 | end 10 | 11 | it "Does not inject the JS snippet" do 12 | visit root_path 13 | 14 | expect(page.html).to_not include('cdn.raygun.io/raygun4js/raygun.min.js') 15 | expect(page.html).to_not include('rg4js(') 16 | end 17 | 18 | context 'With a JS API Key' do 19 | before { Raygun.configuration.js_api_key = 'Sample key' } 20 | 21 | it "Injects the JS snippet" do 22 | visit root_path 23 | 24 | expect(page.html).to include('cdn.raygun.io/raygun4js/raygun.min.js') 25 | expect(page.html).to include('rg4js(') 26 | end 27 | 28 | it "Does not inject the JS snippet" do 29 | visit root_path(format: :json) 30 | 31 | expect(page.html).to_not include('cdn.raygun.io/raygun4js/raygun.min.js') 32 | expect(page.html).to_not include('rg4js(') 33 | end 34 | end 35 | 36 | context "With JS version overriden" do 37 | before do 38 | Raygun.configuration.js_api_version = "2.14.1" 39 | Raygun.configuration.js_api_key = "Sample key" 40 | end 41 | 42 | it "Uses the overriden version" do 43 | visit root_path 44 | 45 | expect(page.html).to include('cdn.raygun.io/raygun4js/2.14.1/raygun.min.js') 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | ruby '2.6.3' 5 | 6 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main' 7 | gem 'rails', '~> 6.1.4' 8 | # Use sqlite3 as the database for Active Record 9 | gem 'sqlite3', '~> 1.4' 10 | # Use Puma as the app server 11 | gem 'puma', '~> 5.0' 12 | # Use SCSS for stylesheets 13 | gem 'sass-rails', '>= 6' 14 | # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker 15 | gem 'webpacker', '~> 5.0' 16 | # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks 17 | gem 'turbolinks', '~> 5' 18 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 19 | gem 'jbuilder', '~> 2.7' 20 | # Use Redis adapter to run Action Cable in production 21 | # gem 'redis', '~> 4.0' 22 | # Use Active Model has_secure_password 23 | # gem 'bcrypt', '~> 3.1.7' 24 | 25 | # Use Active Storage variant 26 | # gem 'image_processing', '~> 1.2' 27 | 28 | # Reduces boot times through caching; required in config/boot.rb 29 | gem 'bootsnap', '>= 1.4.4', require: false 30 | 31 | group :development, :test do 32 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 33 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] 34 | end 35 | 36 | group :development do 37 | # Access an interactive console on exception pages or by calling 'console' anywhere in the code. 38 | gem 'web-console', '>= 4.1.0' 39 | # Display performance information such as SQL time and flame graphs for each request in your browser. 40 | # Can be configured to work on production as well see: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/README.md 41 | gem 'rack-mini-profiler', '~> 2.0' 42 | gem 'listen', '~> 3.3' 43 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 44 | gem 'spring' 45 | end 46 | 47 | group :test do 48 | # Adds support for Capybara system testing and selenium driver 49 | gem 'capybara', '>= 3.26' 50 | gem 'selenium-webdriver' 51 | # Easy installation and use of web drivers to run system tests with browsers 52 | gem 'webdrivers' 53 | end 54 | 55 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 56 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 57 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative "config/application" 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MindscapeHQ/raygun4ruby/5f6e26c34516ae09f9c1fac54e2a6f1df71030e4/spec/rails_applications/6.1.4/app/assets/images/.keep -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 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, or any plugin's 6 | * vendor/assets/stylesheets directory 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 bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | end 3 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MindscapeHQ/raygun4ruby/5f6e26c34516ae09f9c1fac54e2a6f1df71030e4/spec/rails_applications/6.1.4/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | def home 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/javascript/channels/consumer.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the `bin/rails generate channel` command. 3 | 4 | import { createConsumer } from "@rails/actioncable" 5 | 6 | export default createConsumer() 7 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/javascript/channels/index.js: -------------------------------------------------------------------------------- 1 | // Load all the channels within this directory and all subdirectories. 2 | // Channel files must be named *_channel.js. 3 | 4 | const channels = require.context('.', true, /_channel\.js$/) 5 | channels.keys().forEach(channels) 6 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/javascript/packs/application.js: -------------------------------------------------------------------------------- 1 | // This file is automatically compiled by Webpack, along with any other files 2 | // present in this directory. You're encouraged to place your actual application logic in 3 | // a relevant structure within app/javascript and only use these pack files to reference 4 | // that code so it'll be compiled. 5 | 6 | import Rails from "@rails/ujs" 7 | import Turbolinks from "turbolinks" 8 | import * as ActiveStorage from "@rails/activestorage" 9 | import "channels" 10 | 11 | Rails.start() 12 | Turbolinks.start() 13 | ActiveStorage.start() 14 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MindscapeHQ/raygun4ruby/5f6e26c34516ae09f9c1fac54e2a6f1df71030e4/spec/rails_applications/6.1.4/app/models/concerns/.keep -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/views/home/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
Find me in app/views/home/index.html.erb
3 |
<%= Raygun.configuration.js_api_key %>
4 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/views/home/index.json.erb: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /spec/rails_applications/6.1.4/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 | 6 | <%= csrf_meta_tags %> 7 | <%= csp_meta_tag %> 8 | 9 | 10 |
13 |