safely_block-0.5.0/0000755000004100000410000000000015021613154014177 5ustar www-datawww-datasafely_block-0.5.0/lib/0000755000004100000410000000000015021613154014745 5ustar www-datawww-datasafely_block-0.5.0/lib/safely_block.rb0000644000004100000410000000014415021613154017726 0ustar www-datawww-datarequire_relative "safely/core" Object.include Safely::Methods Object.send :private, :safely, :yolo safely_block-0.5.0/lib/safely/0000755000004100000410000000000015021613154016230 5ustar www-datawww-datasafely_block-0.5.0/lib/safely/services.rb0000644000004100000410000000304015021613154020375 0ustar www-datawww-datamodule Safely DEFAULT_EXCEPTION_METHOD = proc do |e, info| begin Airbrake.notify(e, info) if defined?(Airbrake) if defined?(Appsignal) if Appsignal::VERSION.to_i >= 3 Appsignal.send_error(e) do |transaction| transaction.set_tags(info) end else Appsignal.send_error(e, info) end end if defined?(Bugsnag) Bugsnag.notify(e) do |report| report.add_tab(:info, info) if info.any? end end if defined?(Datadog::Tracing) Datadog::Tracing.active_span&.set_tags(info) Datadog::Tracing.active_span&.set_error(e) end ExceptionNotifier.notify_exception(e, data: info) if defined?(ExceptionNotifier) # TODO add info Google::Cloud::ErrorReporting.report(e) if defined?(Google::Cloud::ErrorReporting) Honeybadger.notify(e, context: info) if defined?(Honeybadger) NewRelic::Agent.notice_error(e, custom_params: info) if defined?(NewRelic::Agent) Raven.capture_exception(e, extra: info) if defined?(Raven) Raygun.track_exception(e, custom_data: info) if defined?(Raygun) Rollbar.error(e, info) if defined?(Rollbar) if defined?(ScoutApm::Error) # no way to add context for a single call # ScoutApm::Context.add(info) ScoutApm::Error.capture(e) end Sentry.capture_exception(e, extra: info) if defined?(Sentry) rescue => e $stderr.puts "[safely] Error reporting exception: #{e.class.name}: #{e.message}" end end end safely_block-0.5.0/lib/safely/core.rb0000644000004100000410000000413215021613154017505 0ustar www-datawww-data# stdlib require "digest" # modules require_relative "services" require_relative "version" module Safely class << self attr_accessor :raise_envs, :tag, :report_exception_method, :throttle_counter attr_writer :env def report_exception(e, tag: nil, context: {}) tag = Safely.tag if tag.nil? if tag && e.message e = e.dup # leave original exception unmodified message = e.message e.define_singleton_method(:message) do "[#{tag == true ? "safely" : tag}] #{message}" end end if report_exception_method.arity == 1 report_exception_method.call(e) else report_exception_method.call(e, context) end end def env @env ||= ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development" end def throttled?(e, options) return false unless options key = "#{options[:key] || Digest::MD5.hexdigest([e.class.name, e.message, e.backtrace.join("\n")].join("/"))}/#{(Time.now.to_i / options[:period]) * options[:period]}" throttle_counter.clear if throttle_counter.size > 1000 # prevent from growing indefinitely (throttle_counter[key] += 1) > options[:limit] end end self.tag = true self.report_exception_method = DEFAULT_EXCEPTION_METHOD self.raise_envs = %w(development test) # not thread-safe, but we don't need to be exact self.throttle_counter = Hash.new(0) module Methods def safely(tag: nil, sample: nil, except: nil, only: nil, silence: nil, throttle: false, default: nil, context: {}) yield rescue *Array(only || StandardError) => e raise e if Array(except).any? { |c| e.is_a?(c) } raise e if Safely.raise_envs.include?(Safely.env) if sample ? rand < 1.0 / sample : true begin unless Array(silence).any? { |c| e.is_a?(c) } || Safely.throttled?(e, throttle) Safely.report_exception(e, tag: tag, context: context) end rescue => e2 $stderr.puts "FAIL-SAFE #{e2.class.name}: #{e2.message}" end end default end alias_method :yolo, :safely end extend Methods end safely_block-0.5.0/lib/safely/version.rb0000644000004100000410000000004615021613154020242 0ustar www-datawww-datamodule Safely VERSION = "0.5.0" end safely_block-0.5.0/LICENSE.txt0000644000004100000410000000206115021613154016021 0ustar www-datawww-dataCopyright (c) 2014-2025 Andrew Kane MIT License 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. safely_block-0.5.0/safely_block.gemspec0000644000004100000410000000202215021613154020175 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: safely_block 0.5.0 ruby lib Gem::Specification.new do |s| s.name = "safely_block".freeze s.version = "0.5.0" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Andrew Kane".freeze] s.date = "1980-01-02" s.email = "andrew@ankane.org".freeze s.files = ["CHANGELOG.md".freeze, "LICENSE.txt".freeze, "README.md".freeze, "lib/safely/core.rb".freeze, "lib/safely/services.rb".freeze, "lib/safely/version.rb".freeze, "lib/safely_block.rb".freeze] s.homepage = "https://github.com/ankane/safely".freeze s.licenses = ["MIT".freeze] s.required_ruby_version = Gem::Requirement.new(">= 3.2".freeze) s.rubygems_version = "3.3.15".freeze s.summary = "Rescue and report exceptions in non-critical code".freeze end safely_block-0.5.0/README.md0000644000004100000410000000764215021613154015467 0ustar www-datawww-data# Safely ```ruby safely do # keep going if this code fails end ``` Exceptions are rescued and automatically reported to your favorite reporting service. In development and test environments, exceptions are raised so you can fix them. [Read more](https://ankane.org/safely-pattern) [![Build Status](https://github.com/ankane/safely/actions/workflows/build.yml/badge.svg)](https://github.com/ankane/safely/actions) ## Installation Add this line to your application’s Gemfile: ```ruby gem "safely_block" ``` ## Use It Everywhere “Oh no, analytics brought down search” ```ruby safely { track_search(params) } ``` “Recommendations stopped updating because of one bad user” ```ruby users.each do |user| safely(context: {user_id: user.id}) { update_recommendations(user) } end ``` Also aliased as `yolo` ## Features Pass extra context to be reported with exceptions ```ruby safely context: {user_id: 123} do # code end ``` Specify a default value to return on exceptions ```ruby show_banner = safely(default: true) { show_banner_logic } ``` Raise specific exceptions ```ruby safely except: ActiveRecord::RecordNotUnique do # all other exceptions will be rescued end ``` Pass an array for multiple exception classes. Rescue only specific exceptions ```ruby safely only: ActiveRecord::RecordNotUnique do # all other exceptions will be raised end ``` Silence exceptions ```ruby safely silence: ActiveRecord::RecordNotUnique do # code end ``` Throttle reporting with: ```ruby safely throttle: {limit: 10, period: 1.minute} do # reports only first 10 exceptions each minute end ``` **Note:** The throttle limit is approximate and per process. ## Reporting Reports exceptions to a variety of services out of the box. - [Airbrake](https://airbrake.io/) - [Appsignal](https://appsignal.com/) - [Bugsnag](https://bugsnag.com/) - [Datadog](https://www.datadoghq.com/product/error-tracking/) - [Exception Notification](https://github.com/kmcphillips/exception_notification) - [Google Stackdriver](https://cloud.google.com/stackdriver/) - [Honeybadger](https://www.honeybadger.io/) - [New Relic](https://newrelic.com/) - [Raygun](https://raygun.io/) - [Rollbar](https://rollbar.com/) - [Scout APM](https://scoutapm.com/) - [Sentry](https://getsentry.com/) **Note:** Context is not supported with Google Stackdriver and Scout APM Customize reporting with: ```ruby Safely.report_exception_method = ->(e) { Rollbar.error(e) } ``` With Rails, you can add this in an initializer. By default, exception messages are prefixed with `[safely]`. This makes it easier to spot rescued exceptions. Turn this off with: ```ruby Safely.tag = false ``` By default, exceptions are raised in the development and test environments. Change this with: ```ruby Safely.raise_envs += ["staging"] ``` To report exceptions manually: ```ruby Safely.report_exception(e) ``` ## Data Protection To protect the privacy of your users, do not send [personal data](https://en.wikipedia.org/wiki/Personally_identifiable_information) to exception services. Filter sensitive form fields, use ids (not email addresses) to identify users, and mask IP addresses. With Rollbar, you can do: ```ruby Rollbar.configure do |config| config.person_id_method = "id" # default config.scrub_fields |= [:birthday] config.anonymize_user_ip = true end ``` While working on exceptions, be on the lookout for personal data and correct as needed. ## History View the [changelog](https://github.com/ankane/safely/blob/master/CHANGELOG.md) ## Contributing Everyone is encouraged to help improve this project. Here are a few ways you can help: - [Report bugs](https://github.com/ankane/safely/issues) - Fix bugs and [submit pull requests](https://github.com/ankane/safely/pulls) - Write, clarify, or fix documentation - Suggest or add new features To get started with development and testing: ```sh git clone https://github.com/ankane/safely.git cd safely bundle install bundle exec rake test ``` safely_block-0.5.0/CHANGELOG.md0000644000004100000410000000204415021613154016010 0ustar www-datawww-data## 0.5.0 (2025-05-26) - Dropped support for Ruby < 3.2 ## 0.4.1 (2024-09-04) - Added support for Datadog ## 0.4.0 (2023-05-07) - Added exception reporting from [Errbase](https://github.com/ankane/errbase) - Dropped support for Ruby < 3 ## 0.3.0 (2019-10-28) - Made `safely` method private to behave like `Kernel` methods ## 0.2.2 (2019-08-06) - Added `context` option ## 0.2.1 (2018-02-25) - Tag exceptions reported with `report_exception` ## 0.2.0 (2017-02-21) - Added `tag` option to `safely` method - Switched to keyword arguments - Fixed frozen string error - Fixed tagging with custom error handler ## 0.1.1 (2016-05-14) - Added `Safely.safely` to not pollute when included in gems - Added `throttle` option ## 0.1.0 (2015-03-15) - Added `tag` option and tag exception message by default - Added `except` option - Added `silence` option ## 0.0.4 (2015-03-11) - Added fail-safe ## 0.0.3 (2014-08-13) - Added `safely` method ## 0.0.2 (2014-08-12) - Added `default` option - Added `only` option ## 0.0.1 (2014-08-12) - First release