global_error_handler 0.3.3 → 1.0.2

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: 3c70689ea7861a6dc4822369f19a3d2c45974b9c
4
- data.tar.gz: 564b9d3fa6450d891dc1a16696b736eb3627cf4e
3
+ metadata.gz: 08b88ca05ee6a8591095ccc87331c1bca8afaa71
4
+ data.tar.gz: 5242b40af3b6c0228fb80185cf948e987aef1263
5
5
  SHA512:
6
- metadata.gz: 393e188541f6a3ee090a6f7070a31650f839e53e25b67226c827aa9cb4052ebfe35c45458900e3ef707c8587d89c2587e63b830bf95aa0bbcbaa85d7eaa52031
7
- data.tar.gz: 986013620cacd84d4f71d8e329fa252bd7577e9e756497e24a47554cdf26f2f2beaa77f671128c7ec250d1b5c481e3e47cb522614b0c63ffcedf50f6cfb97323
6
+ metadata.gz: c6faa1b848bfc346376787cf7b212e90007174d14a07e4ff861dd206d5385ffb861600afb094ec329fad20a9340011dbedacd87d99cca2f14d34cb0184d3d379
7
+ data.tar.gz: 20e3123c510cd74ed829b477045cbe58c68402f4c427c2e999b1a2f13bd03782ca3c5cf69ef881d3aba52f3399526638cb0615633cc83ef2f8ca5579be1bba78
data/.gitignore CHANGED
@@ -20,3 +20,4 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
+ config/redis.yml
data/Rakefile CHANGED
@@ -1,2 +1 @@
1
- require "bundler/gem_tasks"
2
-
1
+ require 'bundler/gem_tasks'
@@ -4,23 +4,25 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'global_error_handler/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "global_error_handler"
7
+ spec.name = 'global_error_handler'
8
8
  spec.version = GlobalErrorHandler::VERSION
9
- spec.authors = ["Andrii Rudenko"]
10
- spec.email = ["kolobock@gmail.com"]
11
- spec.summary = %q{Records application' exceptions into the separated redis database.}
12
- spec.description = %q{On the middleware level catch an exception from Rails app and store in the separated Redis database.}
13
- spec.homepage = "https://github.com/kolobock/global_error_handler/"
14
- spec.license = "MIT"
9
+ spec.authors = ['Andrii Rudenko']
10
+ spec.email = ['kolobock@gmail.com']
11
+ spec.summary = "Records application' exceptions into the separated redis database."
12
+ spec.description = 'On the middleware level catch an exception from Rails app and store in the separated Redis database.'
13
+ spec.homepage = 'https://github.com/kolobock/global_error_handler/'
14
+ spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
 
20
- spec.add_development_dependency "bundler", '~> 1.6'
21
- spec.add_development_dependency "rake", '~> 10.3', '>= 10.3.2'
22
- spec.add_development_dependency "capistrano", "< 3.0"
20
+ spec.add_development_dependency 'bundler', '~> 1.6'
21
+ spec.add_development_dependency 'rake', '~> 10.3', '>= 10.3.2'
22
+ spec.add_development_dependency 'capistrano', '< 3.0'
23
23
 
24
24
  spec.add_dependency 'resque', '~> 1.25', '>= 1.25.1'
25
25
  spec.add_dependency 'haml', '~> 4.0', '>= 4.0.5', '< 4.1'
26
+ spec.add_runtime_dependency 'actionview', '>= 4.0.5', '< 4.3'
27
+ spec.add_dependency 'hashie', '>= 3.3'
26
28
  end
@@ -1,72 +1,80 @@
1
- class GlobalErrorHandler::AppException
2
- class << self
3
- def all(start, field = nil, filter = nil)
4
- start ||= 0
5
- if field && filter
6
- keys = GlobalErrorHandler::Redis.filter_exception_keys start, "error_#{field}", filter
7
- else
8
- keys = GlobalErrorHandler::Redis.exception_keys start
1
+ module GlobalErrorHandler
2
+ class AppException #:nodoc:
3
+ class << self
4
+ def all(start, field = nil, filter = nil)
5
+ start ||= 0
6
+ if field && filter
7
+ keys = Redis.filter_exception_keys start, "error_#{field}", filter
8
+ else
9
+ keys = Redis.exception_keys start
10
+ end
11
+ Redis.find_all keys
9
12
  end
10
- GlobalErrorHandler::Redis.find_all keys
11
- end
12
13
 
13
- def count(field = nil, filter = nil)
14
- if field && filter
15
- GlobalErrorHandler::Redis.filtered_exceptions_count("error_#{field}", filter)
16
- else
17
- GlobalErrorHandler::Redis.exceptions_count
14
+ def count(field = nil, filter = nil)
15
+ if field && filter
16
+ Redis.filtered_exceptions_count("error_#{field}", filter)
17
+ else
18
+ Redis.exceptions_count
19
+ end
18
20
  end
19
- end
20
21
 
21
- def find(id)
22
- return if id.blank?
23
- GlobalErrorHandler::Redis.find exception_key(id)
24
- end
22
+ def find(id)
23
+ return if id.blank?
24
+ Redis.find exception_key(id)
25
+ end
25
26
 
26
- def delete(id)
27
- return if id.blank?
28
- GlobalErrorHandler::Redis.delete exception_key(id)
29
- end
27
+ def delete(id)
28
+ return if id.blank?
29
+ Redis.delete exception_key(id)
30
+ end
30
31
 
31
- def delete_all(ids)
32
- return if ids.blank?
33
- keys = ids.map{ |id| exception_key id }
34
- GlobalErrorHandler::Redis.delete_all keys
35
- end
32
+ def delete_all(ids)
33
+ return if ids.blank?
34
+ keys = ids.map { |id| exception_key id }
35
+ Redis.delete_all keys
36
+ end
36
37
 
37
- def truncate(filter = nil, opts = {})
38
- if filter
39
- field = opts.delete(:field)
40
- total = opts.delete(:total) || 1000
41
- size = 1000
42
- (total / size.to_f).ceil.times do |iteration|
43
- ids = filtered_ids_by field, filter, size, iteration
44
- delete_all ids unless ids.blank?
38
+ def truncate(filter = nil, opts = {})
39
+ if filter
40
+ field = opts.delete(:field)
41
+ total = opts.delete(:total) || 1000
42
+ size = 1000
43
+ (total / size.to_f).ceil.times do |iteration|
44
+ ids = filtered_ids_by field, filter, size, iteration
45
+ delete_all ids unless ids.blank?
46
+ end
47
+ else
48
+ Redis.truncate!
45
49
  end
46
- else
47
- GlobalErrorHandler::Redis.truncate!
48
50
  end
49
- end
50
51
 
51
- def filters_for(field)
52
- keys = GlobalErrorHandler::Redis.filter_keys_for "error_#{field}"
53
- return [] if keys.blank?
54
- keys.map do |key|
55
- key =~ /^#{GlobalErrorHandler::Redis::FILTER_KEY_PREFIX}:error_#{field}:(.*)/
56
- $1
52
+ def filters_for(field)
53
+ keys = Redis.filter_keys_for "error_#{field}"
54
+ return [] if keys.blank?
55
+ keys.map do |key|
56
+ key =~ /^#{Redis::FILTER_KEY_PREFIX}:error_#{field}:(.*)/
57
+ Regexp.last_match(1)
58
+ end
57
59
  end
58
- end
59
60
 
60
- def filtered_ids_by(field, str, len=1000, start=0)
61
- keys = GlobalErrorHandler::Redis.filter_exception_keys start, "error_#{field}", str, len
62
- return [] if keys.blank?
63
- keys.map{ |key| key.split(':').last rescue nil }.compact
64
- end
61
+ def filtered_ids_by(field, str, len = 1000, start = 0)
62
+ keys = Redis.filter_exception_keys start, "error_#{field}", str, len
63
+ return [] if keys.blank?
64
+ keys.map do |key|
65
+ begin
66
+ key.split(':').last
67
+ rescue
68
+ nil
69
+ end
70
+ end.compact
71
+ end
65
72
 
66
- private
73
+ private
67
74
 
68
- def exception_key(id)
69
- GlobalErrorHandler::Redis.exception_key(id)
70
- end
75
+ def exception_key(id)
76
+ Redis.exception_key(id)
77
+ end
78
+ end # class << self
71
79
  end
72
80
  end
@@ -1,25 +1,27 @@
1
- class GlobalErrorHandler::Handler
2
- def initialize(env, exception)
3
- @env = env
4
- @exception = exception
5
- @controller = @env['action_controller.instance']
6
- @parsed_error = nil
7
- end
1
+ module GlobalErrorHandler
2
+ class Handler #:nodoc:
3
+ def initialize(env, exception)
4
+ @env = env
5
+ @exception = exception
6
+ @controller = @env['action_controller.instance']
7
+ @parsed_error = nil
8
+ end
8
9
 
9
- def process_exception!
10
- return if @env['global_error_handler.proceed_time']
11
- @env['global_error_handler.proceed_time'] = Time.now.utc
12
- parse_exception
13
- store_exception
14
- end
10
+ def process_exception!
11
+ return if @env['global_error_handler.proceed_time']
12
+ @env['global_error_handler.proceed_time'] = Time.current.utc
13
+ parse_exception
14
+ store_exception
15
+ end
15
16
 
16
- private
17
+ private
17
18
 
18
- def parse_exception
19
- @parsed_error = GlobalErrorHandler::Parser.new(@env, @exception, @controller).parse
20
- end
19
+ def parse_exception
20
+ @parsed_error = Parser.new(@env, @exception, @controller).parse
21
+ end
21
22
 
22
- def store_exception
23
- GlobalErrorHandler::Redis.store(@parsed_error.info_hash)
23
+ def store_exception
24
+ Redis.store(@parsed_error.info_hash)
25
+ end
24
26
  end
25
27
  end
@@ -1,15 +1,17 @@
1
- class GlobalErrorHandler::Middleware
2
- def initialize(app)
3
- @app = app
4
- end
1
+ module GlobalErrorHandler
2
+ class Middleware #:nodoc:
3
+ def initialize(app)
4
+ @app = app
5
+ end
5
6
 
6
- def call(env)
7
- exception = nil
8
- status, headers, response = @app.call(env)
9
- rescue Exception => exception
10
- GlobalErrorHandler::Handler.new(env, exception).process_exception!
11
- ensure
12
- raise exception if exception
13
- [status, headers, response]
7
+ def call(env)
8
+ exception = nil
9
+ status, headers, response = @app.call(env)
10
+ rescue StandardError => exception
11
+ GlobalErrorHandler::Handler.new(env, exception).process_exception!
12
+ ensure
13
+ fail exception if exception
14
+ [status, headers, response]
15
+ end
14
16
  end
15
17
  end
@@ -1,47 +1,49 @@
1
- class GlobalErrorHandler::Parser
2
- attr_reader :info_hash
1
+ module GlobalErrorHandler
2
+ class Parser #:nodoc:
3
+ attr_reader :info_hash
3
4
 
4
- def initialize(env, exception, controller)
5
- @env = env
6
- @exception = exception
7
- @controller = controller
8
- @request = ActionDispatch::Request.new(@env)
9
- @info_hash = {}
10
- end
5
+ def initialize(env, exception, controller)
6
+ @env = env
7
+ @exception = exception
8
+ @controller = controller
9
+ @request = ActionDispatch::Request.new(@env)
10
+ @info_hash = Hashie::Mash.new
11
+ end
11
12
 
12
- def parse
13
- @info_hash[:error_class] = @exception.class.to_s.strip
14
- @info_hash[:error_message] = @exception.message.to_s.strip
15
- @info_hash[:error_trace] = form_backtrace @exception.backtrace
16
- @info_hash[:request_method] = @request.method
17
- @info_hash[:request_params] = @request.params
18
- @info_hash[:target_url] = @request.url
19
- @info_hash[:referer_url] = @request.referer
20
- @info_hash[:user_agent] = @request.user_agent
21
- @info_hash[:user_info] = user_info
22
- @info_hash[:timestamp] = Time.now.utc
23
- self
24
- end
13
+ def parse
14
+ info_hash.error_class = @exception.class.to_s.strip
15
+ info_hash.error_message = @exception.message.to_s.strip
16
+ info_hash.error_trace = form_backtrace @exception.backtrace
17
+ info_hash.request_method = @request.method
18
+ info_hash.request_params = @request.params.to_s
19
+ info_hash.target_url = @request.url
20
+ info_hash.referer_url = @request.referer
21
+ info_hash.user_agent = @request.user_agent
22
+ info_hash.user_info = user_info.to_s
23
+ info_hash.timestamp = Time.current.utc
24
+ self
25
+ end
25
26
 
26
- private
27
+ private
27
28
 
28
- def user_info
29
- {
30
- Orig_IP_Address: get_remote_ip,
31
- IP_Address: @request.ip,
32
- Remote_Address: @request.remote_addr
33
- }
34
- end
29
+ def user_info
30
+ {
31
+ Orig_IP_Address: fetch_remote_ip,
32
+ IP_Address: @request.ip,
33
+ Remote_Address: @request.remote_addr
34
+ }
35
+ end
35
36
 
36
- def get_remote_ip
37
- @request.remote_ip
38
- rescue => e
39
- e.message
40
- end
37
+ def fetch_remote_ip
38
+ @request.remote_ip
39
+ rescue => e
40
+ e.message
41
+ end
41
42
 
42
- def form_backtrace(backtrace)
43
- backtrace.join("\n")
44
- rescue
45
- ''
43
+ def form_backtrace(backtrace)
44
+ backtrace.join("\n")
45
+ rescue
46
+ ''
47
+ end
46
48
  end
47
49
  end
@@ -1,19 +1,21 @@
1
- class GlobalErrorHandler::Railtie < Rails::Railtie
2
- railtie_name :global_error_handler
1
+ module GlobalErrorHandler
2
+ class Railtie < Rails::Railtie #:nodoc:
3
+ railtie_name :global_error_handler
3
4
 
4
- initializer 'global_error_handler.configure_rails_initialization' do
5
- insert_middleware
6
- end
5
+ initializer 'global_error_handler.configure_rails_initialization' do
6
+ insert_middleware
7
+ end
7
8
 
8
- rake_tasks do
9
- load File.join(File.dirname(__FILE__), '..', 'tasks', 'global_error_handler.rake')
10
- end
9
+ rake_tasks do
10
+ load File.join(File.dirname(__FILE__), '..', 'tasks', 'global_error_handler.rake')
11
+ end
11
12
 
12
- def insert_middleware
13
- if defined? ActionDispatch::DebugExceptions
14
- Rails.application.middleware.insert_after ActionDispatch::DebugExceptions, GlobalErrorHandler::Middleware
15
- else
16
- Rails.application.middleware.use GlobalErrorHandler::Middleware
13
+ def insert_middleware
14
+ if defined? ActionDispatch::DebugExceptions
15
+ Rails.application.middleware.insert_after ActionDispatch::DebugExceptions, GlobalErrorHandler::Middleware
16
+ else
17
+ Rails.application.middleware.use GlobalErrorHandler::Middleware
18
+ end
17
19
  end
18
20
  end
19
21
  end
@@ -1,154 +1,186 @@
1
- class GlobalErrorHandler::Redis
2
- CURRENT_ID_KEY = 'global_error_handler:current_id'
3
- EXCEPTIONS_REDIS_KEY = 'global_error_handler:exceptions'
4
- EXCEPTION_KEY_PREFIX = 'global_error_handler:exception'
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
9
-
10
- class << self
11
- def store(info_hash)
12
- return if info_hash.blank?
13
- redis_key = exception_key(next_id!)
14
- redis.hmset redis_key, info_hash.merge(id: current_id).to_a.flatten
15
- redis.rpush EXCEPTIONS_REDIS_KEY, redis_key
16
- FILTER_FIELDS.each do |field|
17
- redis.rpush filter_key(field, build_filter_value(info_hash[field.to_sym])), redis_key
18
- end
19
- redis.expire redis_key, REDIS_TTL
20
- end
1
+ module GlobalErrorHandler
2
+ class Redis #:nodoc:
3
+ CURRENT_ID_KEY = 'global_error_handler:current_id'
4
+ EXCEPTIONS_REDIS_KEY = 'global_error_handler:exceptions'
5
+ EXCEPTION_KEY_PREFIX = 'global_error_handler:exception'
6
+ FILTER_KEY_PREFIX = 'global_error_handler:filter'
7
+ FILTER_FIELDS = %w(error_class error_message)
8
+ FILTER_MAX_CHARS = 60
9
+ REDIS_TTL = 4 * 7 * 24 * 60 * 60 # 4 weeks
10
+
11
+ class << self
12
+ def store(info_hash)
13
+ return if info_hash.blank?
14
+ redis_key = exception_key(next_id!)
15
+ redis.hmset redis_key, info_hash.merge(id: current_id).to_a.flatten
16
+ redis.rpush EXCEPTIONS_REDIS_KEY, redis_key
17
+ FILTER_FIELDS.each do |field|
18
+ redis.rpush filter_key(field, build_filter_value(info_hash[field])), redis_key
19
+ end
20
+ redis.expire redis_key, REDIS_TTL
21
+ end
21
22
 
22
- def initialize_redis_from_config #:nodoc:
23
- redis_config = YAML.load_file(File.join(Rails.root, 'config', 'redis.yml'))[Rails.env]
24
- Redis.new(redis_config['global_exception_handler'])
25
- end
23
+ def initialize_redis_from_config
24
+ redis_config = YAML.load_file(File.join(current_root, 'config', 'redis.yml'))[current_env]
25
+ ::Redis.new(redis_config['global_exception_handler'])
26
+ end
26
27
 
27
- def redis
28
- @redis ||= begin
29
- $redis_global_exception_handler = initialize_redis_from_config unless $redis_global_exception_handler.is_a? Redis
30
- $redis_global_exception_handler
31
- end
32
- end
28
+ def redis
29
+ @redis ||= begin
30
+ $redis_global_exception_handler = initialize_redis_from_config unless $redis_global_exception_handler.is_a? ::Redis
31
+ $redis_global_exception_handler
32
+ end
33
+ end
33
34
 
34
- def current_id
35
- redis.get(CURRENT_ID_KEY)
36
- end
35
+ def current_id
36
+ redis.get(CURRENT_ID_KEY)
37
+ end
37
38
 
38
- # def sort(field, direction = 'ASC', page = 0, per_page = 1000)
39
- # redis.sort(EXCEPTIONS_REDIS_KEY, by: "#{EXCEPTION_KEY_PREFIX}_*->#{field}_*", order: "#{direction}", limit: [page, per_page])
40
- # end
39
+ # def sort(field, direction = 'ASC', page = 0, per_page = 1000)
40
+ # redis.sort(EXCEPTIONS_REDIS_KEY, by: "#{EXCEPTION_KEY_PREFIX}_*->#{field}_*", order: "#{direction}", limit: [page, per_page])
41
+ # end
41
42
 
42
- def exceptions_count
43
- redis.llen EXCEPTIONS_REDIS_KEY
44
- end
43
+ def exceptions_count
44
+ redis.llen EXCEPTIONS_REDIS_KEY
45
+ end
45
46
 
46
- def filtered_exceptions_count(field, filter)
47
- redis.llen filter_key(field, filter)
48
- end
47
+ def filtered_exceptions_count(field, filter)
48
+ redis.llen filter_key(field, filter)
49
+ end
49
50
 
50
- def exception_keys(start = 0, per_page = 10)
51
- redis.lrange EXCEPTIONS_REDIS_KEY, start.to_i, per_page.to_i + start.to_i - 1
52
- end
51
+ def exception_keys(start = 0, per_page = 10)
52
+ redis.lrange EXCEPTIONS_REDIS_KEY, start.to_i, per_page.to_i + start.to_i - 1
53
+ end
53
54
 
54
- def filter_exception_keys(start = 0, field = nil, filter = nil, per_page = 10)
55
- redis.lrange filter_key(field, filter), start.to_i, per_page.to_i + start.to_i - 1
56
- end
55
+ def filter_exception_keys(start = 0, field = nil, filter = nil, per_page = 10)
56
+ redis.lrange filter_key(field, filter), start.to_i, per_page.to_i + start.to_i - 1
57
+ end
57
58
 
58
- def filter_keys_for(field, filter = '')
59
- redis.keys filter_key(field, "#{filter}*")
60
- end
59
+ def filter_keys_for(field, filter = '')
60
+ redis.keys filter_key(field, "#{filter}*")
61
+ end
61
62
 
62
- def find(key)
63
- Hashie::Mash.new redis.hgetall(key)
64
- end
63
+ def find(key)
64
+ Hashie::Mash.new redis.hgetall(key)
65
+ end
65
66
 
66
- def find_all(keys)
67
- keys.map { |key| find(key) }.compact
68
- end
67
+ def find_all(keys)
68
+ keys.map { |key| find(key) }.compact
69
+ end
69
70
 
70
- def delete(key)
71
- delete_dependencies key
72
- redis.del key
73
- end
71
+ def delete(key)
72
+ delete_dependencies key
73
+ redis.del key
74
+ end
74
75
 
75
- def delete_all(keys)
76
- keys.each { |key| delete(key) rescue next }
77
- end
76
+ def delete_all(keys)
77
+ keys.each do |key|
78
+ begin
79
+ delete(key)
80
+ rescue
81
+ next
82
+ end
83
+ end
84
+ end
78
85
 
79
- def truncate!
80
- redis.flushdb
81
- end
86
+ def truncate!
87
+ redis.flushdb
88
+ end
82
89
 
83
- def exception_key(id = current_id)
84
- "#{EXCEPTION_KEY_PREFIX}:#{id}"
85
- end
90
+ def exception_key(id = current_id)
91
+ "#{EXCEPTION_KEY_PREFIX}:#{id}"
92
+ end
86
93
 
87
- def filter_key(field, filter)
88
- "#{FILTER_KEY_PREFIX}:#{field}:#{filter}"
89
- end
94
+ def filter_key(field, filter)
95
+ "#{FILTER_KEY_PREFIX}:#{field}:#{filter}"
96
+ end
90
97
 
91
- def delete_dependencies(key)
92
- redis.lrem EXCEPTIONS_REDIS_KEY, 1, key
93
- clear_filters key
94
- end
98
+ def delete_dependencies(key)
99
+ redis.lrem EXCEPTIONS_REDIS_KEY, 1, key
100
+ clear_filters key
101
+ end
95
102
 
96
- def clear_filters(key)
97
- FILTER_FIELDS.each do |field|
98
- retry_count = 0
99
- field_value = build_filter_value(redis.hget key, field)
100
- begin
101
- filter_keys_for(field, field_value).each do |filter_key|
102
- redis.lrem filter_key, 1, key
103
+ def clear_filters(key)
104
+ FILTER_FIELDS.each do |field|
105
+ retry_count = 0
106
+ field_value = build_filter_value(redis.hget key, field)
107
+ begin
108
+ filter_keys_for(field, field_value).each do |filter_key|
109
+ redis.lrem filter_key, 1, key
110
+ end
111
+ rescue
112
+ field_value = ''
113
+ retry if (retry_count += 1) < 2
103
114
  end
104
- rescue
105
- field_value = ''
106
- retry if (retry_count += 1) < 2
107
115
  end
108
116
  end
109
- end
110
117
 
111
- def cleanup_database_dependencies!
112
- total_exceptions_count = exceptions_count
113
- total_exception_keys_count = redis.keys(exception_key('*')).size
114
- if total_exceptions_count > total_exception_keys_count
115
- puts "==> Database dependency is broken. Need to fix it!"
116
- start = 0
117
- per_page = 500
118
- exception_keys_to_be_cleaned_up = []
119
- valid_chunks_count = 0
120
- cleanup_count = exception_keys_to_be_cleaned_up.size
121
- while total_exceptions_count > start
122
- exception_keys(start, per_page).each do |redis_key|
123
- exception_keys_to_be_cleaned_up.push redis_key unless redis.exists(redis_key)
118
+ def cleanup_database_dependencies!
119
+ total_exceptions_count = exceptions_count
120
+ total_exception_keys_count = redis.keys(exception_key('*')).size
121
+ if total_exceptions_count > total_exception_keys_count
122
+ puts '==> Database dependency is broken. Need to fix it!'
123
+ start = 0
124
+ per_page = 500
125
+ exception_keys_to_be_cleaned_up = []
126
+ valid_chunks_count = 0
127
+ cleanup_count = exception_keys_to_be_cleaned_up.size
128
+ while total_exceptions_count > start
129
+ exception_keys(start, per_page).each do |redis_key|
130
+ exception_keys_to_be_cleaned_up.push redis_key unless redis.exists(redis_key)
131
+ end
132
+ if cleanup_count == (cleanup_count = exception_keys_to_be_cleaned_up.size)
133
+ valid_chunks_count += 1
134
+ end
135
+ break if valid_chunks_count > 3 # if three ranges in a row are consistent, treat database consistency and finish looping
136
+ start += per_page
124
137
  end
125
- if cleanup_count == (cleanup_count = exception_keys_to_be_cleaned_up.size)
126
- valid_chunks_count += 1
138
+
139
+ puts "*** found #{exception_keys_to_be_cleaned_up.count} broken dependency keys."
140
+ exception_keys_to_be_cleaned_up.each do |redis_key|
141
+ begin
142
+ delete_dependencies(redis_key)
143
+ rescue
144
+ next
145
+ end
127
146
  end
128
- break if valid_chunks_count > 3 #if three ranges in a row are consistent, treat database consistency and finish looping
129
- start += per_page
147
+ else
148
+ puts '==> Database dependency is OK. No need to fix it!'
130
149
  end
150
+ end
131
151
 
132
- puts "*** found #{exception_keys_to_be_cleaned_up.count} broken dependency keys."
133
- exception_keys_to_be_cleaned_up.each do |redis_key|
134
- delete_dependencies(redis_key) rescue next
135
- end
136
- else
137
- puts "==> Database dependency is OK. No need to fix it!"
152
+ protected
153
+
154
+ def next_id!
155
+ redis.incr(CURRENT_ID_KEY)
138
156
  end
139
- end
140
157
 
141
- protected
158
+ private
142
159
 
143
- def next_id!
144
- redis.incr(CURRENT_ID_KEY)
145
- end
160
+ def build_filter_value(txt)
161
+ str = begin
162
+ txt.split("\n").first
163
+ rescue
164
+ ''
165
+ end
166
+ str[0...FILTER_MAX_CHARS]
167
+ end
146
168
 
147
- private
169
+ def current_root
170
+ Rails.root
171
+ rescue
172
+ begin
173
+ settings.root
174
+ rescue
175
+ '.'
176
+ end
177
+ end
148
178
 
149
- def build_filter_value(txt)
150
- str = txt.split("\n").first rescue ''
151
- str[0...FILTER_MAX_CHARS]
179
+ def current_env
180
+ Rails.env
181
+ rescue
182
+ ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
183
+ end
152
184
  end
153
185
  end
154
186
  end
@@ -1,66 +1,70 @@
1
1
  require 'global_error_handler/redis'
2
2
 
3
- class GlobalErrorHandler::RedisNotificationSubscriber
4
- class << self
5
- def unsubscribe!
6
- redis.unsubscribe(sub_channel) rescue nil
7
- end
3
+ module GlobalErrorHandler
4
+ class RedisNotificationSubscriber #:nodoc:
5
+ class << self
6
+ def unsubscribe!
7
+ redis.unsubscribe(sub_channel)
8
+ rescue
9
+ nil
10
+ end
8
11
 
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
12
+ def subscribe!
13
+ check_redis_config
14
+ begin
15
+ fail SubscriptionError, "wont subscribe to ##{sub_channel}. Someone already listening to this channel" if subscribers_count > 0
16
+ redis.subscribe(sub_channel) do |on|
17
+ puts "*** ##{Process.pid}: Listeting for the notifications on ##{sub_channel}..."
18
+ on.message do |channel, key|
19
+ puts "**** ##{channel}: #{key}"
20
+ Redis.delete_dependencies(key) if key =~ /#{Redis.exception_key("\\d+")}/
21
+ end
22
+ on.subscribe do |channel, subscriptions|
23
+ puts "##{Process.pid}: Subscribed to ##{channel} (#{subscriptions} subscriptions)"
24
+ end
22
25
 
23
- on.unsubscribe do |channel, subscriptions|
24
- puts "##{Process.pid}: Unsubscribed from ##{channel} (#{subscriptions} subscriptions)"
26
+ on.unsubscribe do |channel, subscriptions|
27
+ puts "##{Process.pid}: Unsubscribed from ##{channel} (#{subscriptions} subscriptions)"
28
+ end
25
29
  end
30
+ rescue ::Redis::BaseConnectionError => error
31
+ puts "##{Process.pid}: #{error}, retrying in 1s"
32
+ sleep 1
33
+ retry
34
+ rescue SubscriptionError => error
35
+ puts "##{Process.pid}: #{error}, retrying in 1s"
36
+ sleep 1
37
+ retry
38
+ rescue Interrupt => error
39
+ puts "##{Process.pid}: unsubscribing..."
40
+ unsubscribe!
41
+ redis.quit
42
+ redis.client.reconnect
26
43
  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
44
  end
41
- end
42
45
 
43
- def redis
44
- @redis ||= GlobalErrorHandler::Redis.initialize_redis_from_config
45
- end
46
+ def redis
47
+ @redis ||= Redis.initialize_redis_from_config
48
+ end
46
49
 
47
- def sub_channel
48
- @sub_channel ||= "__keyevent@#{self.redis.client.db}__:expired"
49
- end
50
+ def sub_channel
51
+ @sub_channel ||= "__keyevent@#{redis.client.db}__:expired"
52
+ end
50
53
 
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
54
+ def check_redis_config
55
+ # x Expired events (events generated every time a key expires)
56
+ # g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...
57
+ # A Alias for g$lshzxe, so that the "AKE" string means all the events.
58
+ # E Keyevent events, published with __keyevent@<db>__ prefix.
59
+ ### AE|gE|xE|AKE|gKE|xKE
60
+ redis.config('set', 'notify-keyspace-events', 'xE') unless redis.config('get', 'notify-keyspace-events').last =~ /[Agx]+.?E/
61
+ end
59
62
 
60
- def subscribers_count
61
- redis.publish sub_channel, "check subscribers count from ##{Process.pid}"
62
- end
63
- end # class << self
63
+ def subscribers_count
64
+ redis.publish sub_channel, "check subscribers count from ##{Process.pid}"
65
+ end
66
+ end # class << self
64
67
 
65
- class SubscriptionError < StandardError; end
68
+ class SubscriptionError < StandardError; end
69
+ end
66
70
  end
@@ -10,10 +10,10 @@
10
10
  %li= link_to 'Truncate all', exceptions_path(nil, params[:filter_by], params[:filter]) + '/truncate',
11
11
  method: :delete, class: 'js-link',
12
12
  data: {confirm: 'Are you sure to truncate ALL the Exceptions?'}
13
- %li.filter= select_tag 'filter_by_message', options_for_select(@all_messages, params[:filter_by].to_s.eql?('message') ? get_filter : nil), prompt: 'Error Message Filter',
14
- class: 'filter-by', data: {field: 'message'}
15
- %li.filter= select_tag 'filter_by_class', options_for_select(@all_classes, params[:filter_by].to_s.eql?('class') ? get_filter : nil), prompt: 'Error Class Filter',
16
- class: 'filter-by', data: {field: 'class'}
13
+ %li.filter= select_tag 'filter_by_message', options_for_select(@all_messages, params[:filter_by].to_s.eql?('message') ? get_filter : nil),
14
+ prompt: 'Error Message Filter', class: 'filter-by', data: {field: 'message'}
15
+ %li.filter= select_tag 'filter_by_class', options_for_select(@all_classes, params[:filter_by].to_s.eql?('class') ? get_filter : nil),
16
+ prompt: 'Error Class Filter', class: 'filter-by', data: {field: 'class'}
17
17
 
18
18
  %p.sub
19
19
  Showing #{apps_start_at} to #{apps_end_at} of
@@ -1,5 +1,5 @@
1
1
  module GlobalErrorHandler
2
- module Server
2
+ module Server #:nodoc:
3
3
  GEH_VIEW_PATH = File.join(File.dirname(__FILE__), 'server', 'views')
4
4
  GEH_PUBLIC_PATH = File.join(File.dirname(__FILE__), 'server', 'public')
5
5
 
@@ -18,7 +18,7 @@ module GlobalErrorHandler
18
18
  end
19
19
 
20
20
  app.get '/exceptions/:id' do
21
- @app_exception = GlobalErrorHandler::AppException.find(params[:id])
21
+ @app_exception = AppException.find(params[:id])
22
22
  show_view :show
23
23
  end
24
24
 
@@ -31,33 +31,34 @@ module GlobalErrorHandler
31
31
  end
32
32
 
33
33
  app.delete '/exceptions/delete' do
34
- GlobalErrorHandler::AppException.delete_all(params[:app_exception_delete_ids])
34
+ AppException.delete_all(params[:app_exception_delete_ids])
35
35
  redirect_to_exceptions
36
36
  end
37
37
 
38
38
  app.delete '/exceptions/:id' do
39
- GlobalErrorHandler::AppException.delete(params[:id])
39
+ AppException.delete(params[:id])
40
40
  redirect_to_exceptions
41
41
  end
42
42
 
43
43
  app.tabs << 'Exceptions'
44
44
 
45
+ require 'action_view'
45
46
  app.helpers do
46
- include ActionView::Helpers::TextHelper #link_to
47
- include ActionView::Helpers::UrlHelper #simple_format
48
- include ActionView::Helpers::FormHelper #select_tag check_box_tag
49
- include ActionView::Helpers::FormOptionsHelper #options_for_select
50
- include ActionView::Helpers::OutputSafetyHelper #delete via ajax
47
+ include ActionView::Helpers::TextHelper # link_to
48
+ include ActionView::Helpers::UrlHelper # simple_format
49
+ include ActionView::Helpers::FormHelper # select_tag check_box_tag
50
+ include ActionView::Helpers::FormOptionsHelper # options_for_select
51
+ include ActionView::Helpers::OutputSafetyHelper # delete via ajax
51
52
 
52
53
  def prepare_and_show_index_action
53
- @app_exceptions = GlobalErrorHandler::AppException.all(params[:start], params[:filter_by], get_filter)
54
- @all_classes = GlobalErrorHandler::AppException.filters_for('class')
55
- @all_messages = GlobalErrorHandler::AppException.filters_for('message')
54
+ @app_exceptions = AppException.all(params[:start], params[:filter_by], fetch_filter)
55
+ @all_classes = AppException.filters_for('class')
56
+ @all_messages = AppException.filters_for('message')
56
57
  show_view :index
57
58
  end
58
59
 
59
60
  def truncate_and_redirect_to_exceptions
60
- GlobalErrorHandler::AppException.truncate(get_filter, field: params[:filter_by], total: apps_size)
61
+ AppException.truncate(fetch_filter, field: params[:filter_by], total: apps_size)
61
62
  redirect exceptions_path
62
63
  end
63
64
 
@@ -66,10 +67,10 @@ module GlobalErrorHandler
66
67
  end
67
68
 
68
69
  def show_view(filename = :index)
69
- erb haml( File.read(File.join(GEH_VIEW_PATH, "#{filename}.html.haml")) )
70
+ erb haml(File.read(File.join(GEH_VIEW_PATH, "#{filename}.html.haml")))
70
71
  end
71
72
 
72
- def geh_public_view(filename, dir='')
73
+ def geh_public_view(filename, dir = '')
73
74
  file = File.join(GEH_PUBLIC_PATH, dir, filename)
74
75
  begin
75
76
  cache_control :public, max_age: 1800
@@ -80,13 +81,13 @@ module GlobalErrorHandler
80
81
  end
81
82
 
82
83
  def exceptions_path(start = nil, filter_by = nil, filter = nil)
83
- path = "/resque/exceptions"
84
+ path = '/resque/exceptions'
84
85
  path += "/filter/#{filter_by}/#{URI.escape(filter)}" if filter_by && filter
85
86
  path += "?start=#{start}" if start
86
87
  path
87
88
  end
88
89
 
89
- def exception_path(id, start=nil, filter_by = nil, filter = nil)
90
+ def exception_path(id, start = nil, filter_by = nil, filter = nil)
90
91
  path = "/resque/exceptions/#{id}"
91
92
  path_params = []
92
93
  path_params.push "start=#{start}" if start
@@ -96,7 +97,7 @@ module GlobalErrorHandler
96
97
  end
97
98
 
98
99
  def apps_size
99
- @apps_size ||= GlobalErrorHandler::AppException.count(params[:filter_by], get_filter).to_i
100
+ @apps_size ||= AppException.count(params[:filter_by], fetch_filter).to_i
100
101
  end
101
102
 
102
103
  def apps_start_at
@@ -116,7 +117,7 @@ module GlobalErrorHandler
116
117
  end
117
118
  end
118
119
 
119
- def each_app_exception(&block)
120
+ def each_app_exception
120
121
  return unless block_given?
121
122
  @app_exceptions.try(:each) do |app_exception|
122
123
  yield app_exception
@@ -129,17 +130,17 @@ module GlobalErrorHandler
129
130
  total = options[:total] || 0
130
131
  return if total < per_page
131
132
 
132
- markup = ""
133
+ markup = ''
133
134
  if start - per_page >= 0
134
- markup << link_to(raw("&laquo; less"), exceptions_path(start - per_page), class: 'btn less')
135
+ markup << link_to(raw('&laquo; less'), exceptions_path(start - per_page), class: 'btn less')
135
136
  elsif start > 0 && start < per_page
136
- markup << link_to(raw("&laquo; less"), exceptions_path(0), class: 'btn less')
137
+ markup << link_to(raw('&laquo; less'), exceptions_path(0), class: 'btn less')
137
138
  end
138
139
 
139
140
  markup << pages_markup(start, per_page, total)
140
141
 
141
142
  if start + per_page < total
142
- markup << link_to(raw("more &raquo;"), exceptions_path(start + per_page), class: 'btn more')
143
+ markup << link_to(raw('more &raquo;'), exceptions_path(start + per_page), class: 'btn more')
143
144
  end
144
145
  markup
145
146
  end
@@ -150,7 +151,7 @@ module GlobalErrorHandler
150
151
 
151
152
  left_ind = start / per_page
152
153
  markups = [left_ind.to_s]
153
- while (left_ind -= 1) >= 0 && (start/per_page - left_ind <= max_side_links || pages_count < max_links)
154
+ while (left_ind -= 1) >= 0 && (start / per_page - left_ind <= max_side_links || pages_count < max_links)
154
155
  markups.unshift link_to(left_ind, exceptions_path(left_ind * per_page, params[:filter_by], params[:filter]), class: 'btn pages')
155
156
  end
156
157
  right_ind = start / per_page
@@ -158,7 +159,7 @@ module GlobalErrorHandler
158
159
  markups.unshift '...' if right_ind - max_side_links > 1
159
160
  markups.unshift link_to(0, exceptions_path(0, params[:filter_by], params[:filter]), class: 'btn pages')
160
161
  end
161
- while (right_ind +=1) * per_page < total && (right_ind - start / per_page <= max_side_links || pages_count < max_links)
162
+ while (right_ind += 1) * per_page < total && (right_ind - start / per_page <= max_side_links || pages_count < max_links)
162
163
  markups.push link_to(right_ind, exceptions_path(per_page * right_ind, params[:filter_by], params[:filter]), class: 'btn pages')
163
164
  end
164
165
  if pages_count >= max_links && pages_count >= right_ind
@@ -176,7 +177,7 @@ module GlobalErrorHandler
176
177
  max_side_links * 2 + 1
177
178
  end
178
179
 
179
- def get_filter
180
+ def fetch_filter
180
181
  URI.unescape(params[:filter]) if params[:filter]
181
182
  end
182
183
  end
@@ -1,3 +1,3 @@
1
- module GlobalErrorHandler
2
- VERSION = "0.3.3"
1
+ module GlobalErrorHandler #:nodoc:
2
+ VERSION = '1.0.2'
3
3
  end
@@ -1,23 +1,24 @@
1
1
  $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
2
2
 
3
- module GlobalErrorHandler
3
+ module GlobalErrorHandler #:nodoc:
4
4
  end
5
5
 
6
6
  require 'resque'
7
7
  require 'resque/server'
8
8
  require 'haml'
9
-
9
+ require 'hashie'
10
10
 
11
11
  require 'global_error_handler/redis'
12
12
  require 'global_error_handler/parser'
13
13
  require 'global_error_handler/handler'
14
14
  require 'global_error_handler/app_exception'
15
+
15
16
  require 'global_error_handler/server'
16
17
 
17
18
  require 'global_error_handler/middleware'
18
19
  require 'global_error_handler/rails' if defined? Rails::Railtie
19
20
 
20
- require "global_error_handler/version"
21
+ require 'global_error_handler/version'
21
22
 
22
23
  require 'global_error_handler/redis_notification_subscriber'
23
24
 
@@ -1,17 +1,17 @@
1
1
  module GlobalErrorHandler
2
- class Capistrano
2
+ class Capistrano #:nodoc:
3
3
  def self.load_into(config)
4
4
  config.load do
5
5
  namespace :global_error_handler do
6
6
  desc 'Subscribe to expiration'
7
7
  task :subscribe do
8
- run %Q{cd #{current_path} && RAILS_ENV=#{rails_env} nohup rake global_error_handler:cleanup_database_dependencies >/dev/null 2>&1 & sleep 2}
9
- run %Q{cd #{current_path} && RAILS_ENV=#{rails_env} nohup rake global_error_handler:subscribe_to_expired >/dev/null 2>&1 & sleep 3}
8
+ run %(cd #{current_path} && RAILS_ENV=#{rails_env} nohup rake global_error_handler:cleanup_database_dependencies >/dev/null 2>&1 & sleep 2)
9
+ run %(cd #{current_path} && RAILS_ENV=#{rails_env} nohup rake global_error_handler:subscribe_to_expired >/dev/null 2>&1 & sleep 3)
10
10
  end
11
11
 
12
12
  desc 'Unsubscribe from expiration'
13
13
  task :unsubscribe do
14
- run %Q{cd #{current_path} && RAILS_ENV=#{rails_env} #{rake} global_error_handler:unsubscribe_from_expired}
14
+ run %(cd #{current_path} && RAILS_ENV=#{rails_env} #{rake} global_error_handler:unsubscribe_from_expired)
15
15
  end
16
16
 
17
17
  desc 'Update Subscription to expiration'
@@ -1,18 +1,22 @@
1
1
  namespace :global_error_handler do
2
2
  desc 'Subscribe to expired keyevent notifications'
3
3
  task subscribe_to_expired: :environment do
4
- puts '*** pid file exists!' or next if File.exists?(pid_file)
5
- File.open(pid_file, 'w'){|f| f.puts Process.pid}
4
+ puts('*** pid file exists!') || next if File.exist?(pid_file)
5
+ File.open(pid_file, 'w') { |f| f.puts Process.pid }
6
6
  begin
7
7
  GlobalErrorHandler::RedisNotificationSubscriber.subscribe!
8
8
  ensure
9
- File.unlink pid_file rescue nil
9
+ begin
10
+ File.unlink pid_file
11
+ rescue
12
+ nil
13
+ end
10
14
  end
11
15
  end
12
16
 
13
17
  desc 'Unsubscribe from expired keyevent notifications'
14
18
  task unsubscribe_from_expired: :environment do
15
- puts '*** pid file does not exist!' or next unless File.exists?(pid_file)
19
+ puts('*** pid file does not exist!') || next unless File.exist?(pid_file)
16
20
  process_id = File.read(pid_file).to_i
17
21
  begin
18
22
  Process.kill 0, process_id
@@ -25,7 +29,11 @@ namespace :global_error_handler do
25
29
  rescue Errno::ESRCH
26
30
  puts "** No such process ##{process_id}. Exiting..."
27
31
  ensure
28
- File.unlink pid_file rescue nil
32
+ begin
33
+ File.unlink pid_file
34
+ rescue
35
+ nil
36
+ end
29
37
  end
30
38
  end
31
39
 
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.3.3
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrii Rudenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-16 00:00:00.000000000 Z
11
+ date: 2015-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -104,6 +104,40 @@ dependencies:
104
104
  - - "<"
105
105
  - !ruby/object:Gem::Version
106
106
  version: '4.1'
107
+ - !ruby/object:Gem::Dependency
108
+ name: actionview
109
+ requirement: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: 4.0.5
114
+ - - "<"
115
+ - !ruby/object:Gem::Version
116
+ version: '4.3'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: 4.0.5
124
+ - - "<"
125
+ - !ruby/object:Gem::Version
126
+ version: '4.3'
127
+ - !ruby/object:Gem::Dependency
128
+ name: hashie
129
+ requirement: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '3.3'
134
+ type: :runtime
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '3.3'
107
141
  description: On the middleware level catch an exception from Rails app and store in
108
142
  the separated Redis database.
109
143
  email: