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 +4 -4
- data/README.md +8 -0
- data/lib/global_error_handler.rb +2 -0
- data/lib/global_error_handler/app_exception.rb +6 -6
- data/lib/global_error_handler/redis.rb +27 -17
- data/lib/global_error_handler/redis_notification_subscriber.rb +66 -0
- data/lib/global_error_handler/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8eb8957dd3db4d34bc8e895b41b35c9154d16881
|
4
|
+
data.tar.gz: e8fb838a0a0a55c7dc6f4d4595ec0c96657caceb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/global_error_handler.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
class GlobalErrorHandler::AppException
|
2
2
|
class << self
|
3
|
-
def all(
|
4
|
-
|
3
|
+
def all(start, field = nil, filter = nil)
|
4
|
+
start ||= 0
|
5
5
|
if field && filter
|
6
|
-
keys = GlobalErrorHandler::Redis.filter_exception_keys
|
6
|
+
keys = GlobalErrorHandler::Redis.filter_exception_keys start, "error_#{field}", filter
|
7
7
|
else
|
8
|
-
keys = GlobalErrorHandler::Redis.exception_keys
|
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,
|
61
|
-
keys = GlobalErrorHandler::Redis.filter_exception_keys
|
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
|
-
|
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(
|
44
|
-
redis.lrange EXCEPTIONS_REDIS_KEY,
|
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(
|
48
|
-
redis.lrange filter_key(field, filter),
|
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
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
93
|
-
|
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
|
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
|
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-
|
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.
|
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.
|