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 +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.
|