global_error_handler 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1d058f4e5d9c2556661eeba4c09ae42089bb29c2
4
- data.tar.gz: 0c91959443b2c90d8ac7368d16555477116f2813
3
+ metadata.gz: 8eb8957dd3db4d34bc8e895b41b35c9154d16881
4
+ data.tar.gz: e8fb838a0a0a55c7dc6f4d4595ec0c96657caceb
5
5
  SHA512:
6
- metadata.gz: 9904aac4b619724df9f9322c88d362bd9de3a9ecdb2c82945fec8f8d573fc19e3497ae798ebb61b33505067cfc9c1e26f541d8969fcc5031db97df4c671f5bf0
7
- data.tar.gz: 30f4ecd49fab72f0f2a2e70a15c3eb2c37e64e0e7269e9fdf148d037d4f78e2301e462e667324a25447cc92e058a849abe756a9cd96add3cadbb36bd0d0f66e1
6
+ metadata.gz: 279b12664a457a4adafa4b7225c24a8510b0fd860f1fe0a5b27ed70bac8f3455ce00359cea63c5a95a10fbff5ee23633fbb5da59d6825b8034dc16c4e593b3d4
7
+ data.tar.gz: 3abf0f128ae798551e589f9ec331de145a3c4cd5086dfe10d4be71f43505d0fc34d62aa231d52d646ad619251ddc37b75681f961f82dd1b8ebbfbeb00d67ad00
data/README.md CHANGED
@@ -55,6 +55,14 @@ If `rescue_from` is used in your application, add following line at top of the m
55
55
  GlobalErrorHandler::Handler.new(request.env, exception).process_exception!
56
56
  ```
57
57
 
58
+ ### Subscribe to expired key notifications
59
+ Following command should run on your server in case to automatically clear filters on deleting keys from redis due to expiration.
60
+ Default expiration time is set to 4 weeks (`GlobalErrorHandler::Redis::REDIS_TTL`)
61
+
62
+ ```ruby
63
+ GlobalErrorHandler::RedisNotificationSubscriber.subscribe!
64
+ ```
65
+
58
66
  ## Data structure
59
67
  Redis database data structure contains following below keys:
60
68
  - 'global_error_handler:current_id' : *STRING* - stores current id of an exception. It is being incremented on adding new exception into database
@@ -19,4 +19,6 @@ require 'global_error_handler/rails' if defined? Rails::Railtie
19
19
 
20
20
  require "global_error_handler/version"
21
21
 
22
+ require 'global_error_handler/redis_notification_subscriber'
23
+
22
24
  Resque::Server.register GlobalErrorHandler::Server
@@ -1,11 +1,11 @@
1
1
  class GlobalErrorHandler::AppException
2
2
  class << self
3
- def all(page, field = nil, filter = nil)
4
- page ||= 0
3
+ def all(start, field = nil, filter = nil)
4
+ start ||= 0
5
5
  if field && filter
6
- keys = GlobalErrorHandler::Redis.filter_exception_keys page, "error_#{field}", filter
6
+ keys = GlobalErrorHandler::Redis.filter_exception_keys start, "error_#{field}", filter
7
7
  else
8
- keys = GlobalErrorHandler::Redis.exception_keys page
8
+ keys = GlobalErrorHandler::Redis.exception_keys start
9
9
  end
10
10
  GlobalErrorHandler::Redis.find_all keys
11
11
  end
@@ -57,8 +57,8 @@ class GlobalErrorHandler::AppException
57
57
  end
58
58
  end
59
59
 
60
- def filtered_ids_by(field, str, len=1000, page=0)
61
- keys = GlobalErrorHandler::Redis.filter_exception_keys page, "error_#{field}", str, len
60
+ def filtered_ids_by(field, str, len=1000, start=0)
61
+ keys = GlobalErrorHandler::Redis.filter_exception_keys start, "error_#{field}", str, len
62
62
  return [] if keys.blank?
63
63
  keys.map{ |key| key.split(':').last rescue nil }.compact
64
64
  end
@@ -3,15 +3,19 @@ class GlobalErrorHandler::Redis
3
3
  EXCEPTIONS_REDIS_KEY = 'global_error_handler:exceptions'
4
4
  EXCEPTION_KEY_PREFIX = 'global_error_handler:exception'
5
5
  FILTER_KEY_PREFIX = 'global_error_handler:filter'
6
+ FILTER_FIELDS = %w(error_class error_message)
7
+ FILTER_MAX_CHARS = 60
8
+ REDIS_TTL = 4 * 7 * 24 * 60 * 60 # 4 weeks
6
9
 
7
10
  class << self
8
11
  def store(info_hash)
9
12
  redis_key = exception_key(next_id!)
10
13
  redis.hmset redis_key, info_hash.merge(id: current_id).to_a.flatten
11
14
  redis.rpush EXCEPTIONS_REDIS_KEY, redis_key
12
- %w(error_class error_message).each do |field|
15
+ FILTER_FIELDS.each do |field|
13
16
  redis.rpush filter_key(field, build_filter_value(info_hash[field.to_sym])), redis_key
14
17
  end
18
+ redis.expire redis_key, REDIS_TTL
15
19
  end
16
20
 
17
21
  def redis
@@ -40,16 +44,16 @@ class GlobalErrorHandler::Redis
40
44
  redis.llen filter_key(field, filter)
41
45
  end
42
46
 
43
- def exception_keys(page = 0, per_page = 10)
44
- redis.lrange EXCEPTIONS_REDIS_KEY, page.to_i, per_page.to_i + page.to_i - 1
47
+ def exception_keys(start = 0, per_page = 10)
48
+ redis.lrange EXCEPTIONS_REDIS_KEY, start.to_i, per_page.to_i + start.to_i - 1
45
49
  end
46
50
 
47
- def filter_exception_keys(page = 0, field = nil, filter = nil, per_page = 10)
48
- redis.lrange filter_key(field, filter), page.to_i, per_page.to_i + page.to_i - 1
51
+ def filter_exception_keys(start = 0, field = nil, filter = nil, per_page = 10)
52
+ redis.lrange filter_key(field, filter), start.to_i, per_page.to_i + start.to_i - 1
49
53
  end
50
54
 
51
- def filter_keys_for(field)
52
- redis.keys filter_key(field, '*')
55
+ def filter_keys_for(field, filter = '')
56
+ redis.keys filter_key(field, "#{filter}*")
53
57
  end
54
58
 
55
59
  def find(key)
@@ -57,12 +61,11 @@ class GlobalErrorHandler::Redis
57
61
  end
58
62
 
59
63
  def find_all(keys)
60
- keys.map { |key| find(key) }
64
+ keys.map { |key| find(key) }.compact
61
65
  end
62
66
 
63
67
  def delete(key)
64
- redis.lrem EXCEPTIONS_REDIS_KEY, 1, key
65
- clear_filters key
68
+ delete_dependencies key
66
69
  redis.del key
67
70
  end
68
71
 
@@ -82,24 +85,31 @@ class GlobalErrorHandler::Redis
82
85
  "#{FILTER_KEY_PREFIX}:#{field}:#{filter}"
83
86
  end
84
87
 
85
- protected
86
-
87
- def next_id!
88
- redis.incr(CURRENT_ID_KEY)
88
+ def delete_dependencies(key)
89
+ redis.lrem EXCEPTIONS_REDIS_KEY, 1, key
90
+ clear_filters key
89
91
  end
90
92
 
91
93
  def clear_filters(key)
92
- %w(error_class error_message).each do |field|
93
- filter_keys_for(field).each do |filter_key|
94
+ FILTER_FIELDS.each do |field|
95
+ field_value = build_filter_value(redis.hget key, field)
96
+ filter_keys_for(field, field_value).each do |filter_key|
94
97
  redis.lrem filter_key, 1, key
95
98
  end
96
99
  end
97
100
  end
98
101
 
102
+ protected
103
+
104
+ def next_id!
105
+ redis.incr(CURRENT_ID_KEY)
106
+ end
107
+
99
108
  private
100
109
 
101
110
  def build_filter_value(txt)
102
- txt.split("\n").first rescue ''
111
+ str = txt.split("\n").first rescue ''
112
+ str[0...FILTER_MAX_CHARS]
103
113
  end
104
114
  end
105
115
  end
@@ -0,0 +1,66 @@
1
+ require 'global_error_handler/redis'
2
+
3
+ class GlobalErrorHandler::RedisNotificationSubscriber
4
+ class << self
5
+ def unsubscribe!
6
+ redis.unsubscribe(sub_channel) rescue nil
7
+ end
8
+
9
+ def subscribe!
10
+ check_redis_config
11
+ begin
12
+ raise SubscriptionError, "wont subscribe to ##{sub_channel}. Someone already listening to this channel" if subscribers_count > 0
13
+ redis.subscribe(sub_channel) do |on|
14
+ puts "*** ##{Process.pid}: Listeting for the notifications on ##{sub_channel}..."
15
+ on.message do |channel, key|
16
+ puts "**** ##{channel}: #{key}"
17
+ GlobalErrorHandler::Redis.delete_dependencies(key) if key =~ /#{GlobalErrorHandler::Redis.exception_key("\\d+")}/
18
+ end
19
+ on.subscribe do |channel, subscriptions|
20
+ puts "##{Process.pid}: Subscribed to ##{channel} (#{subscriptions} subscriptions)"
21
+ end
22
+
23
+ on.unsubscribe do |channel, subscriptions|
24
+ puts "##{Process.pid}: Unsubscribed from ##{channel} (#{subscriptions} subscriptions)"
25
+ end
26
+ end
27
+ rescue Redis::BaseConnectionError => error
28
+ puts "##{Process.pid}: #{error}, retrying in 1s"
29
+ sleep 1
30
+ retry
31
+ rescue SubscriptionError => error
32
+ puts "##{Process.pid}: #{error}, retrying in 1s"
33
+ sleep 1
34
+ retry
35
+ rescue Interrupt => error
36
+ puts "##{Process.pid}: unsubscribing..."
37
+ unsubscribe!
38
+ redis.quit
39
+ redis.client.reconnect
40
+ end
41
+ end
42
+
43
+ def redis
44
+ GlobalErrorHandler::Redis.redis
45
+ end
46
+
47
+ def sub_channel
48
+ @sub_channel ||= "__keyevent@#{self.redis.client.db}__:expired"
49
+ end
50
+
51
+ def check_redis_config
52
+ # x Expired events (events generated every time a key expires)
53
+ # g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...
54
+ # A Alias for g$lshzxe, so that the "AKE" string means all the events.
55
+ # E Keyevent events, published with __keyevent@<db>__ prefix.
56
+ ### AE|gE|xE|AKE|gKE|xKE
57
+ redis.config('set', 'notify-keyspace-events', 'xE') unless redis.config('get', 'notify-keyspace-events').last =~ /[Agx]+.?E/
58
+ end
59
+
60
+ def subscribers_count
61
+ redis.publish sub_channel, "check subscribers count from ##{Process.pid}"
62
+ end
63
+ end # class << self
64
+
65
+ class SubscriptionError < StandardError; end
66
+ end
@@ -1,3 +1,3 @@
1
1
  module GlobalErrorHandler
2
- VERSION = "0.0.6"
2
+ VERSION = "0.1.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: global_error_handler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrii Rudenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-02 00:00:00.000000000 Z
11
+ date: 2014-12-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -106,6 +106,7 @@ files:
106
106
  - lib/global_error_handler/parser.rb
107
107
  - lib/global_error_handler/rails.rb
108
108
  - lib/global_error_handler/redis.rb
109
+ - lib/global_error_handler/redis_notification_subscriber.rb
109
110
  - lib/global_error_handler/server.rb
110
111
  - lib/global_error_handler/server/public/css/global_error_handler.css
111
112
  - lib/global_error_handler/server/public/js/global_error_handler.js
@@ -132,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
133
  version: '0'
133
134
  requirements: []
134
135
  rubyforge_project:
135
- rubygems_version: 2.4.3
136
+ rubygems_version: 2.4.4
136
137
  signing_key:
137
138
  specification_version: 4
138
139
  summary: Records application' exceptions into the separated redis database.