global_error_handler 0.0.6 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.