global_error_handler 0.3.3 → 1.0.2

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: 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: