global_error_handler 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 07f69e5667ebe1d85b0a8c19d1235a4e2f616376
4
+ data.tar.gz: 6b567dc07286f74344be7f8095f5ff2143653428
5
+ SHA512:
6
+ metadata.gz: 0b6e21e73feb9f79d781bda7e1568f189d9e925e6b4e5add59358dc27f3dd756026b75c945b8daabe872a24756d28db307c67083f51be810077089c9c3d61a79
7
+ data.tar.gz: ac37c37754e81d3619c43c3dc8b75be62a041597a81f157643559266a3f53a6506511e836f4d636cef9d06da9187609b9400393f01479f61580581ae8340328d
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in global_error_handler.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 TODO: Write your name
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # GlobalErrorHandler
2
+
3
+ GlobalErrorHandler catches application exceptions on the middleware level and store them into the redis database.
4
+ It adds Exceptions tab to Redis Web server in case to view, filter, delete or truncate them.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'global_error_handler'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install global_error_handler
19
+
20
+ ## Configuration
21
+
22
+ Add redis database configuration into `global_exceptions_handler` section of _redis.yml_. See [redis_example.yml](https://github.com/kolobock/global_error_handler/blob/master/config/redis_example.yml) for more details.
23
+
24
+ ## Usage
25
+
26
+ Target your browser to `/resque/exceptions/` path of your Rails Application server to view all Exceptions.
27
+ *Truncate all* deletes all Exceptions by filter if filter is selected or _ALL_ Exceptions otherwise.
28
+
29
+ If `rescue_from` is used in your application, add following line at top of the method specified to `with:` parameter of resque_from helper.
30
+
31
+ ```ruby
32
+ GlobalErrorHandler::Handler.new(request.env, exception).process_exception!
33
+ ```
34
+
35
+ ## Contributing
36
+
37
+ 1. Fork it ( https://github.com/[my-github-username]/global_error_handler/fork )
38
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
39
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
40
+ 4. Push to the branch (`git push origin my-new-feature`)
41
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,38 @@
1
+ development:
2
+ main: &dev_main
3
+ db: "1"
4
+ host: localhost
5
+ port: 6379
6
+ password:
7
+
8
+ global_exception_handler: &dev_global_exception_handler
9
+ db: "3"
10
+ host: localhost
11
+ port: 6379
12
+ password:
13
+
14
+ test:
15
+ main:
16
+ <<: *dev_main
17
+
18
+ global_exception_handler:
19
+ <<: *dev_global_exception_handler
20
+
21
+ production:
22
+ main: &main
23
+ db: '0'
24
+ host: localhost
25
+ port: 6379
26
+ password:
27
+
28
+ global_exception_handler: &global_exception_handler
29
+ db: '2'
30
+ host: localhost
31
+ port: 6379
32
+ password:
33
+
34
+ staging:
35
+ main:
36
+ <<: *main
37
+ global_exception_handler:
38
+ <<: *global_exception_handler
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'global_error_handler/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "global_error_handler"
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"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", '~> 1.6'
22
+ spec.add_development_dependency "rake", '~> 10.3', '>= 10.3.2'
23
+
24
+ spec.add_dependency 'resque', '~> 1.25', '>= 1.25.1'
25
+ spec.add_dependency 'haml', '~> 4.0', '>= 4.0.5'
26
+ end
@@ -0,0 +1,22 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
2
+
3
+ module GlobalErrorHandler
4
+ end
5
+
6
+ require 'resque'
7
+ require 'resque/server'
8
+ require 'haml'
9
+
10
+
11
+ require 'global_error_handler/redis'
12
+ require 'global_error_handler/parser'
13
+ require 'global_error_handler/handler'
14
+ require 'global_error_handler/app_exception'
15
+ require 'global_error_handler/global_error_handler'
16
+
17
+ require 'global_error_handler/middleware'
18
+ require 'global_error_handler/rails' if defined? Rails::Railtie
19
+
20
+ require "global_error_handler/version"
21
+
22
+ Resque::Server.register GlobalErrorHandler::Server
@@ -0,0 +1,68 @@
1
+ class GlobalErrorHandler::AppException
2
+ class << self
3
+ def all(page, field = nil, filter = nil)
4
+ page ||= 0
5
+ if field && filter
6
+ keys = GlobalErrorHandler::Redis.filter_exception_keys page, "error_#{field}", filter
7
+ else
8
+ keys = GlobalErrorHandler::Redis.exception_keys page
9
+ end
10
+ GlobalErrorHandler::Redis.find_all keys
11
+ end
12
+
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
18
+ end
19
+ end
20
+
21
+ def find(id)
22
+ return if id.blank?
23
+ GlobalErrorHandler::Redis.find exception_key(id)
24
+ end
25
+
26
+ def delete(id)
27
+ return if id.blank?
28
+ GlobalErrorHandler::Redis.delete exception_key(id)
29
+ end
30
+
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
36
+
37
+ def truncate(filter = nil, opts = {})
38
+ if filter
39
+ field = opts.delete(:field)
40
+ ids = filtered_ids_by field, filter
41
+ delete_all ids
42
+ else
43
+ GlobalErrorHandler::Redis.truncate!
44
+ end
45
+ end
46
+
47
+ def filters_for(field)
48
+ keys = GlobalErrorHandler::Redis.filter_keys_for "error_#{field}"
49
+ return [] if keys.blank?
50
+ keys.map do |key|
51
+ key =~ /^#{GlobalErrorHandler::Redis::FILTER_KEY_PREFIX}:error_#{field}:(.*)/
52
+ $1
53
+ end
54
+ end
55
+
56
+ def filtered_ids_by(field, str)
57
+ GlobalErrorHandler::Redis.filter_exception_keys 0, "error_#{field}", str, len
58
+ return [] if keys.blank?
59
+ keys.map{ |key| key.split(':').last rescue nil }.compact
60
+ end
61
+
62
+ private
63
+
64
+ def exception_key(id)
65
+ GlobalErrorHandler::Redis.exception_key(id)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,185 @@
1
+ module GlobalErrorHandler
2
+ module Server
3
+ GEH_VIEW_PATH = File.join(File.dirname(__FILE__), 'server', 'views')
4
+ GEH_PUBLIC_PATH = File.join(File.dirname(__FILE__), 'server', 'public')
5
+
6
+ def self.registered(app)
7
+ app.get '/exceptions' do
8
+ key = params.keys.first
9
+ if %w(js css).include? key
10
+ geh_public_view params[key], key
11
+ else
12
+ prepare_and_show_index_action
13
+ end
14
+ end
15
+
16
+ app.get '/exceptions/filter/:filter_by/:filter' do
17
+ prepare_and_show_index_action
18
+ end
19
+
20
+ app.get '/exceptions/:id' do
21
+ @app_exception = GlobalErrorHandler::AppException.find(params[:id])
22
+ show_view :show
23
+ end
24
+
25
+ app.delete '/exceptions/filter/:filter_by/:filter/truncate' do
26
+ truncate_and_redirect_to_exceptions
27
+ end
28
+
29
+ app.delete '/exceptions/truncate' do
30
+ truncate_and_redirect_to_exceptions
31
+ end
32
+
33
+ app.delete '/exceptions/delete' do
34
+ GlobalErrorHandler::AppException.delete_all(params[:app_exception_delete_ids])
35
+ redirect_to_exceptions
36
+ end
37
+
38
+ app.delete '/exceptions/:id' do
39
+ GlobalErrorHandler::AppException.delete(params[:id])
40
+ redirect_to_exceptions
41
+ end
42
+
43
+ app.tabs << 'Exceptions'
44
+
45
+ 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
51
+
52
+ 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')
56
+ show_view :index
57
+ end
58
+
59
+ def truncate_and_redirect_to_exceptions
60
+ GlobalErrorHandler::AppException.truncate(get_filter, field: params[:filter_by])
61
+ redirect exceptions_path
62
+ end
63
+
64
+ def redirect_to_exceptions
65
+ redirect exceptions_path(params[:start], params[:filter_by], params[:filter])
66
+ end
67
+
68
+ def show_view(filename = :index)
69
+ erb haml( File.read(File.join(GEH_VIEW_PATH, "#{filename}.html.haml")) )
70
+ end
71
+
72
+ def geh_public_view(filename, dir='')
73
+ file = File.join(GEH_PUBLIC_PATH, dir, filename)
74
+ begin
75
+ cache_control :public, :max_age => 1800
76
+ send_file file
77
+ rescue Errno::ENOENT
78
+ 404
79
+ end
80
+ end
81
+
82
+ def exceptions_path(start = nil, filter_by = nil, filter = nil)
83
+ path = "/resque/exceptions"
84
+ path += "/filter/#{filter_by}/#{URI.escape(filter)}" if filter_by && filter
85
+ path += "?start=#{start}" if start
86
+ path
87
+ end
88
+
89
+ def exception_path(id, start=nil, filter_by = nil, filter = nil)
90
+ path = "/resque/exceptions/#{id}"
91
+ path_params = []
92
+ path_params.push "start=#{start}" if start
93
+ path_params.push "filter_by=#{filter_by}&filter=#{URI.escape(filter)}" if filter_by && filter
94
+ path += '?' + path_params.join('&') if path_params.size > 0
95
+ path
96
+ end
97
+
98
+ def apps_size
99
+ @apps_size ||= GlobalErrorHandler::AppException.count(params[:filter_by], get_filter).to_i
100
+ end
101
+
102
+ def apps_start_at
103
+ return 0 if apps_size < 1
104
+ params[:start].to_i + 1
105
+ end
106
+
107
+ def apps_per_page
108
+ 10
109
+ end
110
+
111
+ def apps_end_at
112
+ if apps_start_at + apps_per_page > apps_size
113
+ apps_size
114
+ else
115
+ apps_start_at + apps_per_page - 1
116
+ end
117
+ end
118
+
119
+ def each_app_exception(&block)
120
+ return unless block_given?
121
+ @app_exceptions.try(:each) do |app_exception|
122
+ yield app_exception
123
+ end
124
+ end
125
+
126
+ def pagination(options = {})
127
+ start = options[:start] || 0
128
+ per_page = apps_per_page
129
+ total = options[:total] || 0
130
+ return if total < per_page
131
+
132
+ markup = ""
133
+ if start - per_page >= 0
134
+ markup << link_to(raw("&laquo; less"), exceptions_path(start - per_page), :class => 'btn less')
135
+ elsif start > 0 && start < per_page
136
+ markup << link_to(raw("&laquo; less"), exceptions_path(0), :class => 'btn less')
137
+ end
138
+
139
+ markup << pages_markup(start, per_page, total)
140
+
141
+ if start + per_page < total
142
+ markup << link_to(raw("more &raquo;"), exceptions_path(start + per_page), :class => 'btn more')
143
+ end
144
+ markup
145
+ end
146
+
147
+ def pages_markup(start, per_page, total)
148
+ pages_count = ((total - 1)/ per_page).ceil
149
+ return '' if pages_count < 1
150
+
151
+ left_ind = start / per_page
152
+ markups = [left_ind.to_s]
153
+ while (left_ind -= 1) >= 0 && (start/per_page - left_ind <= max_side_links || pages_count < max_links)
154
+ markups.unshift link_to(left_ind, exceptions_path(left_ind * per_page, params[:filter_by], params[:filter]), :class => 'btn pages')
155
+ end
156
+ right_ind = start / per_page
157
+ if right_ind > max_side_links && pages_count >= max_links
158
+ markups.unshift '...' if right_ind - max_side_links > 1
159
+ markups.unshift link_to(0, exceptions_path(0, params[:filter_by], params[:filter]), :class => 'btn pages')
160
+ end
161
+ while (right_ind +=1) * per_page < total && (right_ind - start / per_page <= max_side_links || pages_count < max_links)
162
+ markups.push link_to(right_ind, exceptions_path(per_page * right_ind, params[:filter_by], params[:filter]), :class => 'btn pages')
163
+ end
164
+ if pages_count >= max_links && pages_count >= right_ind
165
+ markups.push '...' if pages_count - right_ind >= 1
166
+ markups.push link_to(pages_count, exceptions_path(pages_count * per_page, params[:filter_by], params[:filter]), :class => 'btn pages')
167
+ end
168
+ markups.join(' ')
169
+ end
170
+
171
+ def max_side_links
172
+ 4
173
+ end
174
+
175
+ def max_links
176
+ max_side_links * 2 + 1
177
+ end
178
+
179
+ def get_filter
180
+ URI.unescape(params[:filter]) if params[:filter]
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,25 @@
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
8
+
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
15
+
16
+ private
17
+
18
+ def parse_exception
19
+ @parsed_error = GlobalErrorHandler::Parser.new(@env, @exception, @controller).parse
20
+ end
21
+
22
+ def store_exception
23
+ GlobalErrorHandler::Redis.store(@parsed_error.info_hash)
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ class GlobalErrorHandler::Middleware
2
+ def initialize(app)
3
+ @app = app
4
+ end
5
+
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]
14
+ end
15
+ end
@@ -0,0 +1,41 @@
1
+ class GlobalErrorHandler::Parser
2
+ attr_reader :info_hash
3
+
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
11
+
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] = @exception.backtrace.join("\n")
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
25
+
26
+ private
27
+
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
35
+
36
+ def get_remote_ip
37
+ @request.remote_ip
38
+ rescue => e
39
+ e.message
40
+ end
41
+ end
@@ -0,0 +1,13 @@
1
+ class GlobalErrorHandler::Railtie < Rails::Railtie
2
+ initializer 'global_error_handler.configure_rails_initialization' do
3
+ insert_middleware
4
+ end
5
+
6
+ def insert_middleware
7
+ if defined? ActionDispatch::DebugExceptions
8
+ Rails.application.middleware.insert_after ActionDispatch::DebugExceptions, GlobalErrorHandler::Middleware
9
+ else
10
+ Rails.application.middleware.use GlobalErrorHandler::Middleware
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,99 @@
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
+
7
+ class << self
8
+ def store(info_hash)
9
+ redis_key = exception_key(next_id!)
10
+ redis.hmset redis_key, info_hash.merge(id: current_id).to_a.flatten
11
+ redis.rpush EXCEPTIONS_REDIS_KEY, redis_key
12
+ %w(error_class error_message).each do |field|
13
+ redis.rpush filter_key(field, info_hash[field.to_sym]), redis_key
14
+ end
15
+ end
16
+
17
+ def redis
18
+ @redis ||= begin
19
+ unless $redis_global_exception_handler.is_a? Redis
20
+ redis_config = YAML.load_file(File.join(Rails.root, 'config', 'redis.yml'))[Rails.env]
21
+ $redis_global_exception_handler = Redis.new(redis_config['global_exception_handler'])
22
+ end
23
+ $redis_global_exception_handler
24
+ end
25
+ end
26
+
27
+ def current_id
28
+ redis.get(CURRENT_ID_KEY)
29
+ end
30
+
31
+ # def sort(field, direction = 'ASC', page = 0, per_page = 1000)
32
+ # redis.sort(EXCEPTIONS_REDIS_KEY, by: "#{EXCEPTION_KEY_PREFIX}_*->#{field}_*", order: "#{direction}", limit: [page, per_page])
33
+ # end
34
+
35
+ def exceptions_count
36
+ redis.llen EXCEPTIONS_REDIS_KEY
37
+ end
38
+
39
+ def filtered_exceptions_count(field, filter)
40
+ redis.llen filter_key(field, filter)
41
+ end
42
+
43
+ def exception_keys(page = 0, per_page = 10)
44
+ redis.lrange EXCEPTIONS_REDIS_KEY, page.to_i, per_page.to_i + page.to_i - 1
45
+ end
46
+
47
+ def filter_exception_keys(page = 0, field = nil, filter = nil, per_page = 10)
48
+ redis.lrange filter_key(field, filter), page.to_i, per_page.to_i + page.to_i - 1
49
+ end
50
+
51
+ def filter_keys_for(field)
52
+ redis.keys filter_key(field, '*')
53
+ end
54
+
55
+ def find(key)
56
+ Hashie::Mash.new redis.hgetall(key)
57
+ end
58
+
59
+ def find_all(keys)
60
+ keys.map { |key| find(key) }
61
+ end
62
+
63
+ def delete(key)
64
+ redis.lrem EXCEPTIONS_REDIS_KEY, 1, key
65
+ clear_filters key
66
+ redis.del key
67
+ end
68
+
69
+ def delete_all(keys)
70
+ keys.each { |key| delete(key) rescue next }
71
+ end
72
+
73
+ def truncate!
74
+ redis.flushdb
75
+ end
76
+
77
+ def exception_key(id = current_id)
78
+ "#{EXCEPTION_KEY_PREFIX}:#{id}"
79
+ end
80
+
81
+ def filter_key(field, filter)
82
+ "#{FILTER_KEY_PREFIX}:#{field}:#{filter}"
83
+ end
84
+
85
+ protected
86
+
87
+ def next_id!
88
+ redis.incr(CURRENT_ID_KEY)
89
+ end
90
+
91
+ def clear_filters(key)
92
+ %w(error_class error_message).each do |field|
93
+ filter_keys_for(field).each do |filter_key|
94
+ redis.lrem filter_key, 1, key
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,8 @@
1
+ .commands li { display: inline-block; }
2
+ .commands li.filter {
3
+ float: right;
4
+ }
5
+
6
+ .commands li.filter select {
7
+ max-width: 300px;
8
+ }
@@ -0,0 +1,46 @@
1
+ $(document).ready(function() {
2
+ $('.js-link').unbind('click');
3
+ $('.js-link').bind('click', function(event) {
4
+ event.preventDefault();
5
+ event.stopPropagation();
6
+ var h = document.createElement('input');
7
+ h.type = 'hidden';
8
+ h.name = '_method';
9
+ h.value = this.getAttribute('data-method');
10
+ var f = document.createElement('form');
11
+ f.appendChild(h);
12
+ f.style.display = 'none';
13
+
14
+ if(this.getAttribute('data-get-ids')){
15
+ checkboxes = $('.select-exception:checked').clone();
16
+ if(checkboxes.length == 0) {
17
+ alert('Select at least one exception to delete please.');
18
+ return false;
19
+ }
20
+ $(f).append(checkboxes);
21
+ }
22
+
23
+ this.parentNode.appendChild(f);
24
+ f.method = 'POST';
25
+ f.action = this.getAttribute('href');
26
+ if(confirm(this.getAttribute('data-confirm'))) {
27
+ f.submit();
28
+ } else {
29
+ f.parentNode.removeChild(f);
30
+ }
31
+ return false;
32
+ });
33
+
34
+ $('.select-all-exceptions').click(function() {
35
+ $('.select-exception').attr('checked', $(this).attr('checked'));
36
+ });
37
+
38
+ $('.filter-by').change(function() {
39
+ selected = $(this).val();
40
+ field = $(this).attr('data-field');
41
+ if(selected == '')
42
+ window.location = '/resque/exceptions'
43
+ else
44
+ window.location = '/resque/exceptions/filter/' + field + '/' + encodeURIComponent(encodeURIComponent(selected));
45
+ });
46
+ })
@@ -0,0 +1,45 @@
1
+ - filter_suffix = "filter by Error #{params[:filter_by].capitalize}: #{get_filter}" if params[:filter_by]
2
+ %h1 App Exceptions
3
+ %p.sub #{filter_suffix}
4
+ #app_exceptions
5
+ .commands
6
+ %ul
7
+ %li= link_to 'Delete selected', exception_path('delete', params[:start], params[:filter_by], params[:filter]),
8
+ method: :delete, class: 'js-link',
9
+ data: {confirm: 'Are you sure to delete selected Exceptions?', get_ids: true}
10
+ %li= link_to 'Truncate all', exceptions_path(nil, params[:filter_by], params[:filter]) + '/truncate',
11
+ method: :delete, class: 'js-link',
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'}
17
+
18
+ %p.sub
19
+ Showing #{apps_start_at} to #{apps_end_at} of
20
+ %b #{apps_size}
21
+ exceptions
22
+
23
+ %table
24
+ %thead
25
+ %tr
26
+ %th= check_box_tag nil, nil, false, class: 'select-all-exceptions'
27
+ %th Timestamp
28
+ %th Error Class
29
+ %th Error Message
30
+ %th Error Trace
31
+ %th
32
+ %tbody
33
+ - each_app_exception do |app_exception|
34
+ %tr
35
+ %td= check_box_tag 'app_exception_delete_ids[]', app_exception.id, false, class: 'select-exception', id: "select-exception-#{app_exception.id}"
36
+ %td= link_to app_exception.timestamp, exception_path(app_exception.id)
37
+ %td= link_to app_exception.error_class, exception_path(app_exception.id)
38
+ %td= simple_format app_exception.error_message
39
+ %td= simple_format app_exception.error_trace.try(:split, "\n").try(:[], 0..4).try(:join, "\n")
40
+ %td= link_to 'Delete', exception_path(app_exception.id, params[:start], params[:filter_by], params[:filter]), method: :delete,
41
+ data: {confirm: 'Are you sure to remove an Exception?'}, class: 'js-link'
42
+ %p.pagination= pagination(start: apps_start_at - 1, total: apps_size)
43
+ %script{type: 'text/javascript', src: '/resque/exceptions?js=global_error_handler.js'}
44
+ %link{type: 'text/css', href: '/resque/style.css', rel: 'stylesheet'}
45
+ %link{type: 'text/css', href: '/resque/exceptions?css=global_error_handler.css', rel: 'stylesheet'}
@@ -0,0 +1,28 @@
1
+ %h1 #{@app_exception.error_class} at #{@app_exception.timestamp} details
2
+
3
+ %table
4
+ %tr
5
+ %th Message:
6
+ %td= simple_format @app_exception.error_message
7
+ %tr
8
+ %th Trace:
9
+ %td= simple_format @app_exception.error_trace
10
+ %tr
11
+ %th Request method:
12
+ %td= @app_exception.request_method
13
+ %tr
14
+ %th Request parameters:
15
+ %td= simple_format @app_exception.request_params
16
+ %tr
17
+ %th Target URL:
18
+ %td= simple_format @app_exception.target_url
19
+ %tr
20
+ %th Referer:
21
+ %td= simple_format @app_exception.referer_url
22
+ %tr
23
+ %th User Agent:
24
+ %td= simple_format @app_exception.user_agent
25
+ %tr
26
+ %th User Info:
27
+ %td= simple_format @app_exception.user_info
28
+
@@ -0,0 +1,3 @@
1
+ module GlobalErrorHandler
2
+ VERSION = "0.0.2"
3
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: global_error_handler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Andrii Rudenko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.3'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 10.3.2
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '10.3'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 10.3.2
47
+ - !ruby/object:Gem::Dependency
48
+ name: resque
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.25'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 1.25.1
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '1.25'
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 1.25.1
67
+ - !ruby/object:Gem::Dependency
68
+ name: haml
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '4.0'
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: 4.0.5
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '4.0'
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 4.0.5
87
+ description: On the middleware level catch an exception from Rails app and store in
88
+ the separated Redis database.
89
+ email:
90
+ - kolobock@gmail.com
91
+ executables: []
92
+ extensions: []
93
+ extra_rdoc_files: []
94
+ files:
95
+ - ".gitignore"
96
+ - Gemfile
97
+ - LICENSE.txt
98
+ - README.md
99
+ - Rakefile
100
+ - config/redis_example.yml
101
+ - global_error_handler.gemspec
102
+ - lib/global_error_handler.rb
103
+ - lib/global_error_handler/app_exception.rb
104
+ - lib/global_error_handler/global_error_handler.rb
105
+ - lib/global_error_handler/handler.rb
106
+ - lib/global_error_handler/middleware.rb
107
+ - lib/global_error_handler/parser.rb
108
+ - lib/global_error_handler/rails.rb
109
+ - lib/global_error_handler/redis.rb
110
+ - lib/global_error_handler/server/public/css/global_error_handler.css
111
+ - lib/global_error_handler/server/public/js/global_error_handler.js
112
+ - lib/global_error_handler/server/views/index.html.haml
113
+ - lib/global_error_handler/server/views/show.html.haml
114
+ - lib/global_error_handler/version.rb
115
+ homepage: https://github.com/kolobock/global_error_handler/
116
+ licenses:
117
+ - MIT
118
+ metadata: {}
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 2.2.2
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: Records application' exceptions into the separated redis database.
139
+ test_files: []