Created
September 27, 2022 17:23
-
-
Save leandro/644cc3ca3c25f727fb411c9aae3d54cb to your computer and use it in GitHub Desktop.
Sentry toggling extensiion
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'sentry_extensions/toggler' | |
require 'sentry_extensions/configuration' | |
Sentry.init do |config| | |
config.dsn = ENV['SENTRY_API_KEY'] | |
config.excluded_exceptions = config.excluded_exceptions - ['ActiveRecord::RecordNotFound'] | |
# This allows us to skip an error to be sent to Sentry, which is useful for | |
# when we're already manually sending the error to Sentry and then re-raising | |
# the error. | |
config.before_send = lambda do |event, hint| | |
e = hint[:exception] | |
can_report = e.respond_to?(:report_to_sentry?) ? e.report_to_sentry? : true | |
event if can_report | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module SentryExtensions | |
module Configuration | |
def before_send=(value) | |
super | |
original_before_send = @before_send | |
@before_send = ->(event, hint) do | |
send_to_sentry = Sentry.can_report_event?(event) | |
Sentry.set_old_toggler_data_back | |
original_before_send.call(event, hint) if send_to_sentry | |
end | |
end | |
end | |
end | |
Sentry::Configuration.prepend(SentryExtensions::Configuration) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module SentryExtensions | |
module Toggler | |
VAR_NAME = :sentry_toggler | |
private_constant :VAR_NAME | |
def self.extended(base) | |
base.class_exec do | |
class << self | |
alias original_capture_exception capture_exception | |
alias original_capture_message capture_message | |
remove_method(:capture_exception) | |
remove_method(:capture_message) | |
end | |
end | |
end | |
def disable_reporting(...) = toggle_reporting(false, ...) | |
def enable_reporting(...) = toggle_reporting(...) | |
def put_back_old_toggler_data? = !!get_thread_data&.dig(:reversible) | |
# If +can_report+ is +false+ then event won't be reported if any of the | |
# filters matches against the event, otherwise the event will be sent. | |
# Conversely, if +can_report+ is +true+ and any of the filters match the | |
# event, it'll be sent, otherwise it won't be sent. | |
def can_report_event?(event) | |
data = toggler_data | |
can_report, filters = data.values_at(:can_report, :filters) | |
return can_report if filters.blank? | |
any_filter_satisfied = | |
filters.symbolize_keys.slice(:message, :extra).any? do |key, block| | |
unless block.is_a?(Proc) | |
raise ArgumentError, "Filter :#{key} is not a Proc" | |
end | |
block.call(event.public_send(key)) | |
end | |
any_filter_satisfied ? can_report : !can_report | |
end | |
def capture_exception(...) | |
set_old_toggler_data_back_after_event_is_sent | |
original_capture_exception(...) | |
end | |
def capture_message(...) | |
set_old_toggler_data_back_after_event_is_sent | |
original_capture_message(...) | |
end | |
def can_revert_toggler_data? | |
(revert = revert_after_event_is_sent).nil? || revert | |
end | |
def revert_after_event_is_sent | |
get_thread_data&.dig(:revert_after_event_is_sent) | |
end | |
def set_old_toggler_data_back(from_after_yield = false) | |
return if from_after_yield && revert_after_event_is_sent == true | |
return if !put_back_old_toggler_data? || !can_revert_toggler_data? | |
set_toggler_data(get_thread_data&.dig(:old), true) | |
end | |
def toggler_data | |
(get_thread_data || set_toggler_data(can_report: true))&.dig(:current) | |
end | |
private | |
def get_thread_data | |
main, current = [Thread.main, Thread.current] | |
if main != current && main[VAR_NAME] != current[VAR_NAME] | |
current[VAR_NAME] = main[VAR_NAME]&.deep_dup&.freeze | |
end | |
current[VAR_NAME] | |
end | |
def set_old_toggler_data_back_after_event_is_sent | |
return unless put_back_old_toggler_data? | |
set_thread_data(get_thread_data&.merge(revert_after_event_is_sent: true)) | |
end | |
def set_reversible_data(revert) | |
set_thread_data(get_thread_data&.merge(reversible: !!revert)) | |
end | |
def set_thread_data(value) | |
threads = [Thread.main, Thread.current].uniq | |
threads.map { _1[VAR_NAME] = value.deep_dup.freeze }.last | |
end | |
def set_toggler_data(value, clear_old = false) | |
return if value.nil? | |
current = value.freeze | |
payload = get_thread_data || {} | |
old = !clear_old && payload[:current] || { can_report: true }.freeze | |
set_thread_data({ current: current, old: old }.compact) | |
end | |
def toggle_reporting(enable = true, filters = {}) | |
payload = { can_report: !!enable, filters: filters.dup.presence&.freeze } | |
set_toggler_data(payload.compact, !block_given?) | |
set_reversible_data(block_given?) | |
return unless block_given? | |
begin | |
yield.tap { set_old_toggler_data_back(true) } | |
rescue Exception | |
set_old_toggler_data_back_after_event_is_sent | |
raise | |
end | |
end | |
end | |
end | |
Sentry.extend(SentryExtensions::Toggler) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment