Class: Amigo::RateLimitedErrorHandler

Inherits:
Object
  • Object
show all
Defined in:
lib/amigo/rate_limited_error_handler.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(wrapped, sample_rate: 0.1, ttl: 120) ⇒ RateLimitedErrorHandler

Returns a new instance of RateLimitedErrorHandler.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/amigo/rate_limited_error_handler.rb', line 60

def initialize(wrapped, sample_rate: 0.1, ttl: 120)
  @mutex = Mutex.new
  @wrapped = wrapped
  @sample_rate = sample_rate
  @inverse_sample_rate = 1 - @sample_rate
  @ttl = ttl
  # Key is fingerprint, value is when to expire
  @store = {}
  # Add some fast-paths to handle 0 and 1 sample rates.
  @call = if sample_rate == 1
            ->(*a) { @wrapped.call(*a) }
  elsif sample_rate.zero?
    self.method(:call_zero)
  else
    self.method(:call_sampled)
  end
end

Instance Attribute Details

#sample_rateObject (readonly)

After the first error with a fingerprint is seen, how many future errors with the same fingerprint should we sample, until the fingerprint expires ttl after the first error? Use 1 to called the wrapped handler on all errors with the same fingerprint, and 0 to never call the wrapped handler on those errors until ttl has elapsed.



46
47
48
# File 'lib/amigo/rate_limited_error_handler.rb', line 46

def sample_rate
  @sample_rate
end

#ttlObject (readonly)

How long does the fingerprint live for an error? For example, with a sample rate of 0 and a ttl of 2 minutes, the rate will be at most one of the same error every 2 minutes; the error is always sent when the key is set; then no events are sent until the key expires.

Note that, unlike Redis TTL, the ttl is set only when the error is first seen (and then after it’s seen once the fingerprint expires); this means that, if an error is seen once a minute, with a TTL of 2 minutes, even with a sample rate of 0, an error is recorded every 2 minutes, rather than just once and never again.



58
59
60
# File 'lib/amigo/rate_limited_error_handler.rb', line 58

def ttl
  @ttl
end

#wrappedObject (readonly)

The error handler that will be called to report the error.



39
40
41
# File 'lib/amigo/rate_limited_error_handler.rb', line 39

def wrapped
  @wrapped
end

Instance Method Details

#call(ex, context) ⇒ Object



78
79
80
# File 'lib/amigo/rate_limited_error_handler.rb', line 78

def call(ex, context)
  @call[ex, context]
end

#fingerprint(ex) ⇒ Object

Fingerprint an exception.

  • No two exceptions with the same class can be the same.

  • If an exception has no backtrace (it was manually constructed), the identity of the exception instance (object_id) is the fingerprint.

  • If an exception has a backtrace, the md5 of the backtrace is the fingerprint.



111
112
113
114
115
116
117
118
119
120
121
# File 'lib/amigo/rate_limited_error_handler.rb', line 111

def fingerprint(ex)
  md5 = Digest::MD5.new
  md5.update ex.class.to_s
  if ex.backtrace.nil?
    md5.update ex.object_id.to_s
  else
    ex.backtrace.each { |line| md5.update(line) }
  end
  md5.update(self.fingerprint(ex.cause)) if ex.cause
  return md5.hexdigest
end