diff --git a/.rdoc_options b/.rdoc_options deleted file mode 100644 index 3f9238f02..000000000 --- a/.rdoc_options +++ /dev/null @@ -1,27 +0,0 @@ ---- !ruby/object:RDoc::Options -encoding: UTF-8 -static_path: [] -rdoc_include: -- "lib/**/*.rb" -- "README.md" -- "CONTRIBUTING.md" -- "CHANGELOG.md" -charset: UTF-8 -exclude: -format: hanna -hyperlink_all: false -line_numbers: false -locale: -locale_dir: locale -locale_name: -main_page: -markup: markdown -output: rdoc -output_decoration: true -page_dir: -show_hash: false -tab_width: 8 -template_stylesheets: [] -title: bugsnag-ruby API Documentation -visibility: :protected -webcvs: diff --git a/.yardopts b/.yardopts new file mode 100644 index 000000000..b1c9cc2b6 --- /dev/null +++ b/.yardopts @@ -0,0 +1,11 @@ +--charset UTF-8 +--fail-on-warning +--hide-api private +--no-private +--protected +--title "bugsnag-ruby API Documentation" +lib/**/*.rb +- +README.md +CONTRIBUTING.md +CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 3713c9523..5f8e3b490 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ Changelog ========= +## 6.17.0 (27 August 2020) + +### Enhancements + +* Sidekiq now uses `thread_queue` delivery by default + | [#626](https://github.com/bugsnag/bugsnag-ruby/pull/626) + +* Rescue now uses `thread_queue` delivery when `at_exit` hooks are enabled + | [#629](https://github.com/bugsnag/bugsnag-ruby/pull/629) + ## 6.16.0 (12 August 2020) ### Enhancements diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7a8b7dea1..9c97aaa77 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,23 +10,29 @@ Thank you! ### Getting started -- [Fork](https://help.github.com/articles/fork-a-repo) the [library on github](https://github.com/bugsnag/bugsnag-ruby) -- Commit and push until you are happy with your contribution +* [Fork](https://help.github.com/articles/fork-a-repo) the [library on github](https://github.com/bugsnag/bugsnag-ruby) +* Commit and push until you are happy with your contribution ### Polish -- Run the tests with and make sure they all pass - +* Run the tests with and make sure they all pass ``` bundle exec rake spec ``` -- Further information on installing and running the tests can be found in [the testing guide](TESTING.md) +* Further information on installing and running the tests can be found in [the testing guide](TESTING.md) +### Document -### Ship it! +* Write API docs for your contributions using [YARD](https://yardoc.org/) +* Generate the API documentation locally + ``` + bundle exec rake yard + ``` +* Review your changes by opening `doc/index.html` -- [Make a pull request](https://help.github.com/articles/using-pull-requests) +### Ship it! +* [Make a pull request](https://help.github.com/articles/using-pull-requests) ## How to release diff --git a/Gemfile b/Gemfile index da7d7bf3c..60b24790a 100644 --- a/Gemfile +++ b/Gemfile @@ -6,7 +6,7 @@ group :test, optional: true do gem 'rake', ruby_version <= Gem::Version.new('1.9.3') ? '~> 11.3.0' : '~> 12.3.0' gem 'rspec' gem 'rspec-mocks' - gem 'rdoc', ruby_version < Gem::Version.new('2.2.0') ? '4.3.0' : '~> 5.1.0' + gem 'yard', '~> 0.9.25' gem 'pry' gem 'addressable', '~> 2.3.8' if ruby_version >= Gem::Version.new('2.2.2') @@ -43,8 +43,4 @@ group :sidekiq, optional: true do gem 'rack', ruby_version < Gem::Version.new('2.3.0') ? '< 2.2.0' : '~> 2.2' end -group :doc, optional: true do - gem 'hanna-nouveau' -end - gemspec diff --git a/Rakefile b/Rakefile index 8adf83d9c..43574d24b 100644 --- a/Rakefile +++ b/Rakefile @@ -1,37 +1,38 @@ # encoding: utf-8 -require 'rdoc/task' -RDoc::Task.new do |rdoc| - rdoc.rdoc_dir = 'rdoc' - rdoc.rdoc_files.include('README.md') - rdoc.rdoc_files.include('CONTRIBUTING.md') - rdoc.rdoc_files.include('CHANGELOG.md') - rdoc.rdoc_files.include('lib/**/*.rb') - rdoc.options.push '-f', 'hanna' - rdoc.markup = 'markdown' +require 'yard' +require 'rspec/core' +require "rspec/core/rake_task" + +# Yard task (rake yard) +YARD::Rake::YardocTask.new do |task| + version = File.read("VERSION").strip + + task.options += ["--title", "bugsnag-ruby v#{version} API Documentation"] end # RSpec tasks -require 'rspec/core' -require "rspec/core/rake_task" RSpec::Core::RakeTask.new(:spec) do |task| integration_exclusions = [] + begin require 'sidekiq/testing' rescue LoadError puts "Skipping sidekiq tests, missing dependencies" integration_exclusions << 'sidekiq' end + begin require 'delayed_job' rescue LoadError puts "Skipping delayed_job tests, missing dependencies" integration_exclusions << 'delayed_job' end + if integration_exclusions.length > 0 pattern = integration_exclusions.join(',') task.rspec_opts = " --exclude-pattern **/integrations/{#{pattern}}_spec.rb" end end -task :default => :spec +task default: :spec diff --git a/VERSION b/VERSION index bfec95bf0..1980a78f2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.16.0 +6.17.0 diff --git a/bugsnag.gemspec b/bugsnag.gemspec index a66724405..e56504df4 100644 --- a/bugsnag.gemspec +++ b/bugsnag.gemspec @@ -10,7 +10,7 @@ Gem::Specification.new do |s| s.homepage = "https://github.com/bugsnag/bugsnag-ruby" s.licenses = ["MIT"] - s.files = `git ls-files -z lib bugsnag.gemspec VERSION`.split("\x0") + s.files = `git ls-files -z lib bugsnag.gemspec VERSION .yardopts`.split("\x0") s.extra_rdoc_files = [ "LICENSE.txt", "README.md", diff --git a/dockerfiles/Dockerfile.ruby-maze-runner b/dockerfiles/Dockerfile.ruby-maze-runner index a74c08cd9..3faa72e8d 100644 --- a/dockerfiles/Dockerfile.ruby-maze-runner +++ b/dockerfiles/Dockerfile.ruby-maze-runner @@ -8,7 +8,7 @@ COPY bugsnag.gemspec Gemfile VERSION ./ RUN bundle install -COPY CHANGELOG.md CONTRIBUTING.md TESTING.md LICENSE.txt Rakefile README.md UPGRADING.md .gitignore .rdoc_options .rspec .rubocop.yml .rubocop_todo.yml docker-compose.yml ./ +COPY CHANGELOG.md CONTRIBUTING.md TESTING.md LICENSE.txt Rakefile README.md UPGRADING.md .gitignore .rspec .rubocop.yml .rubocop_todo.yml .yardopts docker-compose.yml ./ COPY .buildkite ./.buildkite COPY .git ./.git COPY .github ./.github diff --git a/features/fixtures/docker-compose.yml b/features/fixtures/docker-compose.yml index 1e82ae642..631ff902f 100644 --- a/features/fixtures/docker-compose.yml +++ b/features/fixtures/docker-compose.yml @@ -258,6 +258,7 @@ services: - DB_PASSWORD=test_password - DB_HOST=postgres - REDIS_URL=redis://redis:6379 + - RUN_AT_EXIT_HOOKS restart: "no" sidekiq: @@ -275,6 +276,7 @@ services: - BUGSNAG_APP_VERSION - BUGSNAG_AUTO_CAPTURE_SESSIONS - BUGSNAG_AUTO_NOTIFY + - BUGSNAG_DELIVERY_METHOD - BUGSNAG_ENDPOINT - BUGSNAG_IGNORE_CLASS - BUGSNAG_IGNORE_MESSAGE diff --git a/features/fixtures/rails_integrations/app/config/initializers/bugsnag.rb b/features/fixtures/rails_integrations/app/config/initializers/bugsnag.rb index 456ab188e..38eeba564 100644 --- a/features/fixtures/rails_integrations/app/config/initializers/bugsnag.rb +++ b/features/fixtures/rails_integrations/app/config/initializers/bugsnag.rb @@ -2,4 +2,10 @@ config.api_key = ENV['BUGSNAG_API_KEY'] config.endpoint = ENV['BUGSNAG_ENDPOINT'] config.session_endpoint = ENV['BUGSNAG_ENDPOINT'] + + config.add_on_error(proc do |report| + report.add_tab(:config, { + delivery_method: config.delivery_method.to_s, + }) + end) end diff --git a/features/fixtures/sidekiq/app/app.rb b/features/fixtures/sidekiq/app/app.rb index 44e9b50fe..436325940 100644 --- a/features/fixtures/sidekiq/app/app.rb +++ b/features/fixtures/sidekiq/app/app.rb @@ -6,6 +6,17 @@ conf.api_key = ENV['BUGSNAG_API_KEY'] puts "Configuring `endpoint` to #{ENV['BUGSNAG_ENDPOINT']}" conf.endpoint = ENV['BUGSNAG_ENDPOINT'] + + if ENV.include?('BUGSNAG_DELIVERY_METHOD') + puts "Configuring `delivery_method` to #{ENV['BUGSNAG_DELIVERY_METHOD']}" + conf.delivery_method = ENV['BUGSNAG_DELIVERY_METHOD'].to_sym + end + + conf.add_on_error(proc do |report| + report.add_tab(:config, { + delivery_method: conf.delivery_method.to_s + }) + end) end Sidekiq.configure_client do |config| @@ -32,4 +43,4 @@ class UnhandledError def perform raise RuntimeError.new("Unhandled") end -end \ No newline at end of file +end diff --git a/features/rails_features/integrations.feature b/features/rails_features/integrations.feature index 1ef77a1dc..2a50747e9 100644 --- a/features/rails_features/integrations.feature +++ b/features/rails_features/integrations.feature @@ -70,7 +70,7 @@ Scenario: Rake And the event "metaData.rake_task.arguments" is null @rails_integrations -Scenario: Resque +Scenario: Resque (no on_exit hooks) When I run "bundle exec rake resque:work" in the rails app And I run "Resque.enqueue(ResqueWorker)" with the rails runner And I wait to receive a request @@ -82,6 +82,28 @@ Scenario: Resque And the event "severityReason.attributes.framework" equals "Resque" And the event "app.type" equals "resque" And the exception "errorClass" equals "RuntimeError" + And the event "metaData.config.delivery_method" equals "synchronous" + And the event "metaData.context" equals "ResqueWorker@crash" + And the event "metaData.payload.class" equals "ResqueWorker" + And the event "metaData.rake_task.name" equals "resque:work" + And the event "metaData.rake_task.description" equals "Start a Resque worker" + And the event "metaData.rake_task.arguments" is null + +@rails_integrations +Scenario: Resque (with on_exit hooks) + Given I set environment variable "RUN_AT_EXIT_HOOKS" to "1" + When I run "bundle exec rake resque:work" in the rails app + And I run "Resque.enqueue(ResqueWorker)" with the rails runner + And I wait to receive a request + Then the request is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" + And the event "unhandled" is true + And the event "context" equals "ResqueWorker@crash" + And the event "severity" equals "error" + And the event "severityReason.type" equals "unhandledExceptionMiddleware" + And the event "severityReason.attributes.framework" equals "Resque" + And the event "app.type" equals "resque" + And the exception "errorClass" equals "RuntimeError" + And the event "metaData.config.delivery_method" equals "thread_queue" And the event "metaData.context" equals "ResqueWorker@crash" And the event "metaData.payload.class" equals "ResqueWorker" And the event "metaData.rake_task.name" equals "resque:work" diff --git a/features/sidekiq.feature b/features/sidekiq.feature index 020abbce5..b4df3dea6 100644 --- a/features/sidekiq.feature +++ b/features/sidekiq.feature @@ -12,10 +12,11 @@ Scenario: An unhandled RuntimeError sends a report And the event "app.type" equals "sidekiq" And the exception "errorClass" equals "RuntimeError" And the "file" of stack frame 0 equals "/app/app.rb" - And the "lineNumber" of stack frame 0 equals 33 + And the "lineNumber" of stack frame 0 equals 44 And the payload field "events.0.metaData.sidekiq" matches the appropriate Sidekiq unhandled payload And the event "metaData.sidekiq.msg.created_at" is a parsable timestamp in seconds And the event "metaData.sidekiq.msg.enqueued_at" is a parsable timestamp in seconds + And the event "metaData.config.delivery_method" equals "thread_queue" Scenario: A handled RuntimeError can be notified Given I run the service "sidekiq" with the command "bundle exec rake sidekiq_tests:handled_error" @@ -30,3 +31,20 @@ Scenario: A handled RuntimeError can be notified And the payload field "events.0.metaData.sidekiq" matches the appropriate Sidekiq handled payload And the event "metaData.sidekiq.msg.created_at" is a parsable timestamp in seconds And the event "metaData.sidekiq.msg.enqueued_at" is a parsable timestamp in seconds + And the event "metaData.config.delivery_method" equals "thread_queue" + +Scenario: Synchronous delivery can be used + Given I set environment variable "BUGSNAG_DELIVERY_METHOD" to "synchronous" + And I run the service "sidekiq" with the command "bundle exec rake sidekiq_tests:handled_error" + And I wait to receive a request + Then the request is valid for the error reporting API version "4.0" for the "Ruby Bugsnag Notifier" + And the event "unhandled" is false + And the event "context" equals "HandledError@default" + And the event "severity" equals "warning" + And the event "severityReason.type" equals "handledException" + And the event "app.type" equals "sidekiq" + And the exception "errorClass" equals "RuntimeError" + And the payload field "events.0.metaData.sidekiq" matches the appropriate Sidekiq handled payload + And the event "metaData.sidekiq.msg.created_at" is a parsable timestamp in seconds + And the event "metaData.sidekiq.msg.enqueued_at" is a parsable timestamp in seconds + And the event "metaData.config.delivery_method" equals "synchronous" diff --git a/lib/bugsnag.rb b/lib/bugsnag.rb index 0a7da0bb2..96f2e99b8 100644 --- a/lib/bugsnag.rb +++ b/lib/bugsnag.rb @@ -44,7 +44,10 @@ class << self ## # Configure the Bugsnag notifier application-wide settings. # - # Yields a configuration object to use to set application settings. + # Yields a {Configuration} object to use to set application settings. + # + # @yieldparam configuration [Configuration] + # @return [void] def configure(validate_api_key=true) yield(configuration) if block_given? @@ -125,7 +128,9 @@ def notify(exception, auto_notify=false, &block) end ## - # Registers an at_exit function to automatically catch errors on exit + # Registers an at_exit function to automatically catch errors on exit. + # + # @return [void] def register_at_exit return if at_exit_handler_installed? @exit_handler_added = true @@ -142,14 +147,19 @@ def register_at_exit end ## - # Checks if an at_exit handler has been added + # Checks if an at_exit handler has been added. + # + # The {Bugsnag#configure} method will add this automatically, but it can be + # added manually using {Bugsnag#register_at_exit}. + # + # @return [Boolean] def at_exit_handler_installed? @exit_handler_added ||= false end - # Configuration getters ## # Returns the client's Configuration object, or creates one if not yet created. + # # @return [Configuration] def configuration @configuration = nil unless defined?(@configuration) @@ -158,6 +168,8 @@ def configuration ## # Returns the client's SessionTracker object, or creates one if not yet created. + # + # @return [SessionTracker] def session_tracker @session_tracker = nil unless defined?(@session_tracker) @session_tracker || LOCK.synchronize { @session_tracker ||= Bugsnag::SessionTracker.new} @@ -181,7 +193,10 @@ def before_notify_callbacks Bugsnag.configuration.request_data[:before_callbacks] ||= [] end - # Attempts to load all integrations through auto-discovery + ## + # Attempts to load all integrations through auto-discovery. + # + # @return [void] def load_integrations require "bugsnag/integrations/railtie" if defined?(Rails::Railtie) INTEGRATIONS.each do |integration| @@ -192,7 +207,11 @@ def load_integrations end end - # Load a specific integration + ## + # Load a specific integration. + # + # @param integration [Symbol] One of the integrations in {INTEGRATIONS} + # @return [void] def load_integration(integration) integration = :railtie if integration == :rails if INTEGRATIONS.include?(integration) || integration == :railtie @@ -209,6 +228,7 @@ def load_integration(integration) # @param meta_data [Hash] String, Numeric, or Boolean meta data to attach # @param type [String] the breadcrumb type, from Bugsnag::Breadcrumbs::VALID_BREADCRUMB_TYPES # @param auto [Symbol] set to :auto if the breadcrumb is automatically created + # @return [void] def leave_breadcrumb(name, meta_data={}, type=Bugsnag::Breadcrumbs::MANUAL_BREADCRUMB_TYPE, auto=:manual) breadcrumb = Bugsnag::Breadcrumbs::Breadcrumb.new(name, type, meta_data, auto) validator = Bugsnag::Breadcrumbs::Validator.new(configuration) diff --git a/lib/bugsnag/configuration.rb b/lib/bugsnag/configuration.rb index 44b196e94..8cd8f0d17 100644 --- a/lib/bugsnag/configuration.rb +++ b/lib/bugsnag/configuration.rb @@ -15,64 +15,134 @@ module Bugsnag class Configuration + # Your Integration API Key + # @return [String, nil] attr_accessor :api_key + + # The current stage of the release process, e.g. 'development', production' + # @return [String, nil] attr_accessor :release_stage + + # A list of which release stages should cause notifications to be sent + # @return [Array, nil] attr_accessor :notify_release_stages + + # Whether notifications should automatically be sent + # @return [Boolean] attr_accessor :auto_notify + + # @return [String, nil] attr_accessor :ca_file + + # Whether to automatically attach the Rack environment to notifications + # @return [Boolean] attr_accessor :send_environment + + # Whether code snippets from the exception stacktrace should be sent with notifications + # @return [Boolean] attr_accessor :send_code + + # Any stacktrace lines that match this path will be marked as 'in project' + # @return [String, nil] attr_accessor :project_root + + # The current version of your application + # @return [String, nil] attr_accessor :app_version + + # A list of keys that should be filtered out from the report and breadcrumb + # metadata before sending them to Bugsnag + # @return [Set] attr_accessor :meta_data_filters + + # The logger to use for Bugsnag log messages + # @return [Logger] attr_accessor :logger + + # The middleware stack that will run on every notification + # @return [MiddlewareStack] attr_accessor :middleware + + # @api private + # @return [MiddlewareStack] attr_accessor :internal_middleware + + # The host address of the HTTP proxy that should be used when making requests + # @see parse_proxy + # @return [String, nil] attr_accessor :proxy_host + + # The port number of the HTTP proxy that should be used when making requests + # @see parse_proxy + # @return [Integer, nil] attr_accessor :proxy_port + + # The user that should be used when making requests via a HTTP proxy + # @see parse_proxy + # @return [String, nil] attr_accessor :proxy_user + + # The password for the user that should be used when making requests via a HTTP proxy + # @see parse_proxy + # @return [String, nil] attr_accessor :proxy_password + + # The HTTP request timeout, defaults to 15 seconds + # @return [Integer] attr_accessor :timeout + + # The name or descriptor of the Ruby server host + # @return [String] attr_accessor :hostname + + # @api private + # @return [Hash{String => String}] attr_accessor :runtime_versions + + # Exception classes that will be discarded and not sent to Bugsnag + # @return [Set] attr_accessor :discard_classes + + # Whether Bugsnag should automatically record sessions + # @return [Boolean] attr_accessor :auto_capture_sessions - ## # @deprecated Use {#discard_classes} instead + # @return [Set] attr_accessor :ignore_classes - ## - # @return [String] URL error notifications will be delivered to + # The URL error notifications will be delivered to + # @return [String] attr_reader :notify_endpoint alias :endpoint :notify_endpoint - ## - # @return [String] URL session notifications will be delivered to + # The URL session notifications will be delivered to + # @return [String] attr_reader :session_endpoint - ## - # @return [Boolean] whether any sessions types will be delivered + # Whether sessions will be delivered + # @return [Boolean] attr_reader :enable_sessions - ## - # @return [Array] strings indicating allowable automatic breadcrumb types + # A list of strings indicating allowable automatic breadcrumb types + # @see Bugsnag::Breadcrumbs::VALID_BREADCRUMB_TYPES + # @return [Array] attr_accessor :enabled_automatic_breadcrumb_types - ## - # @return [Array<#call>] callables to be run before a breadcrumb is logged + # Callables to be run before a breadcrumb is logged + # @return [Array<#call>] attr_accessor :before_breadcrumb_callbacks - ## - # @return [Integer] the maximum allowable amount of breadcrumbs per thread + # The maximum allowable amount of breadcrumbs per thread + # @return [Integer] attr_reader :max_breadcrumbs - ## - # @return [Regexp] matching file paths out of project + # + # @return [Regexp] attr_accessor :vendor_path - ## - # @return [Array] + # @api private + # @return [Array] attr_reader :scopes_to_filter API_KEY_REGEX = /[0-9a-f]{32}/i @@ -96,6 +166,7 @@ class Configuration # Path to vendored code. Used to mark file paths as out of project. DEFAULT_VENDOR_PATH = %r{^(vendor/|\.bundle/)} + # @api private DEFAULT_SCOPES_TO_FILTER = ['events.metaData', 'events.breadcrumbs.metaData'].freeze alias :track_sessions :auto_capture_sessions @@ -177,6 +248,7 @@ def initialize # Gets the delivery_method that Bugsnag will use to communicate with the # notification endpoint. # + # @return [Symbol] def delivery_method @delivery_method || @default_delivery_method || :thread_queue end @@ -185,6 +257,10 @@ def delivery_method # Sets the delivery_method that Bugsnag will use to communicate with the # notification endpoint. # + # The default delivery methods are ':thread_queue' and ':synchronous'. + # + # @param delivery_method [Symbol] + # @return [void] def delivery_method=(delivery_method) @delivery_method = delivery_method end @@ -193,6 +269,10 @@ def delivery_method=(delivery_method) # Used to set a new default delivery method that will be used if one is not # set with #delivery_method. # + # @api private + # + # @param delivery_method [Symbol] + # @return [void] def default_delivery_method=(delivery_method) @default_delivery_method = delivery_method end @@ -248,12 +328,16 @@ def detected_app_type=(app_type) ## # Indicates whether the notifier should send a notification based on the # configured release stage. + # + # @return [Boolean] def should_notify_release_stage? @release_stage.nil? || @notify_release_stages.nil? || @notify_release_stages.include?(@release_stage) end ## # Tests whether the configured API key is valid. + # + # @return [Boolean] def valid_api_key? !api_key.nil? && api_key =~ API_KEY_REGEX end @@ -261,48 +345,68 @@ def valid_api_key? ## # Returns the array of data that will be automatically attached to every # error notification. + # + # @return [Hash] def request_data Thread.current[THREAD_LOCAL_NAME] ||= {} end ## # Sets an entry in the array of data attached to every error notification. + # + # @param key [String, #to_s] + # @param value [Object] + # @return [void] def set_request_data(key, value) self.request_data[key] = value end ## # Unsets an entry in the array of data attached to every error notification. + # + # @param (see set_request_data) + # @return [void] def unset_request_data(key, value) self.request_data.delete(key) end ## # Clears the array of data attached to every error notification. + # + # @return [void] def clear_request_data Thread.current[THREAD_LOCAL_NAME] = nil end ## # Logs an info level message + # + # @param message [String, #to_s] The message to log def info(message) logger.info(PROG_NAME) { message } end ## # Logs a warning level message + # + # @param (see info) def warn(message) logger.warn(PROG_NAME) { message } end ## # Logs a debug level message + # + # @param (see info) def debug(message) logger.debug(PROG_NAME) { message } end ## # Parses and sets proxy from a uri + # + # @param uri [String, #to_s] The URI to parse and extract proxy details from + # @return [void] def parse_proxy(uri) proxy = URI.parse(uri) self.proxy_host = proxy.host @@ -314,7 +418,8 @@ def parse_proxy(uri) ## # Sets the maximum allowable amount of breadcrumbs # - # @param [Integer] the new maximum breadcrumb limit + # @param new_max_breadcrumbs [Integer] the new maximum breadcrumb limit + # @return [void] def max_breadcrumbs=(new_max_breadcrumbs) @max_breadcrumbs = new_max_breadcrumbs breadcrumbs.max_items = new_max_breadcrumbs @@ -330,9 +435,10 @@ def breadcrumbs # Sets the notification endpoint # - # @param new_notify_endpoint [String] The URL to deliver error notifications to - # # @deprecated Use {#set_endpoints} instead + # + # @param new_notify_endpoint [String] The URL to deliver error notifications to + # @return [void] def endpoint=(new_notify_endpoint) warn("The 'endpoint' configuration option is deprecated. The 'set_endpoints' method should be used instead") set_endpoints(new_notify_endpoint, session_endpoint) # Pass the existing session_endpoint through so it doesn't get overwritten @@ -341,9 +447,10 @@ def endpoint=(new_notify_endpoint) ## # Sets the sessions endpoint # - # @param new_session_endpoint [String] The URL to deliver session notifications to - # # @deprecated Use {#set_endpoints} instead + # + # @param new_session_endpoint [String] The URL to deliver session notifications to + # @return [void] def session_endpoint=(new_session_endpoint) warn("The 'session_endpoint' configuration option is deprecated. The 'set_endpoints' method should be used instead") set_endpoints(notify_endpoint, new_session_endpoint) # Pass the existing notify_endpoint through so it doesn't get overwritten @@ -354,13 +461,16 @@ def session_endpoint=(new_session_endpoint) # # @param new_notify_endpoint [String] The URL to deliver error notifications to # @param new_session_endpoint [String] The URL to deliver session notifications to + # @return [void] def set_endpoints(new_notify_endpoint, new_session_endpoint) @notify_endpoint = new_notify_endpoint @session_endpoint = new_session_endpoint end ## - # Disables session tracking and delivery. Cannot be undone + # Disables session tracking and delivery. Cannot be undone + # + # @return [void] def disable_sessions self.auto_capture_sessions = false @enable_sessions = false diff --git a/lib/bugsnag/integrations/resque.rb b/lib/bugsnag/integrations/resque.rb index 8ce9b754d..e2074143d 100644 --- a/lib/bugsnag/integrations/resque.rb +++ b/lib/bugsnag/integrations/resque.rb @@ -58,16 +58,23 @@ def save # Auto-load the failure backend Bugsnag::Resque.add_failure_backend -if Resque::Worker.new(:bugsnag_fork_check).fork_per_job? +worker = Resque::Worker.new(:bugsnag_fork_check) + +# If at_exit hooks are not enabled then we can't use the thread queue delivery +# method because it relies on an at_exit hook to ensure the queue is flushed +can_use_thread_queue = worker.respond_to?(:run_at_exit_hooks) && worker.run_at_exit_hooks +default_delivery_method = can_use_thread_queue ? :thread_queue : :synchronous + +if worker.fork_per_job? Resque.after_fork do Bugsnag.configuration.detected_app_type = "resque" - Bugsnag.configuration.default_delivery_method = :synchronous + Bugsnag.configuration.default_delivery_method = default_delivery_method Bugsnag.configuration.runtime_versions["resque"] = ::Resque::VERSION end else Resque.before_first_fork do Bugsnag.configuration.detected_app_type = "resque" - Bugsnag.configuration.default_delivery_method = :synchronous + Bugsnag.configuration.default_delivery_method = default_delivery_method Bugsnag.configuration.runtime_versions["resque"] = ::Resque::VERSION end end diff --git a/lib/bugsnag/integrations/sidekiq.rb b/lib/bugsnag/integrations/sidekiq.rb index b2590e5b1..3fcb4c2bc 100644 --- a/lib/bugsnag/integrations/sidekiq.rb +++ b/lib/bugsnag/integrations/sidekiq.rb @@ -14,7 +14,6 @@ class Sidekiq def initialize Bugsnag.configuration.internal_middleware.use(Bugsnag::Middleware::Sidekiq) Bugsnag.configuration.detected_app_type = "sidekiq" - Bugsnag.configuration.default_delivery_method = :synchronous Bugsnag.configuration.runtime_versions["sidekiq"] = ::Sidekiq::VERSION end diff --git a/lib/bugsnag/report.rb b/lib/bugsnag/report.rb index b42fed8d2..6e6478ba9 100644 --- a/lib/bugsnag/report.rb +++ b/lib/bugsnag/report.rb @@ -19,24 +19,87 @@ class Report CURRENT_PAYLOAD_VERSION = "4.0" - attr_reader :unhandled + # Whether this report is for a handled or unhandled error + # @return [Boolean] + attr_reader :unhandled + + # Your Integration API Key + # @see Configuration#api_key + # @return [String, nil] attr_accessor :api_key + + # The type of application executing the current code + # @see Configuration#app_type + # @return [String, nil] attr_accessor :app_type + + # The current version of your application + # @return [String, nil] attr_accessor :app_version + + # The list of breadcrumbs attached to this report + # @return [Array] attr_accessor :breadcrumbs + + # @api private + # @return [Configuration] attr_accessor :configuration + + # Additional context for this report + # @return [String, nil] attr_accessor :context + + # The delivery method that will be used for this report + # @see Configuration#delivery_method + # @return [Symbol] attr_accessor :delivery_method + + # The list of exceptions in this report + # @return [Array] attr_accessor :exceptions + + # @see Configuration#hostname + # @return [String] attr_accessor :hostname + + # @api private + # @see Configuration#runtime_versions + # @return [Hash{String => String}] attr_accessor :runtime_versions + + # All errors with the same grouping hash will be grouped in the Bugsnag app + # @return [String] attr_accessor :grouping_hash + + # Arbitrary metadata attached to this report + # @return [Hash] attr_accessor :meta_data + + # The raw Exception instances for this report + # @see #exceptions + # @return [Array] attr_accessor :raw_exceptions + + # The current stage of the release process, e.g. 'development', production' + # @see Configuration#release_stage + # @return [String, nil] attr_accessor :release_stage + + # The session that active when this report was generated + # @see SessionTracker#get_current_session + # @return [Hash] attr_accessor :session + + # The severity of this report, e.g. 'error', 'warning' + # @return [String] attr_accessor :severity + + # @api private + # @return [Hash] attr_accessor :severity_reason + + # The current user when this report was generated + # @return [Hash] attr_accessor :user ## @@ -66,6 +129,12 @@ def initialize(exception, passed_configuration, auto_notify=false) ## # Add a new metadata tab to this notification. + # + # @param name [String, #to_s] The name of the tab to add + # @param value [Hash, Object] The value to add to the tab. If the tab already + # exists, this will be merged with the existing values. If a Hash is not + # given, the value will be placed into the 'custom' tab + # @return [void] def add_tab(name, value) return if name.nil? @@ -81,6 +150,9 @@ def add_tab(name, value) ## # Removes a metadata tab from this notification. + # + # @param name [String] + # @return [void] def remove_tab(name) return if name.nil? @@ -89,6 +161,8 @@ def remove_tab(name) ## # Builds and returns the exception payload for this notification. + # + # @return [Hash] def as_json # Build the payload's exception event payload_event = { @@ -129,6 +203,8 @@ def as_json ## # Returns the headers required for the notification. + # + # @return [Hash{String => String}] def headers { "Bugsnag-Api-Key" => api_key, @@ -139,18 +215,24 @@ def headers ## # Whether this report should be ignored and not sent. + # + # @return [Boolean] def ignore? @should_ignore end ## # Data set on the configuration to be attached to every error notification. + # + # @return [Hash] def request_data configuration.request_data end ## # Tells the client this report should not be sent. + # + # @return [void] def ignore! @should_ignore = true end