pax_global_header00006660000000000000000000000064126432544300014515gustar00rootroot0000000000000052 comment=e5f865cb03af2758d7c7963565792afad591d3d1 raven-ruby-0.15.3/000077500000000000000000000000001264325443000136755ustar00rootroot00000000000000raven-ruby-0.15.3/.gitignore000066400000000000000000000001721264325443000156650ustar00rootroot00000000000000coverage pkg ruby/ docs/_build .bundle *.gem gemfiles/*.lock Gemfile.lock .coveralls.yml .ruby-version .ruby-gemset .idea raven-ruby-0.15.3/.gitmodules000066400000000000000000000001551264325443000160530ustar00rootroot00000000000000[submodule "docs/_sentryext"] path = docs/_sentryext url = https://github.com/getsentry/sentry-doc-support raven-ruby-0.15.3/.rspec000066400000000000000000000000401264325443000150040ustar00rootroot00000000000000--colour --format documentation raven-ruby-0.15.3/.rubocop.yml000066400000000000000000000003501264325443000161450ustar00rootroot00000000000000inherit_from: .rubocop_todo.yml AllCops: Exclude: - 'lib/raven/okjson.rb' - 'lib/raven/backports/uri.rb' Lint/UnusedMethodArgument: Exclude: - 'lib/raven/backports/uri.rb' Style/SignalException: Enabled: false raven-ruby-0.15.3/.rubocop_todo.yml000066400000000000000000000247771264325443000172150ustar00rootroot00000000000000# This configuration was generated by # `rubocop --auto-gen-config` # on 2015-12-06 19:42:13 -0500 using RuboCop version 0.35.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 6 Lint/RescueException: Exclude: - 'lib/raven/base.rb' - 'lib/raven/integrations/delayed_job.rb' - 'lib/raven/integrations/rack.rb' - 'lib/raven/integrations/sidekiq.rb' - 'spec/raven/event_spec.rb' # Offense count: 1 Lint/ShadowingOuterLocalVariable: Exclude: - 'lib/raven/event.rb' # Offense count: 1 Lint/UnreachableCode: Exclude: - 'spec/raven/raven_spec.rb' # Offense count: 29 Metrics/AbcSize: Max: 73 # Offense count: 5 Metrics/BlockNesting: Max: 6 # Offense count: 2 # Configuration parameters: CountComments. Metrics/ClassLength: Max: 200 # Offense count: 15 Metrics/CyclomaticComplexity: Max: 20 # Offense count: 112 # Configuration parameters: AllowURI, URISchemes. Metrics/LineLength: Max: 155 # Offense count: 32 # Configuration parameters: CountComments. Metrics/MethodLength: Max: 57 # Offense count: 2 # Configuration parameters: CountComments. Metrics/ModuleLength: Max: 413 # Offense count: 14 Metrics/PerceivedComplexity: Max: 20 # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/AccessModifierIndentation: Enabled: false # Offense count: 9 # Cop supports --auto-correct. Style/Alias: Exclude: - 'lib/raven/base.rb' - 'lib/raven/event.rb' - 'lib/raven/integrations/rake.rb' # Offense count: 2 # Cop supports --auto-correct. Style/AlignArray: Exclude: - 'spec/raven/event_spec.rb' - 'spec/raven/processors/utf8conversion_spec.rb' # Offense count: 6 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/AlignParameters: Exclude: - 'lib/raven/client.rb' - 'lib/raven/event.rb' - 'lib/raven/integrations/delayed_job.rb' - 'spec/raven/event_spec.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/AndOr: Exclude: - 'lib/raven/interfaces/http.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, ProceduralMethods, FunctionalMethods, IgnoredMethods. Style/BlockDelimiters: Exclude: - 'spec/raven/cli_spec.rb' # Offense count: 27 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/BracesAroundHashParameters: Exclude: - 'spec/raven/event_spec.rb' - 'spec/raven/processors/removecirculareferences_spec.rb' - 'spec/raven/processors/sanitizedata_processor_spec.rb' # Offense count: 1 Style/CaseEquality: Exclude: - 'lib/raven/event.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: IndentWhenRelativeTo, SupportedStyles, IndentOneStep. Style/CaseIndentation: Enabled: false # Offense count: 6 # Configuration parameters: EnforcedStyle, SupportedStyles. Style/ClassAndModuleChildren: Exclude: - 'lib/raven/processor/removecircularreferences.rb' - 'lib/raven/processor/removestacktrace.rb' - 'lib/raven/processor/sanitizedata.rb' - 'lib/raven/processor/utf8conversion.rb' - 'spec/raven/event_spec.rb' # Offense count: 37 # Configuration parameters: Exclude. Style/Documentation: Enabled: false # Offense count: 1 Style/DoubleNegation: Exclude: - 'lib/raven/configuration.rb' # Offense count: 2 Style/EachWithObject: Exclude: - 'lib/raven/processor/removecircularreferences.rb' - 'lib/raven/processor/sanitizedata.rb' # Offense count: 2 # Cop supports --auto-correct. Style/ElseAlignment: Exclude: - 'lib/raven/interfaces/stack_trace.rb' # Offense count: 33 # Cop supports --auto-correct. Style/EmptyLines: Exclude: - 'spec/spec_helper.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/EmptyLinesAroundModuleBody: Exclude: - 'lib/raven/backtrace.rb' - 'lib/raven/event.rb' - 'lib/raven/integrations/delayed_job.rb' - 'lib/raven/interfaces.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: AllowForAlignment. # Style/ExtraSpacing: # Exclude: # - 'lib/raven/event.rb' # - 'lib/raven/processor/sanitizedata.rb' # Offense count: 2 # Configuration parameters: Exclude. Style/FileName: Exclude: - 'lib/sentry-raven-without-integrations.rb' - 'lib/sentry-raven.rb' # Offense count: 9 # Configuration parameters: EnforcedStyle, SupportedStyles. Style/FormatString: Exclude: - 'lib/raven/event.rb' - 'lib/raven/integrations/rack.rb' # Offense count: 2 # Configuration parameters: MinBodyLength. Style/GuardClause: Exclude: - 'lib/raven/base.rb' - 'lib/raven/configuration.rb' # Offense count: 119 # Cop supports --auto-correct. # Configuration parameters: SupportedStyles, UseHashRocketsWithSymbolValues. Style/HashSyntax: EnforcedStyle: hash_rockets # Offense count: 24 # Cop supports --auto-correct. # Configuration parameters: MaxLineLength. Style/IfUnlessModifier: Exclude: - 'lib/raven/base.rb' - 'lib/raven/event.rb' - 'lib/raven/interfaces/exception.rb' - 'lib/raven/interfaces/single_exception.rb' # Offense count: 58 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/IndentHash: Enabled: false # Offense count: 4 # Cop supports --auto-correct. # Configuration parameters: Width. Style/IndentationWidth: Exclude: - 'lib/raven/event.rb' - 'lib/raven/interfaces/stack_trace.rb' - 'spec/raven/event_spec.rb' # Offense count: 6 # Cop supports --auto-correct. Style/Lambda: Exclude: - 'spec/raven/configuration_spec.rb' - 'spec/raven/integrations/rack_spec.rb' - 'spec/raven/raven_spec.rb' # Offense count: 4 # Cop supports --auto-correct. Style/LeadingCommentSpace: Exclude: - 'lib/raven/cli.rb' - 'lib/raven/interfaces/http.rb' - 'lib/raven/processor/sanitizedata.rb' - 'lib/raven/transports.rb' # Offense count: 1 Style/MultilineBlockChain: Exclude: - 'lib/raven/event.rb' # Offense count: 2 # Cop supports --auto-correct. Style/NegatedIf: Exclude: - 'lib/raven/client.rb' # Offense count: 5 # Cop supports --auto-correct. Style/NumericLiterals: MinDigits: 17 # Offense count: 4 # Cop supports --auto-correct. Style/ParallelAssignment: Exclude: - 'lib/raven/interfaces/stack_trace.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: PreferredDelimiters. Style/PercentLiteralDelimiters: Exclude: - 'lib/raven/base.rb' - 'spec/raven/configuration_spec.rb' - 'spec/raven/raven_spec.rb' # Offense count: 1 # Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist. Style/PredicateName: Exclude: - 'lib/raven/linecache.rb' # Offense count: 3 # Cop supports --auto-correct. Style/Proc: Exclude: - 'lib/raven/integrations/sidekiq.rb' - 'spec/raven/configuration_spec.rb' - 'spec/raven/raven_spec.rb' # Offense count: 10 # Configuration parameters: EnforcedStyle, SupportedStyles. Style/RaiseArgs: Enabled: false # Offense count: 1 # Cop supports --auto-correct. Style/RedundantBegin: Exclude: - 'lib/raven/processor/sanitizedata.rb' # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: AllowMultipleReturnValues. Style/RedundantReturn: Exclude: - 'lib/raven/linecache.rb' - 'spec/spec_helper.rb' # Offense count: 45 # Cop supports --auto-correct. Style/RedundantSelf: Exclude: - 'lib/raven/backtrace.rb' - 'lib/raven/base.rb' - 'lib/raven/client.rb' - 'lib/raven/configuration.rb' - 'lib/raven/interfaces/http.rb' - 'lib/raven/interfaces/stack_trace.rb' - 'lib/raven/transports/http.rb' - 'lib/raven/transports/udp.rb' # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes. Style/RegexpLiteral: Exclude: - 'lib/raven/backtrace.rb' # Offense count: 6 # Cop supports --auto-correct. Style/RescueModifier: Exclude: - 'lib/raven/configuration.rb' - 'lib/raven/event.rb' # Offense count: 1 # Configuration parameters: Methods. Style/SingleLineBlockParams: Exclude: - 'lib/raven/client.rb' # Offense count: 5 # Cop supports --auto-correct. Style/SingleSpaceBeforeFirstArg: Exclude: - 'exe/raven' # Offense count: 45 # Cop supports --auto-correct. Style/SpaceAfterComma: Exclude: - 'exe/raven' - 'lib/raven/integrations/sidekiq.rb' - 'lib/raven/processor/sanitizedata.rb' - 'spec/raven/processors/sanitizedata_processor_spec.rb' # Offense count: 72 # Cop supports --auto-correct. # Configuration parameters: MultiSpaceAllowedForOperators. Style/SpaceAroundOperators: Exclude: - 'lib/raven/client.rb' - 'lib/raven/event.rb' - 'lib/raven/integrations/sidekiq.rb' - 'lib/raven/interfaces/stack_trace.rb' - 'spec/raven/okjson_spec.rb' - 'spec/raven/processors/sanitizedata_processor_spec.rb' # Offense count: 6 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/SpaceBeforeBlockBraces: Enabled: false # Offense count: 17 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. Style/SpaceInsideBlockBraces: Enabled: false # Offense count: 3 # Cop supports --auto-correct. Style/SpaceInsideBrackets: Exclude: - 'lib/raven/integrations/rack.rb' - 'lib/raven/interfaces.rb' # Offense count: 22 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SupportedStyles. Style/SpaceInsideHashLiteralBraces: Enabled: false # Offense count: 5 # Cop supports --auto-correct. Style/SpecialGlobalVars: Exclude: - 'lib/raven/base.rb' - 'sentry-raven.gemspec' # Offense count: 201 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. Style/StringLiterals: Enabled: false # Offense count: 33 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyleForMultiline, SupportedStyles. Style/TrailingComma: Exclude: - 'lib/raven/configuration.rb' - 'lib/raven/event.rb' - 'lib/raven/integrations/rails/active_job.rb' - 'lib/raven/logger.rb' - 'spec/raven/event_spec.rb' - 'spec/raven/processors/sanitizedata_processor_spec.rb' raven-ruby-0.15.3/.travis.yml000066400000000000000000000013321264325443000160050ustar00rootroot00000000000000language: ruby rvm: - jruby-19mode - ree - 1.8.7 - 1.9.3 - 2.1.8 - 2.2.4 - 2.3.0 - ruby-head gemfile: - gemfiles/rails32.gemfile - gemfiles/rails41.gemfile - gemfiles/rails42.gemfile sudo: false notifications: irc: channels: "irc.freenode.org#sentry" on_success: change on_failure: change matrix: allow_failures: - rvm: ruby-head exclude: - rvm: 1.8.7 gemfile: gemfiles/rails42.gemfile - rvm: 1.8.7 gemfile: gemfiles/rails41.gemfile - rvm: ree gemfile: gemfiles/rails42.gemfile - rvm: ree gemfile: gemfiles/rails41.gemfile - rvm: 2.2.4 gemfile: gemfiles/rails32.gemfile - rvm: ruby-head gemfile: gemfiles/rails32.gemfile raven-ruby-0.15.3/Gemfile000066400000000000000000000001601264325443000151650ustar00rootroot00000000000000source "https://rubygems.org/" gemspec group :test do gem "rack" end group :development do gem "pry" end raven-ruby-0.15.3/LICENSE000066400000000000000000000251371264325443000147120ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2015 Functional Software, Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. raven-ruby-0.15.3/Makefile000066400000000000000000000003721264325443000153370ustar00rootroot00000000000000# lol VERSION := `cat lib/raven/version.rb | grep -e 'VERSION =' | cut -c 14- | rev | cut -c 2- | rev` test: bundle install bundle exec rubocop bundle exec rake spec release: gem build sentry-raven.gemspec gem push sentry-raven-${VERSION}.gem raven-ruby-0.15.3/README.md000066400000000000000000000033661264325443000151640ustar00rootroot00000000000000# Raven-Ruby [![Gem Version](https://img.shields.io/gem/v/sentry-raven.svg)](https://rubygems.org/gems/sentry-raven) [![Build Status](https://img.shields.io/travis/getsentry/raven-ruby/master.svg)](https://travis-ci.org/getsentry/raven-ruby) A client and integration layer for the [Sentry](https://github.com/getsentry/sentry) error reporting API. ## Requirements We test on Ruby MRI 1.8.7/REE, 1.9.3, 2.0, 2.1 and 2.2. JRuby support is experimental - check TravisCI to see if the build is passing or failing. ## Getting Started ### Install ```ruby gem "sentry-raven" #, :github => "getsentry/raven-ruby" ``` ### Set SENTRY_DSN ```bash # Set your SENTRY_DSN environment variable. export SENTRY_DSN=http://public:secret@example.com/project-id ``` ```ruby # Or you can configure the client in the code (not recommended - keep your DSN secret!) Raven.configure do |config| config.dsn = 'http://public:secret@example.com/project-id' end ``` ### Call If you use Rails, you're already done - no more configuration required! Check [Integrations](https://docs.getsentry.com/hosted/clients/ruby/integrations/) for more details on other gems Sentry integrates with automatically. Otherwise, Raven supports two methods of capturing exceptions: ```ruby Raven.capture do # capture any exceptions which happen during execution of this block 1 / 0 end begin 1 / 0 rescue ZeroDivisionError => exception Raven.capture_exception(exception) end ``` ## More Information * [Documentation](https://docs.getsentry.com/hosted/clients/ruby/) * [Bug Tracker](https://github.com/getsentry/raven-ruby/issues>) * [Code](https://github.com/getsentry/raven-ruby>) * [Mailing List](https://groups.google.com/group/getsentry>) * [IRC](irc://irc.freenode.net/sentry>) (irc.freenode.net, #sentry) raven-ruby-0.15.3/Rakefile000066400000000000000000000011621264325443000153420ustar00rootroot00000000000000require 'rake' require 'raven' require 'rubygems/package_task' gemspec = Gem::Specification.load(Dir['*.gemspec'].first) Gem::PackageTask.new(gemspec).define begin require 'rubygems' require 'rspec/core/rake_task' if RUBY_VERSION > '1.8.7' require 'rubocop/rake_task' RuboCop::RakeTask.new(:rubocop) end RSpec::Core::RakeTask.new(:spec) do |spec| spec.pattern = 'spec/**/*_spec.rb' end rescue LoadError task :spec do abort "Rspec is not available. bundle install to run unit tests" end end if RUBY_VERSION > '1.8.7' task :default => [:rubocop, :spec] else task :default => :spec end raven-ruby-0.15.3/changelog.md000066400000000000000000000155611264325443000161560ustar00rootroot000000000000000.15.3 ------ - Double exception reporting in Rails FIXED! [nateberkopec, #422] - Rails 3 users having issues with undefined runner fixed [nateberkopec, #428] - Sidekiq integration works properly when ActiveJob enabled [mattrobenolt] - Fix problems with invalid UTF-8 in exception messages [nateberkopec, #426] - Backtraces now consider "exe" directories part of the app [nateberkopec, #420] - Sinatra::NotFound now ignored by default [drcapulet, #383] - Release versions now properly set. Support for Heroku, Capistrano, and Git. [iloveitaly #377, Sija #380] - DelayedJob integration plays well with ActiveJob [kkumler, #378] - DelayedJob handlers now truncated [nateberkopec, #431] - Tons of code quality improvements [amatsuda, ddrmanxbxfr, pmbrent, cpizzaia, wdhorton, PepperTeasdale] 0.15.2 ------ - Reverted ActiveJob support due to conflicts [#368] 0.15.1 ------ - Fix ActiveJob support [greysteil, #367] 0.15.0 ------ - Remove Certifi and use default Ruby SSL config [zanker, #352] - Support for ``fingerprint`` [dcramer] - Improved documentation and tests around various attributes [dcramer] - Allow configurable integrations [cthornton] - Prevent recursion with ``Exception.cause`` [dcramer, #357] - Use empty hash if false-y value [GeekOnCoffee, #354] - Correct some behavior with at_exit error capturing [kratob, #355] - Sanitize matches whole words [alyssa, #361] - Expose more debugging info to active_job integration [tonywok, #365] - Capture exceptions swallowed by rails [robertclancy, #343] - Sanitize the query string when the key is a symbol [jason-o-matic, #349] - Moved documentation to docs.getsentry.com [mitsuhiko] 0.14.0 ------ - Correct handling of JRuby stacktraces [dcramer] - Better handling of unreachable file contexts [dcramer, #335] - SSL is now default ON [dcramer, #338] - Capture exceptions in runner tasks [eugeneius, #339] - ActiveJob integration [lucasmazza, #327] - Cleanup return values of async blocks [lucasmazza, #344] - Better handling when sending NaN/Infinity JSON values [Alric, #345] - Fix issues with digest/md5 namespace [lsb, #346] 0.13.3 ------ - Fix a deprecation warning being shown in regular operation [ripta, #332] 0.13.2 ------ - DelayedJob integration now includes the job id [javawizard, #321] - Rails integration now works properly when you're not using all parts of Rails (e.g. just ActiveRecord) [lucasmazza, #323] - Bugfix CLI tool when async config is on [if1live, #324] - Fix and standardize tag hierarchies. Event tags > context tags > configuration tags in all cases. [JonathanBatten, #322 and eugeneius, #330] - Using #send on Client, Base, and Transports is now deprecated. See [the commit](https://github.com/getsentry/raven-ruby/commit/9f482022a648ab662c22177ba24fd2e2b6794c34) (or the deprecation message) for their replacements. [nateberkopec, #326] - You can now disable credit-card-like value filtering. [codekitchen, #329] 0.13.1 ------ - Raven::Transports::HTTP#send returns the response now. [eagletmt, #317] - Filenames now work a lot better when you vendor your gems. [eugeneius, #316] - Fix raven:test issue when testing non-async configurations. [weynsee, #318] - Fix blockless Raven#capture. [dinosaurjr, #320] - Fix some log messages [eagletmt, #319] 0.13.0 ------ - Support exception chaining [javawizard, #312] - Add support for sending release version [eugeneius, #310] - Better status reports on configuration [faber, #309] - Client "send" method accepts an event in object or hash format - this will make it much easier to send Sentry events in a delayed job! [marclennox, #300] - Fix duplicate fields in SanitizeData [wyattisimo, #294] - Always preserve filename paths under project_root [eugeneius, #291] - Truncate project root prefixes from filenames [eagletmt, #278] - Renamed should_send callback to should_capture [nateberkopec, #270] - Silencing the ready message now happens in the config as normal [nateberkopec, #260] - Various internal refactorings [see here](https://github.com/getsentry/raven-ruby/compare/0-12-stable...master) 0.12.3 ------ - URL query parameters are now sanitized for sensitive data [pcorliss, #275] - Raven::Client can now use a proxy server when sending events to Sentry [dcramer, #277] - Raven::Client will now use a timed backoff strategy if the server fails [codekitchen, #267] - Automatic integration loading is now a lot less brittle [dcramer, handlers, #263, #264] - Fixed some issues with prefixes and DSN strings [nateberkopec, #259] - If Raven is initialized without a server config, it will no longer send events [nateberkopec, #258] - Slightly nicer credit-card-like number scrubbing [nateberkopec, #254] - Fix some exceptions not being caught by Sidekiq middleware [nateberkopec, #251] - Uncommon types are now encoded correctly [nateberkopec, #249] 0.12.2 ------ - Security fix where exponential numbers in specially crafted params could cause a CPU attack [dcramer, #262] 0.12.1 ------ - Integrations (Sidekiq, DelayedJob, etc) now load independently of your Gemfile order. [nateberkopec, #236] - Fixed bug where setting tags mutated your configuration [berg, #239] - Fixed several issues with SanitizeData and UTF8 sanitization processors [nateberkopec, #238, #241, #244] 0.12.0 ------ - You can now give additional fields to the SanitizeData processor. Values matched are replaced by the string mask (*********). Full documentation (and how to use with Rails config.filter_parameters) [here](https://docs.getsentry.com/hosted/clients/ruby/config/). [jamescway, #232] - An additional processor has been added, though it isn't turned on by default: RemoveStacktrace. Use it to remove stacktraces from exception reports. [nateberkopec, #233] - Dependency on `uuidtools` has been removed. [nateberkopec, #231] 0.11.2 ------ - Fix some issues with the SanitizeData processor when processing strings that look like JSON 0.11.1 ------ - Raven now captures exceptions in Rake tasks automatically. [nateberkopec, troelskn #222] - There is now a configuration option called ```should_send``` that can be configured to use a Proc to determine whether or not an event should be sent to Sentry. This can be used to implement rate limiters, etc. [nateberkopec, #221] - Raven now includes three event processors by default instead of one, which can be turned on and off independently. [nateberkopec, #223] - Fixed bug with YAJL compatibility. [nateberkopec, #223] 0.10.1 ------ - Updated to RSpec 3. - Apply filters to encoded JSON data. 0.10.0 ------ - Events are now sent to Sentry in all environments. To change this behavior, either unset ```SENTRY_DSN``` or explicitly configure it via ```Raven.configure```. - gzip is now the default encoding - Removed hashie dependency 0.9.0 ----- - Native support for Delayed::Job [pkuczynski, #176] - Updated to Sentry protocol version 5 0.5.0 ----- - Rails 2 support [sluukonen, #45] - Controller methods in Rails [jfirebaugh] - Runs by default in any environment other than test, cucumber, or development. [#81] raven-ruby-0.15.3/docs/000077500000000000000000000000001264325443000146255ustar00rootroot00000000000000raven-ruby-0.15.3/docs/Makefile000066400000000000000000000107601264325443000162710ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = ./_build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Sentry.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Sentry.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Sentry" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Sentry" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." raven-ruby-0.15.3/docs/_sentryext/000077500000000000000000000000001264325443000170315ustar00rootroot00000000000000raven-ruby-0.15.3/docs/conf.py000066400000000000000000000163101264325443000161250ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Sentry documentation build configuration file, created by # sphinx-quickstart on Wed Oct 20 16:21:42 2010. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import os import sys import datetime # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. #extensions = ['sphinxtogithub'] extensions = ['sphinx.ext.intersphinx'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Raven' copyright = u'%s, Functional Software Inc.' % datetime.datetime.today().year # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = __import__('pkg_resources').get_distribution('raven').version # The full version, including alpha/beta/rc tags. release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] intersphinx_mapping = { } # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'nature' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. html_theme_path = ['_themes'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = "_static/logo.png" # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Ravendoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'Raven.tex', u'Raven Ruby Documentation', u'David Cramer', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'raven', u'Raven Ruby Documentation', [u'Functional Software Inc.'], 1) ] if os.environ.get('SENTRY_FEDERATED_DOCS') != '1': sys.path.insert(0, os.path.abspath('_sentryext')) import sentryext sentryext.activate() raven-ruby-0.15.3/docs/config.rst000066400000000000000000000133701264325443000166300ustar00rootroot00000000000000Configuration ============= Configuration is passed as part of the client initialization: .. code-block:: javascript Raven.configure do |config| config.dsn = '___DSN___' config.attr = 'value' end Optional settings ----------------- .. describe:: async When an error occurs, the notification is immediately sent to Sentry. Raven can be configured to send notifications asynchronously: .. code-block:: ruby config.async = lambda { |event| SentryJob.perform_later(event) } .. describe:: encoding While unlikely that you'll need to change it, by default Raven compresses outgoing messages with gzip. This has a slight impact on performance, but due to the size of many Ruby stacktrace it's required for the serve to accept the content. To disable gzip, set the encoding to 'json': .. code-block:: ruby config.encoding = 'json' .. describe:: environments As of v0.10.0, events will be sent to Sentry in all environments. If you do not wish to send events in an environment, we suggest you unset the SENTRY_DSN variable in that environment. Alternately, you can configure Raven to run only in certain environments by configuring the environments whitelist. For example, to only run Sentry in production: .. code-block:: ruby config.environments = %w[ production ] Sentry automatically sets the current environment to RAILS_ENV, or if it is not present, RACK_ENV. If you are using Sentry outside of Rack or Rails, you'll need to set the current environment yourself: .. code-block:: ruby config.current_environment = 'my_cool_environment' .. describe:: excluded_exceptions If you never wish to be notified of certain exceptions, specify 'excluded_exceptions' in your config file. In the example below, the exceptions Rails uses to generate 404 responses will be suppressed. .. code-block:: ruby config.excluded_exceptions = ['ActionController::RoutingError', 'ActiveRecord::RecordNotFound'] You can find the list of exceptions that are excluded by default in ``Raven::Configuration::IGNORE_DEFAULT``. Remember you'll be overriding those defaults by setting this configuration. .. describe:: logger The name of the logger used by Sentry. Default: ``''`` .. code-block:: ruby config.logger = ::Logger.new(STDOUT) Raven respects logger levels. .. describe:: processors If you need to sanitize or pre-process (before its sent to the server) data, you can do so using the Processors implementation. By default, a few processors are installed. The most important is ``Raven::Processor::SanitizeData``, which will attempt to sanitize keys that match various patterns (e.g. password) and values that resemble credit card numbers. To specify your own (or to remove the defaults), simply pass them with your configuration: .. code-block:: ruby config.processors = [Raven::Processor::SanitizeData] Check out ``Raven::Processor::SanitizeData`` to see how a Processor is implemented. You can also specify values to be sanitized. Any strings matched will be replaced with the string mask (********). One good use for this is to copy Rails' filter_parameters: .. code-block:: ruby config.sanitize_fields = Rails.application.config.filter_parameters.map(&:to_s) By default, Sentry sends up a stacktrace with an exception. This stacktrace may contain data which you may consider to be sensitive, including lines of source code, line numbers, module names, and source paths. To wipe the stacktrace from all error reports, require and add the RemoveStacktrace processor: .. code-block:: ruby require 'raven/processor/removestacktrace' Raven.configure do |config| config.processors << Raven::Processor::RemoveStacktrace end .. describe:: release Track the version of your application in Sentry. .. code-block:: ruby config.release = '721e41770371db95eee98ca2707686226b993eda' .. describe:: should_capture By providing a proc or lambda, you can control what events are captured. Events are passed to the Proc or lambda you provide - returning false will stop the event from sending to Sentry: .. code-block:: ruby config.should_capture = Proc.new { |e| true unless e.contains_sensitive_info? } .. describe:: silence_ready Upon start, Raven will write the following message to the log at the INFO level: `` ** [out :: hostname.example.com] I, [2014-07-22T15:32:57.498368 #30897] INFO -- : ** [Raven] Raven 0.9.4 ready to catch errors" `` You can turn off this message: .. code-block:: ruby config.silence_ready = true .. describe:: ssl_verification By default SSL certificate verification is enabled in the client. It can be disabled. .. code-block:: ruby config.ssl_verification = false .. describe:: tags Default tags to send with each event. .. code-block:: ruby config.tags = { environment: Rails.env } Environment Variables --------------------- .. describe:: SENTRY_DSN After you complete setting up a project, you'll be given a value which we call a DSN, or Data Source Name. It looks a lot like a standard URL, but it's actually just a representation of the configuration required by Raven (the Sentry client). It consists of a few pieces, including the protocol, public and secret keys, the server address, and the project identifier. With Raven, you may either set the SENTRY_DSN environment variable (recommended), or set your DSN manually in a config block: .. code-block:: ruby # in Rails, this might be in config/initializers/sentry.rb Raven.configure do |config| config.dsn = 'http://public:secret@example.com/project-id' end raven-ruby-0.15.3/docs/context.rst000066400000000000000000000037511264325443000170510ustar00rootroot00000000000000Context ======= Additional context can be passed to the capture methods. This allows you to record extra information that could help you identify the root cause of the issue or who the error happened for. .. sourcecode:: ruby Raven.capture_message "My event", logger: 'logger', extra: { my_custom_variable: 'value' }, tags: { environment: 'production' } The following attributes are available: * ``logger``: the logger name to record this event under * ``level``: a string representing the level of this event (fatal, error, warning, info, debug) * ``server_name``: the hostname of the server * ``tags``: a mapping of tags describing this event * ``extra``: a mapping of arbitrary context Providing Request Context ------------------------- Most of the time you're not actually calling out to Raven directly, but you still want to provide some additional context. This lifecycle generally constists of something like the following: * Set some context via a middleware (e.g. the logged in user) * Send all given context with any events during the request lifecycle * Cleanup context There are three primary methods for providing request context: .. sourcecode:: ruby # bind the logged in user Raven.user_context email: 'foo@example.com' # tag the request with something interesting Raven.tags_context interesting: 'yes' # provide a bit of additional context Raven.extra_context happiness: 'very' Rack Context ------------ Additionally, if you're using Rack (without the middleware), you can easily provide context with the ``rack_context`` helper: .. sourcecode:: ruby Raven.rack_context(env) If you're using the Rack middleware, we've already taken care of cleanup for you, otherwise you'll need to ensure you perform it manually: .. sourcecode:: ruby Raven::Context.clear! Note: the rack and user context will perform a set operation, whereas tags and extra context will merge with any existing request context. raven-ruby-0.15.3/docs/index.rst000066400000000000000000000050271264325443000164720ustar00rootroot00000000000000.. sentry:edition:: self Raven Ruby ========== .. sentry:edition:: hosted, on-premise .. class:: platform-ruby Ruby ==== Raven for Ruby is a client and integration layer for the Sentry error reporting API. It supports Ruby MRI 1.8.7/REE, 1.9.3, 2.0, 2.1 and 2.2. JRuby support is provided but experimental. Installation ------------ Raven Ruby comes as a gem and is straightforward to install. If you are using Bundler just add this to your ``Gemfile``: .. sourcecode:: ruby gem "sentry-raven" For other means of installation see :doc:`install`. Configuration ------------- To use Raven Ruby all you need is your DSN. Like most Sentry libraries it will honor the ``SENTRY_DSN`` environment variable. You can find it on the project settings page under API Keys. You can either export it as environment variable or manually configure it with ``Raven.configure``: .. sourcecode:: ruby Raven.configure do |config| config.dsn = '___DSN___' end Reporting Failures ------------------ If you use Rails, Rake, Rack etc, you're already done - no more configuration required! Check :doc:`integrations/index` for more details on other gems Sentry integrates with automatically. Otherwise, Raven supports two methods of capturing exceptions: .. sourcecode:: ruby Raven.capture do # capture any exceptions which happen during execution of this block 1 / 0 end begin 1 / 0 rescue ZeroDivisionError => exception Raven.capture_exception(exception) end Additional Context ------------------ Much of the usefulness of Sentry comes from additional context data with the events. Raven Ruby makes this very convenient by providing methods to set thread local context data that is then submitted automatically with all events. There are three primary methods for providing request context: .. sourcecode:: ruby # bind the logged in user Raven.user_context email: 'foo@example.com' # tag the request with something interesting Raven.tags_context interesting: 'yes' # provide a bit of additional context Raven.extra_context happiness: 'very' For more information see :doc:`context`. Deep Dive --------- Want to know more? We have a detailed documentation about all parts of the library and the client integrations. .. toctree:: :maxdepth: 2 :titlesonly: install config usage context integrations/index Resources: * `Bug Tracker `_ * `Github Project `_ raven-ruby-0.15.3/docs/install.rst000066400000000000000000000016741264325443000170350ustar00rootroot00000000000000Installation ============ Raven Ruby comes as a gem and is straightforward to install. If you are using Bundler just add this to your ``Gemfile``: .. sourcecode:: ruby gem "sentry-raven" Development Version ------------------- If you want to install the development version from github: .. sourcecode:: ruby gem "sentry-raven", :github => "getsentry/raven-ruby" Without Integrations -------------------- If you wish to activate integrations manually (or don't want them activated by default), require "raven/base" instead of "raven" or "sentry-raven". In that case disable the requiring in the ``Gemfile``: .. sourcecode:: ruby gem "sentry-raven", :require => false And in your initialization code: .. sourcecode:: ruby require "raven/base" require "raven/integrations/rails" require "raven/integrations/delayed_job" This stops you from calling ``Raven.inject``, which is where all this integration loading occurs. raven-ruby-0.15.3/docs/integrations/000077500000000000000000000000001264325443000173335ustar00rootroot00000000000000raven-ruby-0.15.3/docs/integrations/index.rst000066400000000000000000000022041264325443000211720ustar00rootroot00000000000000Integrations ============ For common environments and frameworks like Rails, Rake, Rack and others Ruby Raven provides automatic integration for reporting. Most of the time you don't need to change anything, although you can configure those features if you want. .. toctree:: :maxdepth: 1 rails rack The following integrations are available: * Sidekiq (``:sidekiq``) * ``Delayed::Job`` (``:delayed_job``) * Rake (``:rake``) * Rack (``:rack``) * Rails (``:railties``) Manually using integrations --------------------------- Integrations are automatically loaded by default. This can be problematic if the default integration behavior doesn't suit your projects' needs. To explicitly include integrations: :: require 'sentry-raven-without-integrations' Raven.inject_only(:railties, :rack, :rake) To blacklist integrations: :: require 'sentry-raven-without-integrations' Raven.inject_without(:sidekiq, :delayed_job) If you're using bundler, then in your gemfile: :: gem 'sentry-raven', require: 'sentry-raven-without-integrations' And in some sort of initializer: :: Raven.inject_without(:sidekiq) raven-ruby-0.15.3/docs/integrations/rack.rst000066400000000000000000000004451264325443000210100ustar00rootroot00000000000000Rack (Sinatra etc.) =================== Add ``use Raven::Rack`` to your ``config.ru`` or other rackup file (this is automatically inserted in Rails): .. sourcecode:: ruby require 'raven' Raven.configure(true) do |config| config.dsn = '___DSN___' end use Raven::Rack raven-ruby-0.15.3/docs/integrations/rails.rst000066400000000000000000000041751264325443000212060ustar00rootroot00000000000000Ruby on Rails ============= In Rails, all uncaught exceptions will be automatically reported. We support Rails 3 and newer. You'll still want to ensure you've disabled anything that would prevent errors from being propagated to the ``Raven::Rack`` middleware, ``like ActionDispatch::ShowExceptions``: .. sourcecode:: ruby config.action_dispatch.show_exceptions = false # this is the default setting in production If you have added items to `Rails' log filtering `_, you can also make sure that those items are not sent to Sentry: .. sourcecode:: ruby # in your application.rb: config.filter_parameters << :password # in an initializer, like sentry.rb Raven.configure do |config| config.sanitize_fields = Rails.application.config.filter_parameters.map(&:to_s) end If you only want to send events to Sentry in certain environments, you should set ``config.environments`` too: .. sourcecode:: ruby Raven.configure do |config| config.dsn = '___DSN___' config.environments = ['staging', 'production'] end Params and sessions ------------------- .. sourcecode:: ruby class ApplicationController < ActionController::Base before_action :set_raven_context private def set_raven_context Raven.user_context(user_id: session[:current_user_id]) # or anything else in session Raven.extra_context(params: params.to_hash, url: request.url) end end Authlogic --------- When using Authlogic for authentication, you can provide user context by binding to session ``after_persisting`` and ``after_destroy`` events in ``user_session.rb``: .. sourcecode:: ruby class UserSession < Authlogic::Session::Base # events binding after_persisting :raven_set_user_context after_destroy :raven_clear_user_context def raven_set_user_context Raven.user_context({ 'id' => self.user.id, 'email' => self.user.email, 'username' => self.user.username }) end def raven_clear_user_context Raven.user_context({}) end end raven-ruby-0.15.3/docs/make.bat000066400000000000000000000100121264325443000162240ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Sentry.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Sentry.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end raven-ruby-0.15.3/docs/sentry-doc-config.json000066400000000000000000000012771264325443000210610ustar00rootroot00000000000000{ "support_level": "production", "platforms": { "ruby": { "name": "Ruby", "type": "language", "doc_link": "", "wizard": [ "index#installation", "index#configuration", "index#reporting-failures" ] }, "ruby.rack": { "name": "Rack", "type": "framework", "doc_link": "integrations/rack/", "wizard": [ "index#installation", "integrations/rack#rack-sinatra-etc" ] }, "ruby.rails": { "name": "Rails", "type": "framework", "doc_link": "integrations/rails/", "wizard": [ "index#installation", "integrations/rails#ruby-on-rails" ] } } } raven-ruby-0.15.3/docs/usage.rst000066400000000000000000000054131264325443000164660ustar00rootroot00000000000000Usage ===== To use Raven Ruby all you need is your DSN. Like most Sentry libraries it will honor the ``SENTRY_DSN`` environment variable. You can find it on the project settings page under API Keys. You can either export it as environment variable or manually configure it with ``Raven.configure``: .. code-block:: ruby Raven.configure do |config| config.dsn = '___DSN___' end If you only want to send events to Sentry in certain environments, you should set ``config.environments`` too: .. code-block:: ruby Raven.configure do |config| config.dsn = '___DSN___' config.environments = ['staging', 'production'] end Reporting Failures ------------------ If you use Rails, Rake, Rack etc, you're already done - no more configuration required! Check :doc:`integrations/index` for more details on other gems Sentry integrates with automatically. Otherwise, Raven supports two methods of capturing exceptions: .. sourcecode:: ruby Raven.capture do # capture any exceptions which happen during execution of this block 1 / 0 end begin 1 / 0 rescue ZeroDivisionError => exception Raven.capture_exception(exception) end Reporting Messages ------------------ If you want to report a message rather than an exception you can use the ``capture_message`` method: .. code-block:: ruby Raven.capture_message("Something went very wrong") Optional Attributes ------------------- With calls to ``capture_exception`` or ``capture_message`` additional data can be supplied:: .. code-block:: ruby Raven.capture_message("...", :attr => 'value') .. describe:: extra Additional context for this event. Must be a mapping. Children can be any native JSON type. .. code-block:: ruby { :extra => {'key' => 'value'} } .. describe:: fingerprint The fingerprint for grouping this event. .. code-block:: ruby { :fingerprint => ['{{ default }}', 'other value'] } .. describe:: level The level of the event. Defaults to ``error``. .. code-block:: ruby { :level => 'warning' } Sentry is aware of the following levels: * debug (the least serious) * info * warning * error * fatal (the most serious) .. describe:: logger The logger name for the event. .. code-block:: ruby { :logger => 'default' } .. describe:: tags Tags to index with this event. Must be a mapping of strings. .. code-block:: ruby { :tags => {'key' => 'value'} } .. describe:: user The acting user. .. code-block:: ruby { :user => { 'id' => 42, 'email' => 'clever-girl' } } raven-ruby-0.15.3/exe/000077500000000000000000000000001264325443000144565ustar00rootroot00000000000000raven-ruby-0.15.3/exe/raven000077500000000000000000000014541264325443000155230ustar00rootroot00000000000000#!/usr/bin/env ruby require "raven" require "raven/cli" require "optparse" parser = OptionParser.new do |opt| opt.banner = "Usage: raven COMMAND [OPTIONS]" opt.separator "" opt.separator "Commands" opt.separator " test: send a test event" opt.separator "" opt.separator "Options" # opt.on("-e","--environment ENVIRONMENT","which environment you want server run") do |environment| # options[:environment] = environment # end # opt.on("-d","--daemon","runing on daemon mode?") do # options[:daemon] = true # end opt.on("-h","--help","help") do puts parser end end parser.parse! case ARGV[0] when "test" dsn = ARGV[1] if ARGV.length > 1 if !dsn puts "Usage: raven test " else Raven::CLI.test(dsn) end else puts parser end raven-ruby-0.15.3/gemfiles/000077500000000000000000000000001264325443000154705ustar00rootroot00000000000000raven-ruby-0.15.3/gemfiles/rails32.gemfile000066400000000000000000000002511264325443000202770ustar00rootroot00000000000000source "http://rubygems.org" gem "rails", "~> 3.2.6" gem "rack-cache", "~> 1.2.0" # Ruby 1.8.7 requires this gem "i18n", "~> 0.6.0" gem "rspec" gemspec :path => "../" raven-ruby-0.15.3/gemfiles/rails41.gemfile000066400000000000000000000001321264325443000202750ustar00rootroot00000000000000source "http://rubygems.org" gem "rails", "~> 4.1.0" gem "rspec" gemspec :path => "../" raven-ruby-0.15.3/gemfiles/rails42.gemfile000066400000000000000000000001271264325443000203020ustar00rootroot00000000000000source "http://rubygems.org" gem "rails", '4.2.0' gem "rspec" gemspec :path => "../" raven-ruby-0.15.3/lib/000077500000000000000000000000001264325443000144435ustar00rootroot00000000000000raven-ruby-0.15.3/lib/raven.rb000066400000000000000000000000431264325443000161000ustar00rootroot00000000000000require 'raven/base' Raven.inject raven-ruby-0.15.3/lib/raven/000077500000000000000000000000001264325443000155565ustar00rootroot00000000000000raven-ruby-0.15.3/lib/raven/backports/000077500000000000000000000000001264325443000175465ustar00rootroot00000000000000raven-ruby-0.15.3/lib/raven/backports/uri.rb000066400000000000000000000064161264325443000207010ustar00rootroot00000000000000# :stopdoc: # Stolen from ruby core's uri/common.rb, with modifications to support 1.8.x # # https://github.com/ruby/ruby/blob/trunk/lib/uri/common.rb # # module URI TBLENCWWWCOMP_ = {} # :nodoc: 256.times do |i| TBLENCWWWCOMP_[i.chr] = '%%%02X' % i end TBLENCWWWCOMP_[' '] = '+' TBLENCWWWCOMP_.freeze TBLDECWWWCOMP_ = {} # :nodoc: 256.times do |i| h, l = i>>4, i&15 TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr end TBLDECWWWCOMP_['+'] = ' ' TBLDECWWWCOMP_.freeze # Encode given +s+ to URL-encoded form data. # # This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP # (ASCII space) to + and converts others to %XX. # # This is an implementation of # http://www.w3.org/TR/html5/forms.html#url-encoded-form-data # # See URI.decode_www_form_component, URI.encode_www_form def self.encode_www_form_component(s) str = s.to_s if RUBY_VERSION < "1.9" && $KCODE =~ /u/i str.gsub(/([^ a-zA-Z0-9_.-]+)/) do '%' + $1.unpack('H2' * Rack::Utils.bytesize($1)).join('%').upcase end.tr(' ', '+') else str.gsub(/[^*\-.0-9A-Z_a-z]/) {|m| TBLENCWWWCOMP_[m]} end end # Decode given +str+ of URL-encoded form data. # # This decodes + to SP. # # See URI.encode_www_form_component, URI.decode_www_form def self.decode_www_form_component(str, enc = nil) raise ArgumentError, "invalid %-encoding (#{str})" unless /\A(?:%[0-9a-fA-F]{2}|[^%])*\z/ =~ str str.gsub(/\+|%[0-9a-fA-F]{2}/) {|m| TBLDECWWWCOMP_[m]} end # Generate URL-encoded form data from given +enum+. # # This generates application/x-www-form-urlencoded data defined in HTML5 # from given an Enumerable object. # # This internally uses URI.encode_www_form_component(str). # # This method doesn't convert the encoding of given items, so convert them # before call this method if you want to send data as other than original # encoding or mixed encoding data. (Strings which are encoded in an HTML5 # ASCII incompatible encoding are converted to UTF-8.) # # This method doesn't handle files. When you send a file, use # multipart/form-data. # # This is an implementation of # http://www.w3.org/TR/html5/forms.html#url-encoded-form-data # # URI.encode_www_form([["q", "ruby"], ["lang", "en"]]) # #=> "q=ruby&lang=en" # URI.encode_www_form("q" => "ruby", "lang" => "en") # #=> "q=ruby&lang=en" # URI.encode_www_form("q" => ["ruby", "perl"], "lang" => "en") # #=> "q=ruby&q=perl&lang=en" # URI.encode_www_form([["q", "ruby"], ["q", "perl"], ["lang", "en"]]) # #=> "q=ruby&q=perl&lang=en" # # See URI.encode_www_form_component, URI.decode_www_form def self.encode_www_form(enum) enum.map do |k,v| if v.nil? encode_www_form_component(k) elsif v.respond_to?(:to_ary) v.to_ary.map do |w| str = encode_www_form_component(k) unless w.nil? str << '=' str << encode_www_form_component(w) end end.join('&') else str = encode_www_form_component(k) str << '=' str << encode_www_form_component(v) end end.join('&') end end raven-ruby-0.15.3/lib/raven/backtrace.rb000066400000000000000000000067001264325443000200250ustar00rootroot00000000000000## Inspired by Rails' and Airbrake's backtrace parsers. module Raven # Front end to parsing the backtrace for each notice class Backtrace # Handles backtrace parsing line by line class Line # regexp (optionnally allowing leading X: for windows support) RUBY_INPUT_FORMAT = %r{^((?:[a-zA-Z]:)?[^:]+|<.*>):(\d+)(?::in `([^']+)')?$}.freeze # org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:170) JAVA_INPUT_FORMAT = %r{^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)$}.freeze APP_DIRS_PATTERN = /(bin|exe|app|config|lib|test)/ # The file portion of the line (such as app/models/user.rb) attr_reader :file # The line number portion of the line attr_reader :number # The method of the line (such as index) attr_reader :method # The module name (JRuby) attr_reader :module_name # Parses a single line of a given backtrace # @param [String] unparsed_line The raw line from +caller+ or some backtrace # @return [Line] The parsed backtrace line def self.parse(unparsed_line) ruby_match = unparsed_line.match(RUBY_INPUT_FORMAT) if ruby_match _, file, number, method = ruby_match.to_a module_name = nil else java_match = unparsed_line.match(JAVA_INPUT_FORMAT) _, module_name, method, file, number = java_match.to_a end new(file, number, method, module_name) end def initialize(file, number, method, module_name) self.file = file self.module_name = module_name self.number = number.to_i self.method = method end def in_app app_dirs = Raven.configuration.app_dirs_pattern || APP_DIRS_PATTERN @in_app_pattern ||= Regexp.new("^(#{project_root}/)?#{app_dirs}") if self.file =~ @in_app_pattern true else false end end def project_root @project_root ||= Raven.configuration.project_root && Raven.configuration.project_root.to_s end # Reconstructs the line in a readable fashion def to_s "#{file}:#{number}:in `#{method}'" end def ==(other) to_s == other.to_s end def inspect "" end private attr_writer :file, :number, :method, :module_name end # holder for an Array of Backtrace::Line instances attr_reader :lines def self.parse(ruby_backtrace, opts = {}) ruby_lines = split_multiline_backtrace(ruby_backtrace) filters = opts[:filters] || [] filtered_lines = ruby_lines.to_a.map do |line| filters.reduce(line) do |nested_line, proc| proc.call(nested_line) end end.compact lines = filtered_lines.map do |unparsed_line| Line.parse(unparsed_line) end new(lines) end def initialize(lines) self.lines = lines end def inspect "" end def to_s content = [] lines.each do |line| content << line end content.join("\n") end def ==(other) if other.respond_to?(:lines) lines == other.lines else false end end private attr_writer :lines def self.split_multiline_backtrace(backtrace) if backtrace.to_a.size == 1 backtrace.to_a.first.split(/\n\s*/) else backtrace end end end end raven-ruby-0.15.3/lib/raven/base.rb000066400000000000000000000170341264325443000170220ustar00rootroot00000000000000require 'raven/version' require 'raven/backtrace' require 'raven/configuration' require 'raven/context' require 'raven/client' require 'raven/event' require 'raven/logger' require 'raven/interfaces/message' require 'raven/interfaces/exception' require 'raven/interfaces/single_exception' require 'raven/interfaces/stack_trace' require 'raven/interfaces/http' require 'raven/processor' require 'raven/processor/sanitizedata' require 'raven/processor/removecircularreferences' require 'raven/processor/utf8conversion' major, minor, patch = RUBY_VERSION.split('.').map(&:to_i) if (major == 1 && minor < 9) || (major == 1 && minor == 9 && patch < 2) require 'raven/backports/uri' end module Raven AVAILABLE_INTEGRATIONS = %w[delayed_job railties sidekiq rack rake] class << self # The client object is responsible for delivering formatted data to the Sentry server. # Must respond to #send. See Raven::Client. attr_writer :client # A Raven configuration object. Must act like a hash and return sensible # values for all Raven configuration options. See Raven::Configuration. attr_writer :configuration def context Context.current end def logger @logger ||= Logger.new end # The configuration object. # @see Raven.configure def configuration @configuration ||= Configuration.new end # The client object is responsible for delivering formatted data to the Sentry server. def client @client ||= Client.new(configuration) end # Tell the log that the client is good to go def report_status return if client.configuration.silence_ready if client.configuration.send_in_current_environment? logger.info "Raven #{VERSION} ready to catch errors" else logger.info "Raven #{VERSION} configured not to send errors." end end alias_method :report_ready, :report_status # Call this method to modify defaults in your initializers. # # @example # Raven.configure do |config| # config.server = 'http://...' # end def configure yield(configuration) if block_given? self.client = Client.new(configuration) report_status self.client end # Send an event to the configured Sentry server # # @example # evt = Raven::Event.new(:message => "An error") # Raven.send_event(evt) def send_event(event) client.send_event(event) end # Capture and process any exceptions from the given block, or globally if # no block is given # # @example # Raven.capture do # MyApp.run # end def capture(options = {}) if block_given? begin yield rescue Error raise # Don't capture Raven errors rescue Exception => e capture_exception(e, options) raise end else install_at_exit_hook(options) end end def capture_type(obj, options = {}) return false unless should_capture?(obj) message_or_exc = obj.is_a?(String) ? "message" : "exception" if (evt = Event.send("from_" + message_or_exc, obj, options)) yield evt if block_given? if configuration.async? configuration.async.call(evt) else send_event(evt) end evt end end alias_method :capture_message, :capture_type alias_method :capture_exception, :capture_type def should_capture?(message_or_exc) if configuration.should_capture configuration.should_capture.call(*[message_or_exc]) else true end end # Provides extra context to the exception prior to it being handled by # Raven. An exception can have multiple annotations, which are merged # together. # # The options (annotation) is treated the same as the ``options`` # parameter to ``capture_exception`` or ``Event.from_exception``, and # can contain the same ``:user``, ``:tags``, etc. options as these # methods. # # These will be merged with the ``options`` parameter to # ``Event.from_exception`` at the top of execution. # # @example # begin # raise "Hello" # rescue => exc # Raven.annotate_exception(exc, :user => { 'id' => 1, # 'email' => 'foo@example.com' }) # end def annotate_exception(exc, options = {}) notes = (exc.instance_variable_defined?(:@__raven_context) && exc.instance_variable_get(:@__raven_context)) || {} notes.merge!(options) exc.instance_variable_set(:@__raven_context, notes) exc end # Bind user context. Merges with existing context (if any). # # It is recommending that you send at least the ``id`` and ``email`` # values. All other values are arbitrary. # # @example # Raven.user_context('id' => 1, 'email' => 'foo@example.com') def user_context(options = nil) self.context.user = options || {} end # Bind tags context. Merges with existing context (if any). # # Tags are key / value pairs which generally represent things like application version, # environment, role, and server names. # # @example # Raven.tags_context('my_custom_tag' => 'tag_value') def tags_context(options = nil) self.context.tags.merge!(options || {}) end # Bind extra context. Merges with existing context (if any). # # Extra context shows up as Additional Data within Sentry, and is completely arbitrary. # # @example # Raven.extra_context('my_custom_data' => 'value') def extra_context(options = nil) self.context.extra.merge!(options || {}) end def rack_context(env) if env.empty? env = nil end self.context.rack_env = env end # Injects various integrations. Default behavior: inject all available integrations def inject inject_only(*Raven::AVAILABLE_INTEGRATIONS) end def inject_without(*exclude_integrations) include_integrations = Raven::AVAILABLE_INTEGRATIONS - exclude_integrations.map(&:to_s) inject_only(*include_integrations) end def inject_only(*only_integrations) only_integrations = only_integrations.map(&:to_s) integrations_to_load = Raven::AVAILABLE_INTEGRATIONS & only_integrations not_found_integrations = only_integrations - integrations_to_load if not_found_integrations.any? self.logger.warn "Integrations do not exist: #{not_found_integrations.join ', '}" end integrations_to_load &= Gem.loaded_specs.keys # TODO(dcramer): integrations should have some additional checks baked-in # or we should break them out into their own repos. Specifically both the # rails and delayed_job checks are not always valid (i.e. Rails 2.3) and # https://github.com/getsentry/raven-ruby/issues/180 integrations_to_load.each do |integration| load_integration(integration) end end def load_integration(integration) require "raven/integrations/#{integration}" rescue Exception => error self.logger.warn "Unable to load raven/integrations/#{integration}: #{error}" end # For cross-language compat alias :captureException :capture_exception alias :captureMessage :capture_message alias :annotateException :annotate_exception alias :annotate :annotate_exception private def install_at_exit_hook(options) at_exit do if $! logger.debug "Caught a post-mortem exception: #{$!.inspect}" capture_exception($!, options) end end end end end raven-ruby-0.15.3/lib/raven/cli.rb000066400000000000000000000026511264325443000166560ustar00rootroot00000000000000module Raven class CLI def self.test(dsn = nil) require 'logger' logger = ::Logger.new(STDOUT) logger.level = ::Logger::ERROR logger.formatter = proc do |_severity, _datetime, _progname, msg| "-> #{msg}\n" end Raven.configuration.logger = logger Raven.configuration.timeout = 5 Raven.configuration.dsn = dsn if dsn # wipe out env settings to ensure we send the event unless Raven.configuration.send_in_current_environment? env_name = Raven.configuration.environments.pop || 'production' puts "Setting environment to #{env_name}" Raven.configuration.current_environment = env_name end Raven.configuration.verify! puts "Sending a test event:" puts "" begin 1 / 0 rescue ZeroDivisionError => exception evt = Raven.capture_exception(exception) end if evt && !(evt.is_a? Thread) if evt.is_a? Hash puts "-> event ID: #{evt[:event_id]}" else puts "-> event ID: #{evt.id}" end elsif evt #async configuration if evt.value.is_a? Hash puts "-> event ID: #{evt.value[:event_id]}" else puts "-> event ID: #{evt.value.id}" end else puts "" puts "An error occurred while attempting to send the event." exit 1 end puts "" puts "Done!" end end end raven-ruby-0.15.3/lib/raven/client.rb000066400000000000000000000073751264325443000173750ustar00rootroot00000000000000require 'zlib' require 'base64' require 'raven/version' require 'raven/okjson' require 'raven/transports/http' require 'raven/transports/udp' module Raven # Encodes events and sends them to the Sentry server. class Client PROTOCOL_VERSION = '5' USER_AGENT = "raven-ruby/#{Raven::VERSION}" CONTENT_TYPE = 'application/json' attr_accessor :configuration def initialize(configuration) @configuration = configuration @processors = configuration.processors.map { |v| v.new(self) } @state = ClientState.new end def send_event(event) return false unless configuration_allows_sending # Convert to hash event = event.to_hash if !@state.should_try? Raven.logger.error("Not sending event due to previous failure(s): #{get_log_message(event)}") return end Raven.logger.debug "Sending event #{event[:event_id]} to Sentry" content_type, encoded_data = encode(event) begin transport.send_event(generate_auth_header, encoded_data, :content_type => content_type) rescue => e failed_send(e, event) return end successful_send event end def transport @transport ||= case configuration.scheme when 'udp' Transports::UDP.new(configuration) when 'http', 'https' Transports::HTTP.new(configuration) when 'dummy' Transports::Dummy.new(configuration) else fail "Unknown transport scheme '#{configuration.scheme}'" end end private def configuration_allows_sending if configuration.send_in_current_environment? true else configuration.log_excluded_environment_message false end end def encode(event) hash = @processors.reduce(event.to_hash) { |memo, p| p.process(memo) } encoded = OkJson.encode(hash) case configuration.encoding when 'gzip' ['application/octet-stream', strict_encode64(Zlib::Deflate.deflate(encoded))] else ['application/json', encoded] end end def get_log_message(event) (event && event[:message]) || '' end def generate_auth_header now = Time.now.to_i.to_s fields = { 'sentry_version' => PROTOCOL_VERSION, 'sentry_client' => USER_AGENT, 'sentry_timestamp' => now, 'sentry_key' => configuration.public_key, 'sentry_secret' => configuration.secret_key } 'Sentry ' + fields.map { |key, value| "#{key}=#{value}" }.join(', ') end def strict_encode64(string) if Base64.respond_to? :strict_encode64 Base64.strict_encode64 string else # Ruby 1.8 Base64.encode64(string)[0..-2] end end def successful_send @state.success end def failed_send(e, event) @state.failure Raven.logger.error "Unable to record event with remote Sentry server (#{e.class} - #{e.message})" e.backtrace[0..10].each { |line| Raven.logger.error(line) } Raven.logger.error("Failed to submit event: #{get_log_message(event)}") end end class ClientState def initialize reset end def should_try? return true if @status == :online interval = @retry_after || [@retry_number, 6].min ** 2 return true if Time.now - @last_check >= interval false end def failure(retry_after = nil) @status = :error @retry_number += 1 @last_check = Time.now @retry_after = retry_after end def success reset end def reset @status = :online @retry_number = 0 @last_check = nil @retry_after = nil end def failed? @status == :error end end end raven-ruby-0.15.3/lib/raven/configuration.rb000066400000000000000000000135661264325443000207650ustar00rootroot00000000000000require 'logger' require 'uri' module Raven class Configuration # Simple server string (setter provided below) attr_reader :server # Public key for authentication with the Sentry server attr_accessor :public_key # Secret key for authentication with the Sentry server attr_accessor :secret_key # Accessors for the component parts of the DSN attr_accessor :scheme attr_accessor :host attr_accessor :port attr_accessor :path # Project ID number to send to the Sentry server attr_accessor :project_id # Project directory root attr_accessor :project_root # Encoding type for event bodies attr_reader :encoding # Logger to use internally attr_accessor :logger # Silence ready message attr_accessor :silence_ready # Number of lines of code context to capture, or nil for none attr_accessor :context_lines # Whitelist of environments that will send notifications to Sentry attr_accessor :environments # Include module versions in reports? attr_accessor :send_modules # Which exceptions should never be sent attr_accessor :excluded_exceptions # Processors to run on data before sending upstream attr_accessor :processors # Timeout when waiting for the server to return data in seconds attr_accessor :timeout # Timeout waiting for the connection to open in seconds attr_accessor :open_timeout # Should the SSL certificate of the server be verified? attr_accessor :ssl_verification # The path to the SSL certificate file attr_accessor :ssl_ca_file # SSl settings passed direactly to faraday's ssl option attr_accessor :ssl # Proxy information to pass to the HTTP adapter attr_accessor :proxy attr_reader :current_environment # The Faraday adapter to be used. Will default to Net::HTTP when not set. attr_accessor :http_adapter attr_accessor :server_name attr_accessor :release # DEPRECATED: This option is now ignored as we use our own adapter. attr_accessor :json_adapter # Default tags for events attr_accessor :tags # Optional Proc to be used to send events asynchronously. attr_reader :async # Exceptions from these directories to be ignored attr_accessor :app_dirs_pattern # Catch exceptions before they're been processed by # ActionDispatch::ShowExceptions or ActionDispatch::DebugExceptions attr_accessor :catch_debugged_exceptions # Provide a configurable callback to determine event capture attr_accessor :should_capture # additional fields to sanitize attr_accessor :sanitize_fields # Sanitize values that look like credit card numbers attr_accessor :sanitize_credit_cards IGNORE_DEFAULT = [ 'AbstractController::ActionNotFound', 'ActionController::InvalidAuthenticityToken', 'ActionController::RoutingError', 'ActionController::UnknownAction', 'ActiveRecord::RecordNotFound', 'CGI::Session::CookieStore::TamperedWithCookie', 'Mongoid::Errors::DocumentNotFound', 'Sinatra::NotFound', ] def initialize self.server = ENV['SENTRY_DSN'] if ENV['SENTRY_DSN'] @context_lines = 3 self.current_environment = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default' self.send_modules = true self.excluded_exceptions = IGNORE_DEFAULT self.processors = [Raven::Processor::RemoveCircularReferences, Raven::Processor::UTF8Conversion, Raven::Processor::SanitizeData] self.ssl_verification = true self.encoding = 'gzip' self.timeout = 1 self.open_timeout = 1 self.proxy = nil self.tags = {} self.async = false self.catch_debugged_exceptions = true self.sanitize_fields = [] self.sanitize_credit_cards = true self.environments = [] self.release = ENV['HEROKU_SLUG_COMMIT'] if self.release.nil? || self.release.empty? self.release = File.read(File.join(Rails.root, 'REVISION')).strip rescue nil end if self.release.nil? || self.release.empty? self.release = `git rev-parse --short HEAD`.strip rescue nil end end def server=(value) uri = URI.parse(value) uri_path = uri.path.split('/') if uri.user # DSN-style string @project_id = uri_path.pop @public_key = uri.user @secret_key = uri.password end @scheme = uri.scheme @host = uri.host @port = uri.port if uri.port @path = uri_path.join('/') # For anyone who wants to read the base server string @server = "#{@scheme}://#{@host}" @server << ":#{@port}" unless @port == { 'http' => 80, 'https' => 443 }[@scheme] @server << @path end def encoding=(encoding) raise Error.new('Unsupported encoding') unless %w(gzip json).include? encoding @encoding = encoding end alias_method :dsn=, :server= def async=(value) raise ArgumentError.new("async must be callable (or false to disable)") unless value == false || value.respond_to?(:call) @async = value end alias_method :async?, :async # Allows config options to be read like a hash # # @param [Symbol] option Key for a given attribute def [](option) send(option) end def current_environment=(environment) @current_environment = environment.to_s end def send_in_current_environment? !!server && (environments.empty? || environments.include?(current_environment)) end def log_excluded_environment_message Raven.logger.debug "Event not sent due to excluded environment: #{current_environment}" end def verify! raise Error.new('No server specified') unless server raise Error.new('No public key specified') unless public_key raise Error.new('No secret key specified') unless secret_key raise Error.new('No project ID specified') unless project_id end end end raven-ruby-0.15.3/lib/raven/context.rb000066400000000000000000000005431264325443000175710ustar00rootroot00000000000000module Raven class Context def self.current Thread.current[:sentry_context] ||= new end def self.clear! Thread.current[:sentry_context] = nil end attr_reader :extra, :tags attr_accessor :rack_env, :user def initialize @extra = {} @tags = {} @user = {} @rack_env = nil end end end raven-ruby-0.15.3/lib/raven/error.rb000066400000000000000000000000651264325443000172350ustar00rootroot00000000000000module Raven class Error < StandardError end end raven-ruby-0.15.3/lib/raven/event.rb000066400000000000000000000177341264325443000172400ustar00rootroot00000000000000require 'rubygems' require 'socket' require 'securerandom' require 'digest/md5' require 'raven/error' require 'raven/linecache' module Raven class Event LOG_LEVELS = { "debug" => 10, "info" => 20, "warn" => 30, "warning" => 30, "error" => 40, "fatal" => 50, } BACKTRACE_RE = /^(.+?):(\d+)(?::in `(.+?)')?$/ PLATFORM = "ruby" attr_reader :id attr_accessor :project, :message, :timestamp, :time_spent, :level, :logger, :culprit, :server_name, :release, :modules, :extra, :tags, :context, :configuration, :checksum, :fingerprint def initialize(init = {}) @configuration = Raven.configuration @interfaces = {} @context = Raven.context @id = generate_event_id @project = nil @message = nil @timestamp = Time.now.utc @time_spent = nil @level = :error @logger = '' @culprit = nil @server_name = @configuration.server_name || resolve_hostname @release = @configuration.release @modules = list_gem_specs if @configuration.send_modules @user = {} @extra = {} @tags = {} @checksum = nil @fingerprint = nil yield self if block_given? if !self[:http] && @context.rack_env interface :http do |int| int.from_rack(@context.rack_env) end end init.each_pair { |key, val| instance_variable_set('@' + key.to_s, val) } @user = @context.user.merge(@user) @extra = @context.extra.merge(@extra) @tags = @configuration.tags.merge(@context.tags).merge(@tags) # Some type coercion @timestamp = @timestamp.strftime('%Y-%m-%dT%H:%M:%S') if @timestamp.is_a?(Time) @time_spent = (@time_spent*1000).to_i if @time_spent.is_a?(Float) @level = LOG_LEVELS[@level.to_s.downcase] if @level.is_a?(String) || @level.is_a?(Symbol) end def resolve_hostname # Try to resolve the hostname to an FQDN, but fall back to whatever the load name is hostname = Socket.gethostname Socket.gethostbyname(hostname).first rescue hostname end def list_gem_specs # Older versions of Rubygems don't support iterating over all specs Hash[Gem::Specification.map { |spec| [spec.name, spec.version.to_s] }] if Gem::Specification.respond_to?(:map) end def interface(name, value = nil, &block) int = Raven.find_interface(name) raise Error.new("Unknown interface: #{name}") unless int @interfaces[int.name] = int.new(value, &block) if value || block @interfaces[int.name] end def [](key) interface(key) end def []=(key, value) interface(key, value) end def to_hash data = { :event_id => @id, :message => @message, :timestamp => @timestamp, :time_spent => @time_spent, :level => @level, :project => @project, :platform => PLATFORM, } data[:logger] = @logger if @logger data[:culprit] = @culprit if @culprit data[:server_name] = @server_name if @server_name data[:release] = @release if @release data[:fingerprint] = @fingerprint if @fingerprint data[:modules] = @modules if @modules data[:extra] = @extra if @extra data[:tags] = @tags if @tags data[:user] = @user if @user data[:checksum] = @checksum if @checksum @interfaces.each_pair do |name, int_data| data[name.to_sym] = int_data.to_hash end data end def self.from_exception(exc, options = {}, &block) notes = (exc.instance_variable_defined?(:@__raven_context) && exc.instance_variable_get(:@__raven_context)) || {} options = notes.merge(options) configuration = options[:configuration] || Raven.configuration if exc.is_a?(Raven::Error) # Try to prevent error reporting loops Raven.logger.info "Refusing to capture Raven error: #{exc.inspect}" return nil end if configuration[:excluded_exceptions].any? { |x| (x === exc rescue false) || x == exc.class.name } Raven.logger.info "User excluded error: #{exc.inspect}" return nil end new(options) do |evt| evt.configuration = configuration evt.message = "#{exc.class}: #{exc.message}" evt.level = options[:level] || :error add_exception_interface(evt, exc) block.call(evt) if block end end def self.from_message(message, options = {}) configuration = options[:configuration] || Raven.configuration new(options) do |evt| evt.configuration = configuration evt.message = message evt.level = options[:level] || :error evt.interface :message do |int| int.message = message end if options[:backtrace] evt.interface(:stacktrace) do |int| stacktrace_interface_from(int, evt, options[:backtrace]) end end end end def self.add_exception_interface(evt, exc) evt.interface(:exception) do |exc_int| exceptions = [exc] context = Set.new [exc.object_id] while exc.respond_to?(:cause) && exc.cause exceptions << exc.cause exc = exc.cause # TODO(dcramer): ideally this would log to inform the user if context.include?(exc.object_id) break end context.add(exc.object_id) end exceptions.reverse! exc_int.values = exceptions.map do |e| SingleExceptionInterface.new do |int| int.type = e.class.to_s int.value = e.to_s int.module = e.class.to_s.split('::')[0...-1].join('::') int.stacktrace = if e.backtrace StacktraceInterface.new do |stacktrace| stacktrace_interface_from(stacktrace, evt, e.backtrace) end end end end end end def self.stacktrace_interface_from(int, evt, backtrace) backtrace = Backtrace.parse(backtrace) int.frames = backtrace.lines.reverse.map do |line| StacktraceInterface::Frame.new.tap do |frame| frame.abs_path = line.file if line.file frame.function = line.method if line.method frame.lineno = line.number frame.in_app = line.in_app frame.module = line.module_name if line.module_name if evt.configuration[:context_lines] && frame.abs_path frame.pre_context, frame.context_line, frame.post_context = \ evt.get_file_context(frame.abs_path, frame.lineno, evt.configuration[:context_lines]) end end end.select(&:filename) evt.culprit = evt.get_culprit(int.frames) end # Because linecache can go to hell def self._source_lines(_path, _from, _to) end def get_file_context(filename, lineno, context) return nil, nil, nil unless Raven::LineCache.is_valid_file(filename) lines = (2 * context + 1).times.map do |i| Raven::LineCache.getline(filename, lineno - context + i) end [lines[0..(context - 1)], lines[context], lines[(context + 1)..-1]] end def get_culprit(frames) lastframe = frames.reverse.find(&:in_app) || frames.last "#{lastframe.filename} in #{lastframe.function} at line #{lastframe.lineno}" if lastframe end # For cross-language compat class << self alias :captureException :from_exception alias :captureMessage :from_message alias :capture_exception :from_exception alias :capture_message :from_message end private def generate_event_id # generate a uuid. copy-pasted from SecureRandom, this method is not # available in <1.9. ary = SecureRandom.random_bytes(16).unpack("NnnnnN") ary[2] = (ary[2] & 0x0fff) | 0x4000 ary[3] = (ary[3] & 0x3fff) | 0x8000 uuid = "%08x-%04x-%04x-%04x-%04x%08x" % ary ::Digest::MD5.hexdigest(uuid) end end end raven-ruby-0.15.3/lib/raven/integrations/000077500000000000000000000000001264325443000202645ustar00rootroot00000000000000raven-ruby-0.15.3/lib/raven/integrations/delayed_job.rb000066400000000000000000000033321264325443000230530ustar00rootroot00000000000000require 'delayed_job' module Delayed module Plugins class Raven < ::Delayed::Plugin callbacks do |lifecycle| lifecycle.around(:invoke_job) do |job, *args, &block| begin # Forward the call to the next callback in the callback chain block.call(job, *args) rescue Exception => exception # Log error to Sentry extra = { :delayed_job => { :id => job.id, :priority => job.priority, :attempts => job.attempts, # handlers are YAML objects in strings, we definitely can't # report all of that or the event will get truncated randomly :handler => job.handler[0...100], :last_error => job.last_error, :run_at => job.run_at, :locked_at => job.locked_at, :locked_by => job.locked_by, :queue => job.queue, :created_at => job.created_at } } if job.respond_to?('payload_object') && job.payload_object.respond_to?('job_data') extra.merge!(:active_job => job.payload_object.job_data) end ::Raven.capture_exception(exception, :logger => 'delayed_job', :tags => { :delayed_job_queue => job.queue, :delayed_job_id => job.id }, :extra => extra) # Make sure we propagate the failure! raise exception end end end end end end ## # Register DelayedJob Raven plugin # Delayed::Worker.plugins << Delayed::Plugins::Raven raven-ruby-0.15.3/lib/raven/integrations/rack.rb000066400000000000000000000071211264325443000215320ustar00rootroot00000000000000require 'time' require 'rack' module Raven # Middleware for Rack applications. Any errors raised by the upstream # application will be delivered to Sentry and re-raised. # # Synopsis: # # require 'rack' # require 'raven' # # Raven.configure do |config| # config.server = 'http://my_dsn' # end # # app = Rack::Builder.app do # use Raven::Rack # run lambda { |env| raise "Rack down" } # end # # Use a standard Raven.configure call to configure your server credentials. class Rack def self.capture_type(exception, env, options = {}) if env['raven.requested_at'] options[:time_spent] = Time.now - env['raven.requested_at'] end Raven.capture_type(exception, options) do |evt| evt.interface :http do |int| int.from_rack(env) end end end class << self alias_method :capture_message, :capture_type alias_method :capture_exception, :capture_type end def initialize(app) @app = app end def call(env) # clear context at the beginning of the request to ensure a clean slate Context.clear! # store the current environment in our local context for arbitrary # callers env['raven.requested_at'] = Time.now Raven.rack_context(env) begin response = @app.call(env) rescue Error raise # Don't capture Raven errors rescue Exception => e Raven.logger.debug "Collecting %p: %s" % [ e.class, e.message ] Raven::Rack.capture_exception(e, env) raise end error = env['rack.exception'] || env['sinatra.error'] Raven::Rack.capture_exception(error, env) if error response end end module RackInterface def from_rack(env_hash) req = ::Rack::Request.new(env_hash) self.url = req.scheme && req.url.split('?').first self.method = req.request_method self.query_string = req.query_string self.data = read_data_from(req) self.headers = format_headers_for_sentry(env_hash) self.env = format_env_for_sentry(env_hash) end private def read_data_from(request) if request.form_data? request.POST elsif request.body data = request.body.read request.body.rewind data end end def format_headers_for_sentry(env_hash) env_hash.each_with_object({}) do |(key, value), memo| key = key.to_s # rack env can contain symbols value = value.to_s next unless key.upcase == key # Non-upper case stuff isn't either # Rack adds in an incorrect HTTP_VERSION key, which causes downstream # to think this is a Version header. Instead, this is mapped to # env['SERVER_PROTOCOL']. But we don't want to ignore a valid header # if the request has legitimately sent a Version header themselves. # See: https://github.com/rack/rack/blob/028438f/lib/rack/handler/cgi.rb#L29 next if key == 'HTTP_VERSION' && value == ENV['SERVER_PROTOCOL'] if key.start_with?('HTTP_') # Header http_key = key[5..key.length - 1].split('_').map(&:capitalize).join('-') memo[http_key] = value elsif %w(CONTENT_TYPE CONTENT_LENGTH).include? key memo[key.capitalize] = value end end end def format_env_for_sentry(env_hash) trimmed_hash = env_hash.select do |k, _v| %w(REMOTE_ADDR SERVER_NAME SERVER_PORT).include? k.to_s end Hash[trimmed_hash] # select returns an Array in Ruby 1.8 end end class HttpInterface include RackInterface end end raven-ruby-0.15.3/lib/raven/integrations/rails.rb000066400000000000000000000024221264325443000217230ustar00rootroot00000000000000require 'rails' module Raven class Rails < ::Rails::Railtie initializer "raven.use_rack_middleware" do |app| app.config.middleware.insert 0, "Raven::Rack" end initializer 'raven.action_controller' do ActiveSupport.on_load :action_controller do require 'raven/integrations/rails/controller_methods' include Raven::Rails::ControllerMethods end end config.after_initialize do Raven.configure do |config| config.logger ||= ::Rails.logger config.project_root ||= ::Rails.root end if Raven.configuration.catch_debugged_exceptions if defined?(::ActionDispatch::DebugExceptions) require 'raven/integrations/rails/middleware/debug_exceptions_catcher' ::ActionDispatch::DebugExceptions.send(:include, Raven::Rails::Middleware::DebugExceptionsCatcher) elsif defined?(::ActionDispatch::ShowExceptions) require 'raven/integrations/rails/middleware/debug_exceptions_catcher' ::ActionDispatch::ShowExceptions.send(:include, Raven::Rails::Middleware::DebugExceptionsCatcher) end end end rake_tasks do require 'raven/integrations/tasks' end if defined?(runner) runner do Raven.capture end end end end raven-ruby-0.15.3/lib/raven/integrations/rails/000077500000000000000000000000001264325443000213765ustar00rootroot00000000000000raven-ruby-0.15.3/lib/raven/integrations/rails/active_job.rb000066400000000000000000000017521264325443000240350ustar00rootroot00000000000000module Raven class Rails module ActiveJob def self.included(base) base.class_eval do rescue_from(Exception) do |exception| # Do not capture exceptions when using Sidekiq so we don't capture # The same exception twice. unless self.class.queue_adapter.to_s == 'ActiveJob::QueueAdapters::SidekiqAdapter' active_job_details = { :active_job => self.class.name, :arguments => arguments, :scheduled_at => scheduled_at, :job_id => job_id, :locale => locale, } # Add provider_job_id details if Rails 5 if defined?(provider_job_id) active_job_details.merge!(:provider_job_id => provider_job_id) end Raven.capture_exception(exception, :extra => active_job_details) raise exception end end end end end end end raven-ruby-0.15.3/lib/raven/integrations/rails/controller_methods.rb000066400000000000000000000005171264325443000256340ustar00rootroot00000000000000module Raven class Rails module ControllerMethods def capture_message(message, options = {}) Raven::Rack.capture_message(message, request.env, options) end def capture_exception(exception, options = {}) Raven::Rack.capture_exception(exception, request.env, options) end end end end raven-ruby-0.15.3/lib/raven/integrations/rails/middleware/000077500000000000000000000000001264325443000235135ustar00rootroot00000000000000raven-ruby-0.15.3/lib/raven/integrations/rails/middleware/debug_exceptions_catcher.rb000066400000000000000000000006271264325443000310650ustar00rootroot00000000000000module Raven class Rails module Middleware module DebugExceptionsCatcher def self.included(base) base.send(:alias_method_chain, :render_exception, :raven) end def render_exception_with_raven(env, exception) Raven::Rack.capture_exception(exception, env) render_exception_without_raven(env, exception) end end end end end raven-ruby-0.15.3/lib/raven/integrations/railties.rb000066400000000000000000000000431264325443000224220ustar00rootroot00000000000000require 'raven/integrations/rails' raven-ruby-0.15.3/lib/raven/integrations/rake.rb000066400000000000000000000005261264325443000215360ustar00rootroot00000000000000require 'rake' require 'rake/task' require 'raven/integrations/tasks' module Rake class Application alias :orig_display_error_messsage :display_error_message def display_error_message(ex) Raven.capture_exception ex, :logger => 'rake', :tags => { 'rake_task' => @name } orig_display_error_messsage(ex) end end end raven-ruby-0.15.3/lib/raven/integrations/sidekiq.rb000066400000000000000000000026751264325443000222540ustar00rootroot00000000000000require 'time' require 'sidekiq' module Raven class Sidekiq def call(_worker, msg, _queue) started_at = Time.now yield rescue Exception => ex Raven.capture_exception(ex, :extra => { :sidekiq => msg }, :time_spent => Time.now-started_at) raise end end end if Sidekiq::VERSION < '3' # old behavior ::Sidekiq.configure_server do |config| config.server_middleware do |chain| chain.add ::Raven::Sidekiq end end else Sidekiq.configure_server do |config| config.error_handlers << Proc.new do |ex, context| Raven.capture_exception(ex, :extra => { :sidekiq => filter_context(context) }) end end end def filter_context(context) case context when Array context.map { |arg| filter_context(arg) } when Hash Hash[context.map { |key, value| filter_context_hash(key, value) }] else context end end def filter_context_hash(key, value) # Strip any `_aj` prefixes from keys. # These keys come from an internal serialized object from ActiveJob. # Internally, there are a subset of keys that ActiveJob references, but # these are declared as private, and I don't think it's wise # to keep chasing what this list is. But they all use a common prefix, so # we want to strip this becuase ActiveJob will complain. # e.g.: _aj_globalid -> _globalid (key = key[3..-1]) if key [0..3] == "_aj_" [key, filter_context(value)] end raven-ruby-0.15.3/lib/raven/integrations/tasks.rb000066400000000000000000000003621264325443000217370ustar00rootroot00000000000000require 'rake' require 'raven/cli' namespace :raven do desc "Send a test event to the remote Sentry server" task :test, [:dsn] do |_t, args| Rake::Task["environment"].invoke if defined? Rails Raven::CLI.test(args.dsn) end end raven-ruby-0.15.3/lib/raven/interfaces.rb000066400000000000000000000012301264325443000202220ustar00rootroot00000000000000module Raven INTERFACES = {} class Interface def initialize(attributes = nil) attributes.each do |attr, value| public_send "#{attr}=", value end if attributes yield self if block_given? end def self.name(value = nil) @interface_name ||= value end def to_hash Hash[instance_variables.map { |name| [name[1..-1].to_sym, instance_variable_get(name)] } ] end end def self.register_interface(mapping) mapping.each_pair do |key, klass| INTERFACES[key.to_s] = klass INTERFACES[klass.name] = klass end end def self.find_interface(name) INTERFACES[name.to_s] end end raven-ruby-0.15.3/lib/raven/interfaces/000077500000000000000000000000001264325443000177015ustar00rootroot00000000000000raven-ruby-0.15.3/lib/raven/interfaces/exception.rb000066400000000000000000000005331264325443000222250ustar00rootroot00000000000000require 'raven/interfaces' module Raven class ExceptionInterface < Interface name 'exception' attr_accessor :values def to_hash(*args) data = super(*args) if data[:values] data[:values] = data[:values].map(&:to_hash) end data end end register_interface :exception => ExceptionInterface end raven-ruby-0.15.3/lib/raven/interfaces/http.rb000066400000000000000000000007201264325443000212040ustar00rootroot00000000000000require 'raven/interfaces' module Raven class HttpInterface < Interface name 'request' attr_accessor :url attr_accessor :method attr_accessor :data attr_accessor :query_string attr_accessor :cookies attr_accessor :headers attr_accessor :env def initialize(*arguments) self.headers = {} self.env = {} self.cookies = nil super(*arguments) end end register_interface :http => HttpInterface end raven-ruby-0.15.3/lib/raven/interfaces/message.rb000066400000000000000000000004741264325443000216570ustar00rootroot00000000000000require 'raven/interfaces' module Raven class MessageInterface < Interface name 'sentry.interfaces.Message' attr_accessor :message attr_accessor :params def initialize(*arguments) self.params = [] super(*arguments) end end register_interface :message => MessageInterface end raven-ruby-0.15.3/lib/raven/interfaces/single_exception.rb000066400000000000000000000005511264325443000235660ustar00rootroot00000000000000require 'raven/interfaces' module Raven class SingleExceptionInterface < Interface attr_accessor :type attr_accessor :value attr_accessor :module attr_accessor :stacktrace def to_hash(*args) data = super(*args) if data[:stacktrace] data[:stacktrace] = data[:stacktrace].to_hash end data end end end raven-ruby-0.15.3/lib/raven/interfaces/stack_trace.rb000066400000000000000000000041251264325443000225130ustar00rootroot00000000000000require 'raven/interfaces' module Raven class StacktraceInterface < Interface name 'stacktrace' attr_accessor :frames def initialize(*arguments) self.frames = [] super(*arguments) end def to_hash(*args) data = super(*args) data[:frames] = data[:frames].map(&:to_hash) data end # Not actually an interface, but I want to use the same style class Frame < Interface attr_accessor :abs_path attr_accessor :function attr_accessor :vars attr_accessor :pre_context attr_accessor :post_context attr_accessor :context_line attr_accessor :module attr_accessor :lineno attr_accessor :in_app def initialize(*arguments) self.vars, self.pre_context, self.post_context = [], [], [] super(*arguments) end def filename return nil if self.abs_path.nil? prefix = if under_project_root? && in_app project_root elsif under_project_root? longest_load_path || project_root else longest_load_path end prefix ? self.abs_path[prefix.to_s.chomp(File::SEPARATOR).length+1..-1] : self.abs_path end def under_project_root? project_root && abs_path.start_with?(project_root) end def project_root @project_root ||= Raven.configuration.project_root && Raven.configuration.project_root.to_s end def longest_load_path $LOAD_PATH.select { |s| self.abs_path.start_with?(s.to_s) }.sort_by { |s| s.to_s.length }.last end def to_hash(*args) data = super(*args) data[:filename] = self.filename data.delete(:vars) unless self.vars && !self.vars.empty? data.delete(:pre_context) unless self.pre_context && !self.pre_context.empty? data.delete(:post_context) unless self.post_context && !self.post_context.empty? data.delete(:context_line) unless self.context_line && !self.context_line.empty? data end end end register_interface :stack_trace => StacktraceInterface end raven-ruby-0.15.3/lib/raven/linecache.rb000066400000000000000000000010371264325443000200170ustar00rootroot00000000000000# A much simpler source line cacher because linecache sucks at platform compat module Raven class LineCache class << self CACHE = {} def is_valid_file(path) lines = getlines(path) !lines.nil? end def getlines(path) CACHE[path] ||= begin IO.readlines(path) rescue nil end end def getline(path, n) return nil if n < 1 lines = getlines(path) return nil if lines.nil? lines[n - 1] end end end end raven-ruby-0.15.3/lib/raven/logger.rb000066400000000000000000000007531264325443000173670ustar00rootroot00000000000000module Raven class Logger LOG_PREFIX = "** [Raven] " [ :fatal, :error, :warn, :info, :debug, ].each do |level| define_method level do |*args, &block| msg = args[0] # Block-level default args is a 1.9 feature msg ||= block.call if block logger = Raven.configuration[:logger] logger = ::Logger.new(STDOUT) if logger.nil? logger.send(level, "#{LOG_PREFIX}#{msg}") if logger end end end end raven-ruby-0.15.3/lib/raven/okjson.rb000066400000000000000000000340071264325443000174120ustar00rootroot00000000000000# encoding: UTF-8 # # Copyright 2011, 2012 Keith Rarick # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # See https://github.com/kr/okjson for updates. require 'stringio' # Some parts adapted from # http://golang.org/src/pkg/json/decode.go and # http://golang.org/src/pkg/utf8/utf8.go module Raven module OkJson Upstream = '43' extend self # Decodes a json document in string s and # returns the corresponding ruby value. # String s must be valid UTF-8. If you have # a string in some other encoding, convert # it first. # # String values in the resulting structure # will be UTF-8. def decode(s) ts = lex(s) v, ts = textparse(ts) if ts.length > 0 raise Error, 'trailing garbage' end v end # Encodes x into a json text. It may contain only # Array, Hash, String, Numeric, true, false, nil. # (Note, this list excludes Symbol.) # X itself must be an Array or a Hash. # No other value can be encoded, and an error will # be raised if x contains any other value, such as # Nan, Infinity, Symbol, and Proc, or if a Hash key # is not a String. # Strings contained in x must be valid UTF-8. def encode(x) case x when Hash then objenc(x) when Array then arrenc(x) else raise Error, 'root value must be an Array or a Hash' end end def valenc(x) case x when Hash then objenc(x) when Array then arrenc(x) when String then strenc(x) when Symbol then strenc(x.to_s) when Numeric then numenc(x) when true then "true" when false then "false" when nil then "null" else strenc((x.inspect rescue $!.to_s)) end end private # Parses a "json text" in the sense of RFC 4627. # Returns the parsed value and any trailing tokens. # Note: this is almost the same as valparse, # except that it does not accept atomic values. def textparse(ts) if ts.length <= 0 raise Error, 'empty' end typ, _, val = ts[0] case typ when '{' then objparse(ts) when '[' then arrparse(ts) else raise Error, "unexpected #{val.inspect}" end end # Parses a "value" in the sense of RFC 4627. # Returns the parsed value and any trailing tokens. def valparse(ts) if ts.length <= 0 raise Error, 'empty' end typ, _, val = ts[0] case typ when '{' then objparse(ts) when '[' then arrparse(ts) when :val,:str then [val, ts[1..-1]] else raise Error, "unexpected #{val.inspect}" end end # Parses an "object" in the sense of RFC 4627. # Returns the parsed value and any trailing tokens. def objparse(ts) ts = eat('{', ts) obj = {} unless ts[0] raise Error, "unexpected end of object" end if ts[0][0] == '}' return obj, ts[1..-1] end k, v, ts = pairparse(ts) obj[k] = v if ts[0][0] == '}' return obj, ts[1..-1] end loop do ts = eat(',', ts) k, v, ts = pairparse(ts) obj[k] = v if ts[0][0] == '}' return obj, ts[1..-1] end end end # Parses a "member" in the sense of RFC 4627. # Returns the parsed values and any trailing tokens. def pairparse(ts) (typ, _, k), ts = ts[0], ts[1..-1] if typ != :str raise Error, "unexpected #{k.inspect}" end ts = eat(':', ts) v, ts = valparse(ts) [k, v, ts] end # Parses an "array" in the sense of RFC 4627. # Returns the parsed value and any trailing tokens. def arrparse(ts) ts = eat('[', ts) arr = [] unless ts[0] raise Error, "unexpected end of array" end if ts[0][0] == ']' return arr, ts[1..-1] end v, ts = valparse(ts) arr << v if ts[0][0] == ']' return arr, ts[1..-1] end loop do ts = eat(',', ts) v, ts = valparse(ts) arr << v if ts[0][0] == ']' return arr, ts[1..-1] end end end def eat(typ, ts) if ts[0][0] != typ raise Error, "expected #{typ} (got #{ts[0].inspect})" end ts[1..-1] end # Scans s and returns a list of json tokens, # excluding white space (as defined in RFC 4627). def lex(s) ts = [] while s.length > 0 typ, lexeme, val = tok(s) if typ.nil? raise Error, "invalid character at #{s[0,10].inspect}" end if typ != :space ts << [typ, lexeme, val] end s = s[lexeme.length..-1] end ts end # Scans the first token in s and # returns a 3-element list, or nil # if s does not begin with a valid token. # # The first list element is one of # '{', '}', ':', ',', '[', ']', # :val, :str, and :space. # # The second element is the lexeme. # # The third element is the value of the # token for :val and :str, otherwise # it is the lexeme. def tok(s) case s[0] when ?{ then ['{', s[0,1], s[0,1]] when ?} then ['}', s[0,1], s[0,1]] when ?: then [':', s[0,1], s[0,1]] when ?, then [',', s[0,1], s[0,1]] when ?[ then ['[', s[0,1], s[0,1]] when ?] then [']', s[0,1], s[0,1]] when ?n then nulltok(s) when ?t then truetok(s) when ?f then falsetok(s) when ?" then strtok(s) when Spc, ?\t, ?\n, ?\r then [:space, s[0,1], s[0,1]] else numtok(s) end end def nulltok(s); s[0,4] == 'null' ? [:val, 'null', nil] : [] end def truetok(s); s[0,4] == 'true' ? [:val, 'true', true] : [] end def falsetok(s); s[0,5] == 'false' ? [:val, 'false', false] : [] end def numtok(s) m = /-?([1-9][0-9]+|[0-9])([.][0-9]+)?([eE][+-]?[0-9]+)?/.match(s) if m && m.begin(0) == 0 if !m[2] && !m[3] [:val, m[0], Integer(m[0])] elsif m[2] [:val, m[0], Float(m[0])] else # We don't convert scientific notation [:val, m[0], m[0]] end else [] end end def strtok(s) m = /"([^"\\]|\\["\/\\bfnrt]|\\u[0-9a-fA-F]{4})*"/.match(s) unless m raise Error, "invalid string literal at #{abbrev(s)}" end [:str, m[0], unquote(m[0])] end def abbrev(s) t = s[0,10] p = t['`'] t = t[0,p] if p t = t + '...' if t.length < s.length '`' + t + '`' end # Converts a quoted json string literal q into a UTF-8-encoded string. # The rules are different than for Ruby, so we cannot use eval. # Unquote will raise an error if q contains control characters. def unquote(q) q = q[1...-1] a = q.dup # allocate a big enough string # In ruby >= 1.9, a[w] is a codepoint, not a byte. if rubydoesenc? a.force_encoding('UTF-8') end r, w = 0, 0 while r < q.length c = q[r] if c == ?\\ r += 1 if r >= q.length raise Error, "string literal ends with a \"\\\": \"#{q}\"" end case q[r] when ?",?\\,?/,?' a[w] = q[r] r += 1 w += 1 when ?b,?f,?n,?r,?t a[w] = Unesc[q[r]] r += 1 w += 1 when ?u r += 1 uchar = begin hexdec4(q[r,4]) rescue RuntimeError => e raise Error, "invalid escape sequence \\u#{q[r,4]}: #{e}" end r += 4 if surrogate? uchar if q.length >= r+6 uchar1 = hexdec4(q[r+2,4]) uchar = subst(uchar, uchar1) if uchar != Ucharerr # A valid pair; consume. r += 6 end end end if rubydoesenc? a[w] = '' << uchar w += 1 else w += ucharenc(a, w, uchar) end else raise Error, "invalid escape char #{q[r]} in \"#{q}\"" end elsif c == ?" || c < Spc raise Error, "invalid character in string literal \"#{q}\"" else # Copy anything else byte-for-byte. # Valid UTF-8 will remain valid UTF-8. # Invalid UTF-8 will remain invalid UTF-8. # In ruby >= 1.9, c is a codepoint, not a byte, # in which case this is still what we want. a[w] = c r += 1 w += 1 end end a[0,w] end # Encodes unicode character u as UTF-8 # bytes in string a at position i. # Returns the number of bytes written. def ucharenc(a, i, u) if u <= Uchar1max a[i] = (u & 0xff).chr 1 elsif u <= Uchar2max a[i+0] = (Utag2 | ((u>>6)&0xff)).chr a[i+1] = (Utagx | (u&Umaskx)).chr 2 elsif u <= Uchar3max a[i+0] = (Utag3 | ((u>>12)&0xff)).chr a[i+1] = (Utagx | ((u>>6)&Umaskx)).chr a[i+2] = (Utagx | (u&Umaskx)).chr 3 else a[i+0] = (Utag4 | ((u>>18)&0xff)).chr a[i+1] = (Utagx | ((u>>12)&Umaskx)).chr a[i+2] = (Utagx | ((u>>6)&Umaskx)).chr a[i+3] = (Utagx | (u&Umaskx)).chr 4 end end def hexdec4(s) if s.length != 4 raise Error, 'short' end (nibble(s[0])<<12) | (nibble(s[1])<<8) | (nibble(s[2])<<4) | nibble(s[3]) end def subst(u1, u2) if Usurr1 <= u1 && u1 < Usurr2 && Usurr2 <= u2 && u2 < Usurr3 return ((u1-Usurr1)<<10) | (u2-Usurr2) + Usurrself end return Ucharerr end def surrogate?(u) Usurr1 <= u && u < Usurr3 end def nibble(c) if ?0 <= c && c <= ?9 then c.ord - ?0.ord elsif ?a <= c && c <= ?z then c.ord - ?a.ord + 10 elsif ?A <= c && c <= ?Z then c.ord - ?A.ord + 10 else raise Error, "invalid hex code #{c}" end end def objenc(x) '{' + x.map{|k,v| keyenc(k) + ':' + valenc(v)}.join(',') + '}' end def arrenc(a) '[' + a.map{|x| valenc(x)}.join(',') + ']' end def keyenc(k) case k when String then strenc(k) when Symbol then strenc(k.to_s) when Fixnum then strenc(k.to_s) else raise Error, "Hash key is not a string: #{k.inspect}" end end def strenc(s) t = StringIO.new t.putc(?") r = 0 while r < s.length case s[r] when ?" then t.print('\\"') when ?\\ then t.print('\\\\') when ?\b then t.print('\\b') when ?\f then t.print('\\f') when ?\n then t.print('\\n') when ?\r then t.print('\\r') when ?\t then t.print('\\t') else c = s[r] # In ruby >= 1.9, s[r] is a codepoint, not a byte. if rubydoesenc? begin # c.ord will raise an error if c is invalid UTF-8 if c.ord < Spc.ord c = "\\u%04x" % [c.ord] end t.write(c) rescue t.write(Ustrerr) end elsif c < Spc t.write("\\u%04x" % c) elsif Spc <= c && c <= ?~ t.putc(c) else n = ucharcopy(t, s, r) # ensure valid UTF-8 output r += n - 1 # r is incremented below end end r += 1 end t.putc(?") t.string end def numenc(x) if ((x.nan? || x.infinite?) rescue false) return strenc(x.to_s) end "#{x}" end # Copies the valid UTF-8 bytes of a single character # from string s at position i to I/O object t, and # returns the number of bytes copied. # If no valid UTF-8 char exists at position i, # ucharcopy writes Ustrerr and returns 1. def ucharcopy(t, s, i) n = s.length - i raise Utf8Error if n < 1 c0 = s[i].ord # 1-byte, 7-bit sequence? if c0 < Utagx t.putc(c0) return 1 end raise Utf8Error if c0 < Utag2 # unexpected continuation byte? raise Utf8Error if n < 2 # need continuation byte c1 = s[i+1].ord raise Utf8Error if c1 < Utagx || Utag2 <= c1 # 2-byte, 11-bit sequence? if c0 < Utag3 raise Utf8Error if ((c0&Umask2)<<6 | (c1&Umaskx)) <= Uchar1max t.putc(c0) t.putc(c1) return 2 end # need second continuation byte raise Utf8Error if n < 3 c2 = s[i+2].ord raise Utf8Error if c2 < Utagx || Utag2 <= c2 # 3-byte, 16-bit sequence? if c0 < Utag4 u = (c0&Umask3)<<12 | (c1&Umaskx)<<6 | (c2&Umaskx) raise Utf8Error if u <= Uchar2max t.putc(c0) t.putc(c1) t.putc(c2) return 3 end # need third continuation byte raise Utf8Error if n < 4 c3 = s[i+3].ord raise Utf8Error if c3 < Utagx || Utag2 <= c3 # 4-byte, 21-bit sequence? if c0 < Utag5 u = (c0&Umask4)<<18 | (c1&Umaskx)<<12 | (c2&Umaskx)<<6 | (c3&Umaskx) raise Utf8Error if u <= Uchar3max t.putc(c0) t.putc(c1) t.putc(c2) t.putc(c3) return 4 end raise Utf8Error rescue Utf8Error t.write(Ustrerr) return 1 end def rubydoesenc? ::String.method_defined?(:force_encoding) end class Utf8Error < ::StandardError end class Error < ::StandardError end Utagx = 0b1000_0000 Utag2 = 0b1100_0000 Utag3 = 0b1110_0000 Utag4 = 0b1111_0000 Utag5 = 0b1111_1000 Umaskx = 0b0011_1111 Umask2 = 0b0001_1111 Umask3 = 0b0000_1111 Umask4 = 0b0000_0111 Uchar1max = (1<<7) - 1 Uchar2max = (1<<11) - 1 Uchar3max = (1<<16) - 1 Ucharerr = 0xFFFD # unicode "replacement char" Ustrerr = "\xef\xbf\xbd" # unicode "replacement char" Usurrself = 0x10000 Usurr1 = 0xd800 Usurr2 = 0xdc00 Usurr3 = 0xe000 Spc = ' '[0] Unesc = {?b=>?\b, ?f=>?\f, ?n=>?\n, ?r=>?\r, ?t=>?\t} end end raven-ruby-0.15.3/lib/raven/processor.rb000066400000000000000000000002431264325443000201210ustar00rootroot00000000000000module Raven class Processor def initialize(client) @client = client end def process(_data) raise NotImplementedError end end end raven-ruby-0.15.3/lib/raven/processor/000077500000000000000000000000001264325443000175755ustar00rootroot00000000000000raven-ruby-0.15.3/lib/raven/processor/removecircularreferences.rb000066400000000000000000000006261264325443000252120ustar00rootroot00000000000000module Raven class Processor::RemoveCircularReferences < Processor def process(v, visited = []) return "(...)" if visited.include?(v.__id__) visited += [v.__id__] if v.is_a?(Hash) v.each_with_object({}) { |(k, v_), memo| memo[k] = process(v_, visited) } elsif v.is_a?(Array) v.map { |v_| process(v_, visited) } else v end end end end raven-ruby-0.15.3/lib/raven/processor/removestacktrace.rb000066400000000000000000000004611264325443000234650ustar00rootroot00000000000000module Raven class Processor::RemoveStacktrace < Processor def process(value) if value[:exception] value[:exception][:values].map do |single_exception| single_exception.delete(:stacktrace) if single_exception[:stacktrace] end end value end end end raven-ruby-0.15.3/lib/raven/processor/sanitizedata.rb000066400000000000000000000042101264325443000225770ustar00rootroot00000000000000require 'json' module Raven class Processor::SanitizeData < Processor STRING_MASK = '********' INT_MASK = 0 DEFAULT_FIELDS = %w(authorization password passwd secret ssn social(.*)?sec) CREDIT_CARD_RE = /^(?:\d[ -]*?){13,16}$/ REGEX_SPECIAL_CHARACTERS = %w(. $ ^ { [ ( | ) * + ?) attr_accessor :sanitize_fields, :sanitize_credit_cards def initialize(client) super self.sanitize_fields = client.configuration.sanitize_fields self.sanitize_credit_cards = client.configuration.sanitize_credit_cards end def process(value) value.each_with_object(value) { |(k,v), memo| memo[k] = sanitize(k,v) } end def sanitize(k,v) if v.is_a?(Hash) process(v) elsif v.is_a?(Array) v.map{|a| sanitize(k, a)} elsif k.to_s == 'query_string' sanitize_query_string(v) elsif v.is_a?(Integer) && matches_regexes?(k,v) INT_MASK elsif v.is_a?(String) if fields_re.match(v.to_s) && (json = parse_json_or_nil(v)) #if this string is actually a json obj, convert and sanitize json.is_a?(Hash) ? process(json).to_json : v elsif matches_regexes?(k,v) STRING_MASK else v end else v end end private def sanitize_query_string(query_string) query_hash = CGI.parse(query_string) processed_query_hash = process(query_hash) URI.encode_www_form(processed_query_hash) end def matches_regexes?(k, v) (sanitize_credit_cards && CREDIT_CARD_RE.match(v.to_s)) || fields_re.match(k.to_s) end def fields_re @fields_re ||= /#{(DEFAULT_FIELDS | sanitize_fields).map do |f| use_boundary?(f) ? "\\b#{f}\\b" : f end.join("|")}/i end def use_boundary?(string) !DEFAULT_FIELDS.include?(string) && !special_characters?(string) end def special_characters?(string) REGEX_SPECIAL_CHARACTERS.select { |r| string.include?(r) }.any? end def parse_json_or_nil(string) begin OkJson.decode(string) rescue Raven::OkJson::Error, NoMethodError nil end end end end raven-ruby-0.15.3/lib/raven/processor/utf8conversion.rb000066400000000000000000000014711264325443000231210ustar00rootroot00000000000000module Raven class Processor::UTF8Conversion < Processor def process(value) if value.is_a? Array value.map { |v| process v } elsif value.is_a? Hash value.merge(value) { |_, v| process v } elsif value.is_a?(Exception) && !value.message.valid_encoding? clean_exc = value.class.new(clean_invalid_utf8_bytes(value.message)) clean_exc.set_backtrace(value.backtrace) clean_exc else clean_invalid_utf8_bytes(value) end end private def clean_invalid_utf8_bytes(obj) if obj.respond_to?(:to_utf8) obj.to_utf8 elsif obj.respond_to?(:encoding) && obj.is_a?(String) obj.encode('UTF-16', :invalid => :replace, :undef => :replace, :replace => '').encode('UTF-8') else obj end end end end raven-ruby-0.15.3/lib/raven/transports.rb000066400000000000000000000006741264325443000203310ustar00rootroot00000000000000require 'raven/error' module Raven module Transports class Transport attr_accessor :configuration def initialize(configuration) @configuration = configuration end def send_event #(auth_header, data, options = {}) raise NotImplementedError.new('Abstract method not implemented') end protected def verify_configuration configuration.verify! end end end end raven-ruby-0.15.3/lib/raven/transports/000077500000000000000000000000001264325443000177755ustar00rootroot00000000000000raven-ruby-0.15.3/lib/raven/transports/dummy.rb000066400000000000000000000004421264325443000214550ustar00rootroot00000000000000module Raven module Transports class Dummy < Transport attr_accessor :events def initialize(*) super @events = [] end def send_event(auth_header, data, options = {}) @events << [auth_header, data, options] end end end end raven-ruby-0.15.3/lib/raven/transports/http.rb000066400000000000000000000032721264325443000213050ustar00rootroot00000000000000require 'faraday' require 'raven/transports' require 'raven/error' module Raven module Transports class HTTP < Transport attr_accessor :conn, :adapter def initialize(*args) super self.adapter = configuration.http_adapter || Faraday.default_adapter self.conn = set_conn end def send_event(auth_header, data, options = {}) project_id = configuration[:project_id] path = configuration[:path] + "/" response = conn.post "#{path}api/#{project_id}/store/" do |req| req.headers['Content-Type'] = options[:content_type] req.headers['X-Sentry-Auth'] = auth_header req.body = data end Raven.logger.warn "Error from Sentry server (#{response.status}): #{response.body}" unless response.status == 200 response end private def set_conn verify_configuration Raven.logger.debug "Raven HTTP Transport connecting to #{configuration.server}" ssl_configuration = configuration.ssl || {} ssl_configuration[:verify] = configuration.ssl_verification ssl_configuration[:ca_file] = configuration.ssl_ca_file conn = Faraday.new( :url => configuration[:server], :ssl => ssl_configuration ) do |builder| builder.adapter(*adapter) end conn.headers[:user_agent] = "sentry-ruby/#{Raven::VERSION}" conn.options[:proxy] = configuration.proxy if configuration.proxy conn.options[:timeout] = configuration.timeout if configuration.timeout conn.options[:open_timeout] = configuration.open_timeout if configuration.open_timeout conn end end end end raven-ruby-0.15.3/lib/raven/transports/udp.rb000066400000000000000000000011111264325443000211040ustar00rootroot00000000000000require 'socket' require 'raven/transports' require 'raven/error' module Raven module Transports class UDP < Transport def send_event(auth_header, data, _options = {}) conn.send "#{auth_header}\n\n#{data}", 0 end private def conn @conn ||= begin sock = UDPSocket.new sock.connect(self.configuration.host, self.configuration.port) sock end end def verify_configuration super raise Error.new('No port specified') unless self.configuration.port end end end end raven-ruby-0.15.3/lib/raven/version.rb000066400000000000000000000000461264325443000175700ustar00rootroot00000000000000module Raven VERSION = "0.15.3" end raven-ruby-0.15.3/lib/sentry-raven-without-integrations.rb000066400000000000000000000000251264325443000236270ustar00rootroot00000000000000require 'raven/base' raven-ruby-0.15.3/lib/sentry-raven.rb000066400000000000000000000000201264325443000174150ustar00rootroot00000000000000require "raven" raven-ruby-0.15.3/sentry-raven.gemspec000066400000000000000000000025311264325443000177000ustar00rootroot00000000000000$:.unshift File.expand_path('../lib', __FILE__) require 'raven/version' Gem::Specification.new do |gem| gem.name = "sentry-raven" gem.version = Raven::VERSION gem.platform = Gem::Platform::RUBY gem.description = gem.summary = "A gem that provides a client interface for the Sentry error logger" gem.email = "getsentry@googlegroups.com" gem.homepage = "https://github.com/getsentry/raven-ruby" gem.authors = ["Sentry Team"] gem.has_rdoc = true gem.extra_rdoc_files = ["README.md", "LICENSE"] gem.files = Dir['lib/**/*'] gem.executables = gem.files.grep(%r{^exe/}) { |f| File.basename(f) } gem.license = 'Apache-2.0' gem.required_ruby_version = '>= 1.8.7' gem.add_dependency "faraday", ">= 0.7.6" gem.add_development_dependency "rake" gem.add_development_dependency "rubocop" if RUBY_VERSION > '1.8.7' gem.add_development_dependency "rspec", "~> 3.0" gem.add_development_dependency "rspec-rails" gem.add_development_dependency "mime-types", "~> 1.16" gem.add_development_dependency "rest-client", "< 1.7.0" if RUBY_VERSION == '1.8.7' gem.add_development_dependency "rest-client" if RUBY_VERSION > '1.8.7' gem.add_development_dependency "timecop", "0.6.1" if RUBY_VERSION == '1.8.7' gem.add_development_dependency "timecop" if RUBY_VERSION > '1.8.7' gem.add_development_dependency "test-unit" if RUBY_VERSION > '2.2' end raven-ruby-0.15.3/spec/000077500000000000000000000000001264325443000146275ustar00rootroot00000000000000raven-ruby-0.15.3/spec/raven/000077500000000000000000000000001264325443000157425ustar00rootroot00000000000000raven-ruby-0.15.3/spec/raven/cli_spec.rb000066400000000000000000000020451264325443000200510ustar00rootroot00000000000000require 'spec_helper' require 'raven/cli' describe "CLI tests" do example "posting an exception" do stubs = Faraday::Adapter::Test::Stubs.new do |stub| stub.post('sentry/api/42/store/') { [200, {}, 'ok'] } end Raven.configure do |config| config.server = 'http://12345:67890@sentry.localdomain/sentry/42' config.environments = ["test"] config.current_environment = "test" config.http_adapter = [:test, stubs] end expect { Raven::CLI.test }.not_to raise_error stubs.verify_stubbed_calls end example "posting an exception to a prefixed DSN" do stubs = Faraday::Adapter::Test::Stubs.new do |stub| stub.post('/prefix/sentry/api/42/store/') { [200, {}, 'ok'] } end Raven.configure do |config| config.environments = ["test"] config.current_environment = "test" config.http_adapter = [:test, stubs] end expect { Raven::CLI.test 'http://12345:67890@sentry.localdomain/prefix/sentry/42' }.not_to raise_error stubs.verify_stubbed_calls end end raven-ruby-0.15.3/spec/raven/client_state_spec.rb000066400000000000000000000022451264325443000217620ustar00rootroot00000000000000require 'spec_helper' require 'timecop' describe Raven::ClientState do let(:state) { Raven::ClientState.new } it 'should try when online' do expect(state.should_try?).to eq(true) end it 'should not try with a new error' do state.failure expect(state.should_try?).to eq(false) end it 'should try again after time passes' do Timecop.freeze(-10) { state.failure } expect(state.should_try?).to eq(true) end it 'should try again after success' do state.failure state.success expect(state.should_try?).to eq(true) end it 'should try again after retry_after' do Timecop.freeze(-2) { state.failure(1) } expect(state.should_try?).to eq(true) end it 'should exponentially backoff' do Timecop.freeze do state.failure Timecop.travel(2) expect(state.should_try?).to eq(true) state.failure Timecop.travel(3) expect(state.should_try?).to eq(false) Timecop.travel(2) expect(state.should_try?).to eq(true) state.failure Timecop.travel(8) expect(state.should_try?).to eq(false) Timecop.travel(2) expect(state.should_try?).to eq(true) end end end raven-ruby-0.15.3/spec/raven/configuration_spec.rb000066400000000000000000000103521264325443000221510ustar00rootroot00000000000000require 'spec_helper' describe Raven::Configuration do before do # Make sure we reset the env in case something leaks in ENV.delete('SENTRY_DSN') ENV.delete('RAILS_ENV') ENV.delete('RACK_ENV') end shared_examples 'a complete configuration' do it 'should have a server' do expect(subject[:server]).to eq('http://sentry.localdomain/sentry') end it 'should have a scheme' do expect(subject[:scheme]).to eq('http') end it 'should have a public key' do expect(subject[:public_key]).to eq('12345') end it 'should have a secret key' do expect(subject[:secret_key]).to eq('67890') end it 'should have a host' do expect(subject[:host]).to eq('sentry.localdomain') end it 'should have a port' do expect(subject[:port]).to eq(80) end it 'should have a path' do expect(subject[:path]).to eq('/sentry') end it 'should have a project ID' do expect(subject[:project_id]).to eq('42') end it 'should not be async' do expect(subject[:async]).to eq(false) expect(subject[:async?]).to eq(false) end it 'should catch_debugged_exceptions' do expect(subject[:catch_debugged_exceptions]).to eq(true) end it 'should have no sanitize fields' do expect(subject[:sanitize_fields]).to eq([]) end end context 'being initialized without server configuration' do before do subject.environments = %w[ test ] end it 'should not send events' do expect(subject[:server]).to eq(nil) expect(subject.send_in_current_environment?).to eq(false) end end context 'being initialized with a server string' do before do subject.server = 'http://12345:67890@sentry.localdomain/sentry/42' end it_should_behave_like 'a complete configuration' end context 'being initialized with a DSN string' do before do subject.dsn = 'http://12345:67890@sentry.localdomain/sentry/42' end it_should_behave_like 'a complete configuration' end context 'being initialized with options' do before do subject.server = 'http://sentry.localdomain/sentry' subject.public_key = '12345' subject.secret_key = '67890' subject.project_id = '42' end it_should_behave_like 'a complete configuration' end context 'being initialized with an environment variable' do subject do ENV['SENTRY_DSN'] = 'http://12345:67890@sentry.localdomain/sentry/42' Raven::Configuration.new end it_should_behave_like 'a complete configuration' end context 'configuring for async' do it 'should be configurable to send events async' do subject.async = lambda { |_e| :ok } expect(subject.async.respond_to?(:call)).to eq(true) expect(subject.async.call('event')).to eq(:ok) end it 'should raise when setting async to anything other than callable or false' do expect { subject.async = Proc.new {} }.to_not raise_error expect { subject.async = lambda {} }.to_not raise_error expect { subject.async = false }.to_not raise_error expect { subject.async = true }.to raise_error(ArgumentError) end end context 'being initialized with a current environment' do before(:each) do subject.current_environment = 'test' subject.server = 'http://sentry.localdomain/sentry' end it 'should send events if test is whitelisted' do subject.environments = %w[ test ] expect(subject.send_in_current_environment?).to eq(true) end it 'should not send events if test is not whitelisted' do subject.environments = %w[ not_test ] expect(subject.send_in_current_environment?).to eq(false) end end context 'configuration for sanitize fields' do it 'should union default sanitize fields with user-defined sanitize fields' do fields = Raven::Processor::SanitizeData::DEFAULT_FIELDS | %w(test monkeybutt foo(.*)?bar) subject.sanitize_fields = fields client = Raven::Client.new(subject) processor = Raven::Processor::SanitizeData.new(client) expected_fields_re = /authorization|password|passwd|secret|ssn|social(.*)?sec|\btest\b|\bmonkeybutt\b|foo(.*)?bar/i expect(processor.send(:fields_re)).to eq(expected_fields_re) end end end raven-ruby-0.15.3/spec/raven/event_spec.rb000066400000000000000000000370061264325443000204300ustar00rootroot00000000000000require 'spec_helper' describe Raven::Event do before do Raven::Context.clear! end context 'a fully implemented event' do let(:hash) do Raven::Event.new({ :message => 'test', :level => 'warning', :logger => 'foo', :tags => { 'foo' => 'bar' }, :extra => { 'my_custom_variable' => 'value' }, :server_name => 'foo.local', :release => '721e41770371db95eee98ca2707686226b993eda', }).to_hash end it 'has message' do expect(hash[:message]).to eq('test') end it 'has level' do expect(hash[:level]).to eq(30) end it 'has logger' do expect(hash[:logger]).to eq('foo') end it 'has server name' do expect(hash[:server_name]).to eq('foo.local') end it 'has release' do expect(hash[:release]).to eq('721e41770371db95eee98ca2707686226b993eda') end it 'has tag data' do expect(hash[:tags]).to eq({ 'foo' => 'bar' }) end it 'has extra data' do expect(hash[:extra]).to eq({ 'my_custom_variable' => 'value' }) end it 'has platform' do expect(hash[:platform]).to eq('ruby') end end context 'user context specified' do let(:hash) do Raven.user_context({ 'id' => 'hello', }) Raven::Event.new({ :level => 'warning', :logger => 'foo', :tags => { 'foo' => 'bar' }, :extra => { 'my_custom_variable' => 'value' }, :server_name => 'foo.local', }).to_hash end it "adds user data" do expect(hash[:user]).to eq({ 'id' => 'hello', }) end end context 'tags context specified' do let(:hash) do Raven.tags_context({ 'key' => 'value', }) Raven::Event.new({ :level => 'warning', :logger => 'foo', :tags => { 'foo' => 'bar' }, :extra => { 'my_custom_variable' => 'value' }, :server_name => 'foo.local', }).to_hash end it "merges tags data" do expect(hash[:tags]).to eq({ 'key' => 'value', 'foo' => 'bar', }) end end context 'extra context specified' do let(:hash) do Raven.extra_context({ 'key' => 'value', }) Raven::Event.new({ :level => 'warning', :logger => 'foo', :tags => { 'foo' => 'bar' }, :extra => { 'my_custom_variable' => 'value' }, :server_name => 'foo.local', }).to_hash end it "merges extra data" do expect(hash[:extra]).to eq({ 'key' => 'value', 'my_custom_variable' => 'value', }) end end context 'rack context specified' do require 'stringio' let(:hash) do Raven.rack_context({ 'REQUEST_METHOD' => 'POST', 'QUERY_STRING' => 'biz=baz', 'HTTP_HOST' => 'localhost', 'SERVER_NAME' => 'localhost', 'SERVER_PORT' => '80', 'PATH_INFO' => '/lol', 'rack.url_scheme' => 'http', 'rack.input' => StringIO.new('foo=bar'), }) Raven::Event.new({ :level => 'warning', :logger => 'foo', :tags => { 'foo' => 'bar' }, :extra => { 'my_custom_variable' => 'value' }, :server_name => 'foo.local', }).to_hash end it "adds http data" do expect(hash[:request]).to eq({ :data => { 'foo' => 'bar' }, :env => { 'SERVER_NAME' => 'localhost', 'SERVER_PORT' => '80' }, :headers => { 'Host' => 'localhost' }, :method => 'POST', :query_string => 'biz=baz', :url => 'http://localhost/lol', :cookies => nil }) end end context 'configuration tags specified' do let(:hash) do config = Raven::Configuration.new config.tags = { 'key' => 'value' } Raven::Event.new( :level => 'warning', :logger => 'foo', :tags => { 'foo' => 'bar' }, :server_name => 'foo.local', :configuration => config ).to_hash end it 'merges tags data' do expect(hash[:tags]).to eq({ 'key' => 'value', 'foo' => 'bar' }) end end context 'configuration tags unspecified' do it 'should not persist tags between unrelated events' do config = Raven::Configuration.new Raven::Event.new( :level => 'warning', :logger => 'foo', :tags => { 'foo' => 'bar' }, :server_name => 'foo.local', :configuration => config ) hash = Raven::Event.new( :level => 'warning', :logger => 'foo', :server_name => 'foo.local', :configuration => config ).to_hash expect(hash[:tags]).to eq({}) end end context 'tags hierarchy respected' do let(:hash) do config = Raven::Configuration.new config.tags = { 'configuration_context_event_key' => 'configuration_value', 'configuration_context_key' => 'configuration_value', 'configuration_event_key' => 'configuration_value', 'configuration_key' => 'configuration_value', } Raven.tags_context({ 'configuration_context_event_key' => 'context_value', 'configuration_context_key' => 'context_value', 'context_event_key' => 'context_value', 'context_key' => 'context_value', }) Raven::Event.new( :level => 'warning', :logger => 'foo', :tags => { 'configuration_context_event_key' => 'event_value', 'configuration_event_key' => 'event_value', 'context_event_key' => 'event_value', 'event_key' => 'event_value', }, :server_name => 'foo.local', :configuration => config ).to_hash end it 'merges tags data' do expect(hash[:tags]).to eq({ 'configuration_context_event_key' => 'event_value', 'configuration_context_key' => 'context_value', 'configuration_event_key' => 'event_value', 'context_event_key' => 'event_value', 'configuration_key' => 'configuration_value', 'context_key' => 'context_value', 'event_key' => 'event_value', }) end end context 'merging user context' do before do Raven.user_context({ 'context_event_key' => 'context_value', 'context_key' => 'context_value', }) end let(:hash) do Raven::Event.new({ :user => { 'context_event_key' => 'event_value', 'event_key' => 'event_value', }, }).to_hash end it 'prioritizes event context over request context' do expect(hash[:user]).to eq({ 'context_event_key' => 'event_value', 'context_key' => 'context_value', 'event_key' => 'event_value', }) end end context 'merging extra context' do before do Raven.extra_context({ 'context_event_key' => 'context_value', 'context_key' => 'context_value', }) end let(:hash) do Raven::Event.new({ :extra => { 'context_event_key' => 'event_value', 'event_key' => 'event_value', }, }).to_hash end it 'prioritizes event context over request context' do expect(hash[:extra]).to eq({ 'context_event_key' => 'event_value', 'context_key' => 'context_value', 'event_key' => 'event_value', }) end end describe '.initialize' do it 'should not touch the env object for an ignored environment' do Raven.configure do |config| config.current_environment = 'test' end Raven.rack_context({}) expect { Raven::Event.new }.not_to raise_error end end describe '.capture_message' do let(:message) { 'This is a message' } let(:hash) { Raven::Event.capture_message(message).to_hash } context 'for a Message' do it 'returns an event' do expect(Raven::Event.capture_message(message)).to be_a(Raven::Event) end it "sets the message to the value passed" do expect(hash[:message]).to eq(message) end it 'has level ERROR' do expect(hash[:level]).to eq(40) end it 'accepts an options hash' do expect(Raven::Event.capture_message(message, :logger => 'logger').logger).to eq('logger') end it 'accepts a stacktrace' do backtrace = ["/path/to/some/file:22:in `function_name'", "/some/other/path:1412:in `other_function'"] evt = Raven::Event.capture_message(message, :backtrace => backtrace) expect(evt[:stacktrace]).to be_a(Raven::StacktraceInterface) frames = evt[:stacktrace].to_hash[:frames] expect(frames.length).to eq(2) expect(frames[0][:lineno]).to eq(1412) expect(frames[0][:function]).to eq('other_function') expect(frames[0][:filename]).to eq('/some/other/path') expect(frames[1][:lineno]).to eq(22) expect(frames[1][:function]).to eq('function_name') expect(frames[1][:filename]).to eq('/path/to/some/file') end end end describe '.capture_exception' do let(:message) { 'This is a message' } let(:exception) { Exception.new(message) } let(:hash) { Raven::Event.capture_exception(exception).to_hash } context 'for an Exception' do it 'returns an event' do expect(Raven::Event.capture_exception(exception)).to be_a(Raven::Event) end it "sets the message to the exception's message and type" do expect(hash[:message]).to eq("Exception: #{message}") end # sentry uses python's logging values; 40 is the value of logging.ERROR it 'has level ERROR' do expect(hash[:level]).to eq(40) end it 'uses the exception class name as the exception type' do expect(hash[:exception][:values][0][:type]).to eq('Exception') end it 'uses the exception message as the exception value' do expect(hash[:exception][:values][0][:value]).to eq(message) end it 'does not belong to a module' do expect(hash[:exception][:values][0][:module]).to eq('') end end context 'for a nested exception type' do module Raven::Test class Exception < Exception; end end let(:exception) { Raven::Test::Exception.new(message) } it 'sends the module name as part of the exception info' do expect(hash[:exception][:values][0][:module]).to eq('Raven::Test') end end context 'for a Raven::Error' do let(:exception) { Raven::Error.new } it 'does not create an event' do expect(Raven::Event.capture_exception(exception)).to be_nil end end context 'for an excluded exception type' do module Raven::Test class BaseExc < Exception; end class SubExc < BaseExc; end end it 'returns nil for a string match' do config = Raven::Configuration.new config.excluded_exceptions << 'Raven::Test::BaseExc' expect(Raven::Event.capture_exception(Raven::Test::BaseExc.new, :configuration => config)).to be_nil end it 'returns nil for a class match' do config = Raven::Configuration.new config.excluded_exceptions << Raven::Test::BaseExc expect(Raven::Event.capture_exception(Raven::Test::SubExc.new, :configuration => config)).to be_nil end end # Only check causes when they're supported if Exception.new.respond_to? :cause context 'when the exception has a cause' do let(:exception) { build_exception_with_cause } it 'captures the cause' do expect(hash[:exception][:values].length).to eq(2) end end context 'when the exception has nested causes' do let(:exception) { build_exception_with_two_causes } it 'captures nested causes' do expect(hash[:exception][:values].length).to eq(3) end end end context 'when the exception has a recursive cause' do let(:exception) { build_exception_with_recursive_cause } it 'should handle it gracefully' do expect(hash[:exception][:values].length).to eq(2) end end if RUBY_PLATFORM == "java" context 'when running under jRuby' do let(:exception) do begin raise java.lang.OutOfMemoryError.new("A Java error") rescue Exception => e return e end end it 'should have a backtrace' do frames = hash[:exception][:values][0][:stacktrace][:frames] expect(frames.length).not_to eq(0) end end end context 'when the exception has a backtrace' do let(:exception) do e = Exception.new(message) allow(e).to receive(:backtrace).and_return([ "/path/to/some/file:22:in `function_name'", "/some/other/path:1412:in `other_function'", ]) e end it 'parses the backtrace' do frames = hash[:exception][:values][0][:stacktrace][:frames] expect(frames.length).to eq(2) expect(frames[0][:lineno]).to eq(1412) expect(frames[0][:function]).to eq('other_function') expect(frames[0][:filename]).to eq('/some/other/path') expect(frames[1][:lineno]).to eq(22) expect(frames[1][:function]).to eq('function_name') expect(frames[1][:filename]).to eq('/path/to/some/file') end context 'with internal backtrace' do let(:exception) do e = Exception.new(message) allow(e).to receive(:backtrace).and_return([":10:in `synchronize'"]) e end it 'marks filename and in_app correctly' do frames = hash[:exception][:values][0][:stacktrace][:frames] expect(frames[0][:lineno]).to eq(10) expect(frames[0][:function]).to eq("synchronize") expect(frames[0][:filename]).to eq("") end end it "sets the culprit" do expect(hash[:culprit]).to eq("/path/to/some/file in function_name at line 22") end context 'when a path in the stack trace is on the load path' do before do $LOAD_PATH << '/some' end after do $LOAD_PATH.delete('/some') end it 'strips prefixes in the load path from frame filenames' do frames = hash[:exception][:values][0][:stacktrace][:frames] expect(frames[0][:filename]).to eq('other/path') end end end it 'accepts an options hash' do expect(Raven::Event.capture_exception(exception, :logger => 'logger').logger).to eq('logger') end it 'uses an annotation if one exists' do Raven.annotate_exception(exception, :logger => 'logger') expect(Raven::Event.capture_exception(exception).logger).to eq('logger') end it 'accepts a checksum' do expect(Raven::Event.capture_exception(exception, :checksum => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa').checksum).to eq('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') end it 'accepts a release' do expect(Raven::Event.capture_exception(exception, :release => '1.0').release).to eq('1.0') end it 'accepts a fingerprint' do expect(Raven::Event.capture_exception(exception, :fingerprint => ['{{ default }}', 'foo']).fingerprint).to eq(['{{ default }}', 'foo']) end it 'accepts a logger' do expect(Raven::Event.capture_exception(exception, :logger => 'root').logger).to eq('root') end end end raven-ruby-0.15.3/spec/raven/integration_spec.rb000066400000000000000000000050511264325443000216250ustar00rootroot00000000000000require 'spec_helper' describe "Integration tests" do example "posting an exception" do stubs = Faraday::Adapter::Test::Stubs.new do |stub| stub.post('sentry/api/42/store/') { [200, {}, 'ok'] } end io = StringIO.new Raven.configure do |config| config.server = 'http://12345:67890@sentry.localdomain/sentry/42' config.environments = ["test"] config.current_environment = "test" config.http_adapter = [:test, stubs] config.logger = Logger.new(io) end Raven.capture_exception(build_exception) stubs.verify_stubbed_calls expect(io.string).to match(/Sending event [0-9a-f]+ to Sentry$/) end example "posting an exception to a prefixed DSN" do stubs = Faraday::Adapter::Test::Stubs.new do |stub| stub.post('/prefix/sentry/api/42/store/') { [200, {}, 'ok'] } end Raven.configure do |config| config.server = 'http://12345:67890@sentry.localdomain/prefix/sentry/42' config.environments = ["test"] config.current_environment = "test" config.http_adapter = [:test, stubs] end Raven.capture_exception(build_exception) stubs.verify_stubbed_calls end example "hitting quota limit shouldn't swallow exception" do stubs = Faraday::Adapter::Test::Stubs.new do |stub| stub.post('sentry/api/42/store/') { [403, {}, 'Creation of this event was blocked'] } end Raven.configure do |config| config.server = 'http://12345:67890@sentry.localdomain/sentry/42' config.environments = ["test"] config.current_environment = "test" config.http_adapter = [:test, stubs] end expect(Raven.logger).to receive(:warn).once expect { Raven.capture_exception(build_exception) }.not_to raise_error stubs.verify_stubbed_calls end example "timed backoff should prevent sends" do io = StringIO.new Raven.configure do |config| config.server = 'http://12345:67890@sentry.localdomain/sentry/42' config.environments = ["test"] config.current_environment = "test" config.http_adapter = [:test, nil] config.logger = Logger.new(io) end expect_any_instance_of(Raven::Transports::HTTP).to receive(:send_event).exactly(1).times.and_raise(Faraday::Error::ConnectionFailed, "conn failed") expect { Raven.capture_exception(build_exception) }.not_to raise_error expect(Raven.logger).to receive(:error).exactly(1).times expect { Raven.capture_exception(build_exception) }.not_to raise_error expect(io.string).to match(/Failed to submit event: ZeroDivisionError: divided by 0$/) end end raven-ruby-0.15.3/spec/raven/integrations/000077500000000000000000000000001264325443000204505ustar00rootroot00000000000000raven-ruby-0.15.3/spec/raven/integrations/rack_spec.rb000066400000000000000000000040211264325443000227240ustar00rootroot00000000000000require 'spec_helper' require 'raven/integrations/rack' describe Raven::Rack do it 'should capture exceptions' do exception = build_exception env = {} expect(Raven::Rack).to receive(:capture_exception).with(exception, env) app = lambda { |_e| raise exception } stack = Raven::Rack.new(app) expect { stack.call(env) }.to raise_error end it 'should capture rack.exception' do exception = build_exception env = {} expect(Raven::Rack).to receive(:capture_exception).with(exception, env) app = lambda do |e| e['rack.exception'] = exception [200, {}, ['okay']] end stack = Raven::Rack.new(app) stack.call(env) end it 'should capture sinatra errors' do exception = build_exception env = {} expect(Raven::Rack).to receive(:capture_exception).with(exception, env) app = lambda do |e| e['sinatra.error'] = exception [200, {}, ['okay']] end stack = Raven::Rack.new(app) stack.call(env) end it 'should clear context after app is called' do Raven::Context.current.tags[:environment] = :test app = lambda { |env| ['response', {}, env] } stack = Raven::Rack.new(app) stack.call({}) expect(Raven::Context.current.tags).to eq({}) end it 'should allow empty rack env in rspec tests' do env = {} # the rack env is empty when running rails/rspec tests Raven.rack_context(env) expect { Raven.capture_exception(build_exception) }.not_to raise_error end it 'should bind request context' do Raven::Context.current.rack_env = nil app = lambda do |env| expect(Raven::Context.current.rack_env).to eq(env) ['response', {}, env] end stack = Raven::Rack.new(app) env = { :foo => :bar } stack.call(env) end it 'should pass rack/lint' do env = Rack::MockRequest.env_for("/test") app = proc do [200, {'Content-Type' => 'text/plain'}, ['OK']] end stack = Raven::Rack.new(Rack::Lint.new(app)) expect { stack.call(env) }.to_not raise_error end end raven-ruby-0.15.3/spec/raven/integrations/rails/000077500000000000000000000000001264325443000215625ustar00rootroot00000000000000raven-ruby-0.15.3/spec/raven/integrations/rails/controller_methods_spec.rb000066400000000000000000000015241264325443000270310ustar00rootroot00000000000000require 'spec_helper' require 'raven' require 'raven/integrations/rails/controller_methods' describe Raven::Rails::ControllerMethods do include described_class let(:env) { { "foo" => "bar" } } let(:request) { double('request', :env => env) } let(:options) { double('options') } describe "#capture_message" do let(:message) { double('message') } it "captures a message with the request environment" do expect(Raven::Rack).to receive(:capture_message).with(message, env, options) capture_message(message, options) end end describe "#capture_exception" do let(:exception) { double('exception') } it "captures a exception with the request environment" do expect(Raven::Rack).to receive(:capture_exception).with(exception, env, options) capture_exception(exception, options) end end end raven-ruby-0.15.3/spec/raven/integrations/rails/event_spec.rb000066400000000000000000000055201264325443000242440ustar00rootroot00000000000000describe Raven::Event do context 'in a rails environment' do before do Raven.configure do |config| config.project_root ||= ::Rails.root end end context 'with an application stacktrace' do let(:exception) do e = Exception.new("Oh no!") allow(e).to receive(:backtrace).and_return([ "#{Rails.root}/vendor/bundle/cache/other_gem.rb:10:in `public_method'", "vendor/bundle/some_gem.rb:10:in `a_method'", "#{Rails.root}/app/models/user.rb:132:in `new_function'", "/gem/lib/path:87:in `a_function'", "/app/some/other/path:1412:in `other_function'", "test/some/other/path:1412:in `other_function'" ]) e end let(:hash) { Raven::Event.capture_exception(exception).to_hash } it 'marks in_app correctly' do frames = hash[:exception][:values][0][:stacktrace][:frames] expect(frames[0][:filename]).to eq("test/some/other/path") expect(frames[0][:in_app]).to eq(true) expect(frames[1][:filename]).to eq("/app/some/other/path") expect(frames[1][:in_app]).to eq(false) expect(frames[2][:filename]).to eq("/gem/lib/path") expect(frames[2][:in_app]).to eq(false) expect(frames[3][:filename]).to eq("app/models/user.rb") expect(frames[3][:in_app]).to eq(true) expect(frames[4][:filename]).to eq("vendor/bundle/some_gem.rb") expect(frames[4][:in_app]).to eq(false) expect(frames[5][:filename]).to eq("vendor/bundle/cache/other_gem.rb") expect(frames[5][:in_app]).to eq(false) end context 'when an in_app path under project_root is on the load path' do before do $LOAD_PATH << "#{Rails.root}/app/models" end after do $LOAD_PATH.delete("#{Rails.root}/app/models") end it 'normalizes the filename using project_root' do frames = hash[:exception][:values][0][:stacktrace][:frames] expect(frames[3][:filename]).to eq("app/models/user.rb") end end context 'when a non-in_app path under project_root is on the load path' do before do $LOAD_PATH << "#{Rails.root}/vendor/bundle" end after do $LOAD_PATH.delete("#{Rails.root}/vendor/bundle") end it 'normalizes the filename using the load path' do frames = hash[:exception][:values][0][:stacktrace][:frames] expect(frames[5][:filename]).to eq("cache/other_gem.rb") end end context "when a non-in_app path under project_root isn't on the load path" do it 'normalizes the filename using project_root' do frames = hash[:exception][:values][0][:stacktrace][:frames] expect(frames[5][:filename]).to eq("vendor/bundle/cache/other_gem.rb") end end end end end raven-ruby-0.15.3/spec/raven/integrations/rails_spec.rb000066400000000000000000000016511264325443000231240ustar00rootroot00000000000000require "spec_helper" require "rspec/rails" require "raven/transports/dummy" describe TestApp, :type => :request do before(:all) do Raven.configure do |config| config.dsn = 'dummy://notaserver' config.encoding = 'json' end Rails.env = "production" TestApp.initialize! end after(:each) do Raven.client.transport.events = [] end it "inserts middleware" do expect(TestApp.middleware).to include(Raven::Rack) end it "should capture exceptions in production" do get "/exception" expect(response.status).to eq(500) expect(Raven.client.transport.events.size).to eq(1) end it "should properly set the exception's URL" do get "/exception" # TODO: dummy transport shouldn't even encode the event event = Raven.client.transport.events.first event = JSON.parse!(event[1]) expect(event['request']['url']).to eq("http://www.example.com/exception") end end raven-ruby-0.15.3/spec/raven/integrations/rake_spec.rb000066400000000000000000000003061264325443000227300ustar00rootroot00000000000000require 'rake' describe 'Rake tasks' do it 'should capture exceptions in Rake tasks' do expect(`cd spec/support && bundle exec rake raise_exception 2>&1`).to match(/Sending event/) end end raven-ruby-0.15.3/spec/raven/logger_spec.rb000066400000000000000000000026141264325443000205630ustar00rootroot00000000000000require 'spec_helper' describe Raven::Logger do context 'without a backend logger' do before do allow(Raven.configuration).to receive(:logger) { nil } end it 'should not error' do subject.fatal 'fatalmsg' subject.error 'errormsg' subject.warn 'warnmsg' subject.info 'infomsg' subject.debug 'debugmsg' end end context 'with a backend logger' do before do @logger = double('logger') allow(Raven.configuration).to receive(:logger) { @logger } end it 'should log fatal messages' do expect(@logger).to receive(:fatal).with('** [Raven] fatalmsg') subject.fatal 'fatalmsg' end it 'should log error messages' do expect(@logger).to receive(:error).with('** [Raven] errormsg') subject.error 'errormsg' end it 'should log warning messages' do expect(@logger).to receive(:warn).with('** [Raven] warnmsg') subject.warn 'warnmsg' end it 'should log info messages' do expect(@logger).to receive(:info).with('** [Raven] infomsg') subject.info 'infomsg' end it 'should log debug messages' do expect(@logger).to receive(:debug).with('** [Raven] debugmsg') subject.debug 'debugmsg' end it 'should log messages from blocks' do expect(@logger).to receive(:info).with('** [Raven] infoblock') subject.info { 'infoblock' } end end end raven-ruby-0.15.3/spec/raven/okjson_spec.rb000066400000000000000000000030031264325443000206000ustar00rootroot00000000000000require 'spec_helper' describe Raven::OkJson do data = [ OpenStruct.new(:key => 'foo', :val => 'bar', :enc_key => '"foo"', :enc_val => '"bar"'), OpenStruct.new(:key => :foo, :val => :bar, :enc_key => '"foo"', :enc_val => '"bar"'), OpenStruct.new(:key => 1, :val => 1, :enc_key => '"1"', :enc_val => '1') ] data.each do |obj| it "works with #{obj.key.class} keys" do expect(Raven::OkJson.encode(obj.key => 'bar')).to eq "{#{obj.enc_key}:\"bar\"}" end it "works with #{obj.val.class} values" do expect(Raven::OkJson.encode('bar' => obj.val)).to eq "{\"bar\":#{obj.enc_val}}" end it "works with an array of #{obj.val.class}s" do expect(Raven::OkJson.encode('bar' => [obj.val])).to eq "{\"bar\":[#{obj.enc_val}]}" end it "works with a hash of #{obj.val.class}s" do expect(Raven::OkJson.encode('bar' => {obj.key => obj.val})).to eq "{\"bar\":{#{obj.enc_key}:#{obj.enc_val}}}" end end it 'encodes anything that responds to to_s' do data = [ (1..5), :symbol, 1/0.0, 0/0.0 ] expect(Raven::OkJson.encode(data)).to eq "[\"1..5\",\"symbol\",\"Infinity\",\"NaN\"]" end it 'does not parse scientific notation' do expect(Raven::OkJson.decode("[123e090]")).to eq ["123e090"] end it 'it raises the correct error on strings that look like incomplete objects' do expect{Raven::OkJson.decode("{")}.to raise_error(Raven::OkJson::Error) expect{Raven::OkJson.decode("[")}.to raise_error(Raven::OkJson::Error) end end raven-ruby-0.15.3/spec/raven/processors/000077500000000000000000000000001264325443000201445ustar00rootroot00000000000000raven-ruby-0.15.3/spec/raven/processors/removecirculareferences_spec.rb000066400000000000000000000013341264325443000264060ustar00rootroot00000000000000# Encoding: utf-8 require 'spec_helper' describe Raven::Processor::RemoveCircularReferences do before do @client = double("client") @processor = Raven::Processor::RemoveCircularReferences.new(@client) end it 'should cleanup circular references' do data = {} data['data'] = data data['ary'] = [] data['ary'].push('x' => data['ary']) data['ary2'] = data['ary'] data['leave intact'] = { 'not a circular reference' => true } result = @processor.process(data) expect(result['data']).to eq('(...)') expect(result['ary'].first['x']).to eq('(...)') expect(result['ary2']).not_to eq('(...)') expect(result['leave intact']).to eq({ 'not a circular reference' => true }) end end raven-ruby-0.15.3/spec/raven/processors/removestacktrace_spec.rb000066400000000000000000000032201264325443000250420ustar00rootroot00000000000000require 'spec_helper' require 'raven/processor/removestacktrace' describe Raven::Processor::RemoveStacktrace do before do @client = double("client") @processor = Raven::Processor::RemoveStacktrace.new(@client) end it 'should remove stacktraces' do data = Raven::Event.capture_exception(build_exception).to_hash expect(data[:exception][:values][0][:stacktrace]).to_not eq(nil) result = @processor.process(data) expect(result[:exception][:values][0][:stacktrace]).to eq(nil) end # Only check causes when they're supported if Exception.new.respond_to? :cause it 'should remove stacktraces from causes' do data = Raven::Event.capture_exception(build_exception_with_cause).to_hash expect(data[:exception][:values][0][:stacktrace]).to_not eq(nil) expect(data[:exception][:values][1][:stacktrace]).to_not eq(nil) result = @processor.process(data) expect(result[:exception][:values][0][:stacktrace]).to eq(nil) expect(result[:exception][:values][1][:stacktrace]).to eq(nil) end it 'should remove stacktraces from nested causes' do data = Raven::Event.capture_exception(build_exception_with_two_causes).to_hash expect(data[:exception][:values][0][:stacktrace]).to_not eq(nil) expect(data[:exception][:values][1][:stacktrace]).to_not eq(nil) expect(data[:exception][:values][2][:stacktrace]).to_not eq(nil) result = @processor.process(data) expect(result[:exception][:values][0][:stacktrace]).to eq(nil) expect(result[:exception][:values][1][:stacktrace]).to eq(nil) expect(result[:exception][:values][2][:stacktrace]).to eq(nil) end end end raven-ruby-0.15.3/spec/raven/processors/sanitizedata_processor_spec.rb000066400000000000000000000153451264325443000262720ustar00rootroot00000000000000require 'spec_helper' describe Raven::Processor::SanitizeData do before do @client = double("client") allow(@client).to receive_message_chain(:configuration, :sanitize_fields) { ['user_field'] } allow(@client).to receive_message_chain(:configuration, :sanitize_credit_cards) { true } @processor = Raven::Processor::SanitizeData.new(@client) end it 'should filter http data' do data = { 'sentry.interfaces.Http' => { 'data' => { 'foo' => 'bar', 'password' => 'hello', 'the_secret' => 'hello', 'a_password_here' => 'hello', 'mypasswd' => 'hello', 'test' => 1, :ssn => '123-45-6789', # test symbol handling 'social_security_number' => 123456789, 'user_field' => 'user', 'user_field_foo' => 'hello' } } } result = @processor.process(data) vars = result["sentry.interfaces.Http"]["data"] expect(vars["foo"]).to eq("bar") expect(vars["password"]).to eq(Raven::Processor::SanitizeData::STRING_MASK) expect(vars["the_secret"]).to eq(Raven::Processor::SanitizeData::STRING_MASK) expect(vars["a_password_here"]).to eq(Raven::Processor::SanitizeData::STRING_MASK) expect(vars["mypasswd"]).to eq(Raven::Processor::SanitizeData::STRING_MASK) expect(vars["test"]).to eq(1) expect(vars[:ssn]).to eq(Raven::Processor::SanitizeData::STRING_MASK) expect(vars["social_security_number"]).to eq(Raven::Processor::SanitizeData::INT_MASK) expect(vars["user_field"]).to eq(Raven::Processor::SanitizeData::STRING_MASK) expect(vars["user_field_foo"]).to eq('hello') end it 'should filter json data' do data_with_json = { 'json' => { 'foo' => 'bar', 'password' => 'hello', 'the_secret' => 'hello', 'a_password_here' => 'hello', 'mypasswd' => 'hello', 'test' => 1, 'ssn' => '123-45-6789', 'social_security_number' => 123456789, 'user_field' => 'user', 'user_field_foo' => 'hello' }.to_json } result = JSON.parse(@processor.process(data_with_json)['json']) vars = result expect(vars["foo"]).to eq("bar") expect(vars["password"]).to eq(Raven::Processor::SanitizeData::STRING_MASK) expect(vars["the_secret"]).to eq(Raven::Processor::SanitizeData::STRING_MASK) expect(vars["a_password_here"]).to eq(Raven::Processor::SanitizeData::STRING_MASK) expect(vars["mypasswd"]).to eq(Raven::Processor::SanitizeData::STRING_MASK) expect(vars["test"]).to eq(1) expect(vars["ssn"]).to eq(Raven::Processor::SanitizeData::STRING_MASK) expect(vars["social_security_number"]).to eq(Raven::Processor::SanitizeData::INT_MASK) expect(vars["user_field"]).to eq(Raven::Processor::SanitizeData::STRING_MASK) expect(vars["user_field_foo"]).to eq('hello') end it 'should filter json embedded in a ruby object' do data_with_embedded_json = { 'data' => { 'json' => %w(foo bar).to_json, 'json_hash' => {'foo' => 'bar'}.to_json, 'sensitive' => {'password' => 'secret'}.to_json } } result = @processor.process(data_with_embedded_json) expect(JSON.parse(result["data"]["json"])).to eq(%w(foo bar)) expect(JSON.parse(result["data"]["json_hash"])).to eq({'foo' => 'bar'}) expect(JSON.parse(result["data"]["sensitive"])).to eq({'password' => Raven::Processor::SanitizeData::STRING_MASK}) end it 'should not fail when json is invalid' do data_with_invalid_json = { 'data' => { 'invalid' => "{\r\n\"key\":\"value\",\r\n \"foo\":{\"bar\":\"baz\"}\r\n" } } result = @processor.process(data_with_invalid_json) expect{JSON.parse(result["data"]["invalid"])}.to raise_exception(JSON::ParserError) end it 'should filter credit card values' do data = { 'ccnumba' => '4242424242424242', 'ccnumba_13' => '4242424242424', 'ccnumba-dash' => '4242-4242-4242-4242', 'ccnumba_int' => 4242424242424242, } result = @processor.process(data) expect(result["ccnumba"]).to eq(Raven::Processor::SanitizeData::STRING_MASK) expect(result["ccnumba_13"]).to eq(Raven::Processor::SanitizeData::STRING_MASK) expect(result["ccnumba_int"]).to eq(Raven::Processor::SanitizeData::INT_MASK) end it 'should pass through credit card values if configured' do @processor.sanitize_credit_cards = false data = { 'ccnumba' => '4242424242424242', 'ccnumba_13' => '4242424242424', 'ccnumba-dash' => '4242-4242-4242-4242', 'ccnumba_int' => 4242424242424242, } result = @processor.process(data) expect(result["ccnumba"]).to eq('4242424242424242') expect(result["ccnumba_13"]).to eq('4242424242424') expect(result["ccnumba_int"]).to eq(4242424242424242) end it 'sanitizes hashes nested in arrays' do data = { "empty_array"=> [], "array"=>[{'password' => 'secret'}], } result = @processor.process(data) expect(result["array"][0]['password']).to eq(Raven::Processor::SanitizeData::STRING_MASK) end context "query strings" do it 'sanitizes' do data = { 'sentry.interfaces.Http' => { 'data' => { 'query_string' => 'foo=bar&password=secret' } } } result = @processor.process(data) vars = result["sentry.interfaces.Http"]["data"] expect(vars["query_string"]).to_not include("secret") end it 'handles :query_string as symbol' do data = { 'sentry.interfaces.Http' => { 'data' => { :query_string => 'foo=bar&password=secret' } } } result = @processor.process(data) vars = result["sentry.interfaces.Http"]["data"] expect(vars[:query_string]).to_not include("secret") end it 'handles multiple values for a key' do data = { 'sentry.interfaces.Http' => { 'data' => { 'query_string' => 'foo=bar&foo=fubar&foo=barfoo' } } } result = @processor.process(data) vars = result["sentry.interfaces.Http"]["data"] query_string = vars["query_string"].split('&') expect(query_string).to include("foo=bar") expect(query_string).to include("foo=fubar") expect(query_string).to include("foo=barfoo") end it 'handles url encoded keys and values' do encoded_query_string = 'Bio+4%24=cA%24%7C-%7C+M%28%29n3%5E' data = { 'sentry.interfaces.Http' => { 'data' => { 'query_string' => encoded_query_string } } } result = @processor.process(data) vars = result["sentry.interfaces.Http"]["data"] expect(vars["query_string"]).to eq(encoded_query_string) end it 'handles url encoded values' do end end end raven-ruby-0.15.3/spec/raven/processors/utf8conversion_spec.rb000066400000000000000000000040411264325443000244760ustar00rootroot00000000000000# Encoding: utf-8 require 'spec_helper' describe Raven::Processor::UTF8Conversion do before do @client = double("client") @processor = Raven::Processor::UTF8Conversion.new(@client) end if RUBY_VERSION > '1.8.7' it 'should not fail because of invalid byte sequence in UTF-8' do data = {} data['invalid'] = "invalid utf8 string goes here\255".force_encoding('UTF-8') expect { @processor.process(data) }.not_to raise_error end it 'should cleanup invalid UTF-8 bytes' do data = {} data['invalid'] = "invalid utf8 string goes here\255".force_encoding('UTF-8') results = @processor.process(data) expect(results['invalid']).to eq("invalid utf8 string goes here") end it "should cleanup invalid UTF-8 bytes in Exception messages" do data = Exception.new("invalid utf8 string goes here\255".force_encoding('UTF-8')) results = @processor.process(data) expect(results.message).to eq("invalid utf8 string goes here") end it 'should keep valid UTF-8 bytes after cleaning' do data = {} data['invalid'] = "한국, 中國, 日本(にっぽん)\255".force_encoding('UTF-8') results = @processor.process(data) expect(results['invalid']).to eq("한국, 中國, 日本(にっぽん)") end it 'should work recursively on hashes' do data = {'nested' => {}} data['nested']['invalid'] = "invalid utf8 string goes here\255".force_encoding('UTF-8') results = @processor.process(data) expect(results['nested']['invalid']).to eq("invalid utf8 string goes here") end it 'should work recursively on arrays' do data = ['good string', 'good string', ['good string', "invalid utf8 string goes here\255".force_encoding('UTF-8')]] results = @processor.process(data) expect(results[2][1]).to eq("invalid utf8 string goes here") end it 'should not blow up on symbols' do data = {:key => :value} results = @processor.process(data) expect(results[:key]).to eq(:value) end end end raven-ruby-0.15.3/spec/raven/raven_spec.rb000066400000000000000000000174551264325443000204300ustar00rootroot00000000000000require 'spec_helper' describe Raven do let(:event) { double("event") } let(:options) { double("options") } before do allow(Raven).to receive(:send_event) allow(Raven::Event).to receive(:from_message) { event } allow(Raven::Event).to receive(:from_exception) { event } end describe '.capture_message' do let(:message) { "Test message" } it 'sends the result of Event.capture_message' do expect(Raven::Event).to receive(:from_message).with(message, options) expect(Raven).to receive(:send_event).with(event) Raven.capture_message(message, options) end it 'yields the event to a passed block' do expect { |b| Raven.capture_message(message, options, &b) }.to yield_with_args(event) end end describe '.capture_message when async' do let(:message) { "Test message" } around do |example| prior_async = Raven.configuration.async Raven.configuration.async = Proc.new { :ok } example.run Raven.configuration.async = prior_async end it 'sends the result of Event.capture_message' do expect(Raven::Event).to receive(:from_message).with(message, options) expect(Raven).not_to receive(:send_event).with(event) expect(Raven.configuration.async).to receive(:call).with(event) Raven.capture_message(message, options) end it 'returns the generated event' do returned = Raven.capture_message(message, options) expect(returned).to eq(event) end end describe '.capture_exception' do let(:exception) { build_exception } it 'sends the result of Event.capture_exception' do expect(Raven::Event).to receive(:from_exception).with(exception, options) expect(Raven).to receive(:send_event).with(event) Raven.capture_exception(exception, options) end it 'yields the event to a passed block' do expect { |b| Raven.capture_exception(exception, options, &b) }.to yield_with_args(event) end end describe '.capture_exception when async' do let(:exception) { build_exception } around do |example| prior_async = Raven.configuration.async Raven.configuration.async = Proc.new { :ok } example.run Raven.configuration.async = prior_async end it 'sends the result of Event.capture_exception' do expect(Raven::Event).to receive(:from_exception).with(exception, options) expect(Raven).not_to receive(:send_event).with(event) expect(Raven.configuration.async).to receive(:call).with(event) Raven.capture_exception(exception, options) end it 'returns the generated event' do returned = Raven.capture_exception(exception, options) expect(returned).to eq(event) end end describe '.capture_exception with a should_capture callback' do let(:exception) { build_exception } it 'sends the result of Event.capture_exception according to the result of should_capture' do expect(Raven).not_to receive(:send_event).with(event) prior_should_capture = Raven.configuration.should_capture Raven.configuration.should_capture = Proc.new { false } expect(Raven.configuration.should_capture).to receive(:call).with(exception) expect(Raven.capture_exception(exception, options)).to be false Raven.configuration.should_capture = prior_should_capture end end describe '.capture' do context 'given a block' do it 'yields to the given block' do expect { |b| described_class.capture(&b) }.to yield_with_no_args end it 'does not install an exit_hook' do expect(described_class).not_to receive(:install_at_exit_hook) described_class.capture {} end end context 'not given a block' do let(:options) { { :key => 'value' } } def capture_in_separate_process pipe_in, pipe_out = IO.pipe fork do pipe_in.close described_class.capture(options) allow(Raven).to receive(:capture_exception) do |exception, _options| pipe_out.puts exception.message end # silence process $stderr.reopen('/dev/null', 'w') $stdout.reopen('/dev/null', 'w') raise 'test error' exit end pipe_out.close captured_messages = pipe_in.read pipe_in.close # sometimes the at_exit hook was registered multiple times captured_messages.split("\n").last end it 'does not yield' do # As there is no yield matcher that does not require a probe (e.g. this # is not valid: expect { |b| described_class.capture }.to_not yield_control), # expect that a LocalJumpError, which is raised when yielding when no # block is defined, is not raised. expect { described_class.capture }.not_to raise_error end it 'installs an at exit hook that will capture exceptions' do skip('fork not supported in jruby') if RUBY_PLATFORM == 'java' captured_message = capture_in_separate_process { raise 'test error' } expect(captured_message).to eq('test error') end end end describe '.annotate_exception' do let(:exception) { build_exception } def ivars(object) object.instance_variables.map(&:to_s) end it 'adds an annotation to the exception' do expect(ivars(exception)).not_to include("@__raven_context") Raven.annotate_exception(exception, {}) expect(ivars(exception)).to include("@__raven_context") expect(exception.instance_variable_get(:@__raven_context)).to \ be_kind_of Hash end end describe '.report_status' do let(:ready_message) do "Raven #{Raven::VERSION} ready to catch errors" end let(:not_ready_message) do "Raven #{Raven::VERSION} configured not to send errors." end it 'logs a ready message when configured' do Raven.configuration.silence_ready = false expect(Raven.configuration).to( receive(:send_in_current_environment?).and_return(true)) expect(Raven.logger).to receive(:info).with(ready_message) Raven.report_status end it 'logs not ready message if the config does not send in current environment' do Raven.configuration.silence_ready = false expect(Raven.configuration).to( receive(:send_in_current_environment?).and_return(false)) expect(Raven.logger).to receive(:info).with(not_ready_message) Raven.report_status end it 'logs nothing if "silence_ready" configuration is true' do Raven.configuration.silence_ready = true expect(Raven.logger).not_to receive(:info) Raven.report_status end end describe '.inject_only' do before do allow(Gem.loaded_specs).to receive(:keys).and_return(%w[railties rack sidekiq]) end it 'loads integrations when they are valid configurations' do expect(Raven).to receive(:load_integration).once.with('railties') expect(Raven).to receive(:load_integration).once.with('sidekiq') Raven.inject_only(:railties, :sidekiq) end it 'skips any integrations that are not supported' do expect(Raven).to receive(:load_integration).once.with('railties') expect(Raven.logger).to receive(:warn).with('Integrations do not exist: doesnot, exist') Raven.inject_only(:railties, :doesnot, :exist) end it 'skips any integrations that are not loaded in the gemspec' do expect(Raven).to receive(:load_integration).once.with('railties') Raven.inject_only(:railties, :delayed_job) end end describe '.inject_without' do before do allow(Gem.loaded_specs).to receive(:keys).and_return(Raven::AVAILABLE_INTEGRATIONS) end it 'injects all integrations except those passed as an argument' do expect(Raven).to receive(:load_integration).once.with('rake') Raven.inject_without(:delayed_job, :railties, :sidekiq, :rack) end end end raven-ruby-0.15.3/spec/raven/transports/000077500000000000000000000000001264325443000201615ustar00rootroot00000000000000raven-ruby-0.15.3/spec/raven/transports/http_spec.rb000066400000000000000000000004631264325443000225020ustar00rootroot00000000000000describe Raven::Transports::HTTP do it 'should set a custom User-Agent' do Raven.configure do |config| config.server = 'http://12345:67890@sentry.localdomain/sentry/42' end expect(Raven.client.send(:transport).conn.headers[:user_agent]).to eq("sentry-ruby/#{Raven::VERSION}") end end raven-ruby-0.15.3/spec/spec_helper.rb000066400000000000000000000021011264325443000174370ustar00rootroot00000000000000require 'raven' require File.dirname(__FILE__) + "/support/test_rails_app/app.rb" require "rspec/rails" RSpec.configure do |config| config.mock_with(:rspec) { |mocks| mocks.verify_partial_doubles = true } config.raise_errors_for_deprecations! end def build_exception 1 / 0 rescue ZeroDivisionError => exception return exception end def build_exception_with_cause begin 1 / 0 rescue ZeroDivisionError 1 / 0 end rescue ZeroDivisionError => exception return exception end def build_exception_with_two_causes begin begin 1 / 0 rescue ZeroDivisionError 1 / 0 end rescue ZeroDivisionError 1 / 0 end rescue ZeroDivisionError => exception return exception end def build_exception_with_recursive_cause backtrace = double("Backtrace") allow(backtrace).to receive(:to_a).and_return([]) exception = double("Exception") allow(exception).to receive(:cause).and_return(exception) allow(exception).to receive(:message).and_return("example") allow(exception).to receive(:backtrace).and_return(backtrace) return exception end raven-ruby-0.15.3/spec/support/000077500000000000000000000000001264325443000163435ustar00rootroot00000000000000raven-ruby-0.15.3/spec/support/Rakefile000066400000000000000000000002721264325443000200110ustar00rootroot00000000000000require 'rake' require 'rubygems' require 'raven' Raven.configure do |config| config.dsn = 'http://12345:67890@sentry.localdomain/sentry/42' end task :raise_exception do 1 / 0 end raven-ruby-0.15.3/spec/support/test_rails_app/000077500000000000000000000000001264325443000213545ustar00rootroot00000000000000raven-ruby-0.15.3/spec/support/test_rails_app/app.rb000066400000000000000000000016221264325443000224620ustar00rootroot00000000000000require 'rails' # require "active_model/railtie" # require "active_job/railtie" # require "active_record/railtie" require "action_controller/railtie" # require "action_mailer/railtie" require "action_view/railtie" # require "action_cable/engine" # require "sprockets/railtie" # require "rails/test_unit/railtie" require 'raven/integrations/rails' class TestApp < Rails::Application config.secret_key_base = "test" # Usually set for us in production.rb config.eager_load = true config.cache_classes = true config.serve_static_files = false config.log_level = :error config.logger = Logger.new(STDOUT) routes.append do get "/exception", :to => "hello#exception" root :to => "hello#world" end end class HelloController < ActionController::Base def exception raise "An unhandled exception!" end def world render :text => "Hello World!" end end Rails.env = "production"