Commit 3dde7609e7c14ead995a1670d15029f8b1bc36e9
0 parents
Exists in
master
first commit
Showing
20 changed files
with
616 additions
and
0 deletions
Show diff stats
1 | +++ a/.travis.yml | ||
@@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
1 | +language: ruby | ||
2 | +cache: bundler | ||
3 | + | ||
4 | +before_install: gem install bundler | ||
5 | + | ||
6 | +rvm: | ||
7 | + - 3.0.0 | ||
8 | + - 2.7.2 | ||
9 | + - 2.6.6 | ||
10 | + - 2.5.8 | ||
11 | + - 2.4.10 | ||
12 | +gemfile: | ||
13 | + - gemfiles/Gemfile.rails_6.1 | ||
14 | + - gemfiles/Gemfile.rails_6.0 | ||
15 | + - gemfiles/Gemfile.rails_5.2 | ||
16 | +jobs: | ||
17 | + exclude: | ||
18 | + - rvm: 3.0.0 | ||
19 | + gemfile: gemfiles/Gemfile.rails_5.2.4 | ||
20 | + - rvm: 2.4.10 | ||
21 | + gemfile: gemfiles/Gemfile.rails_6.0 | ||
22 | + - rvm: 2.4.10 | ||
23 | + gemfile: gemfiles/Gemfile.rails_6.1 |
1 | +++ a/LICENSE.txt | ||
@@ -0,0 +1,21 @@ | @@ -0,0 +1,21 @@ | ||
1 | +The MIT License (MIT) | ||
2 | + | ||
3 | +Copyright (c) 2017 Koji Onishi | ||
4 | + | ||
5 | +Permission is hereby granted, free of charge, to any person obtaining a copy | ||
6 | +of this software and associated documentation files (the "Software"), to deal | ||
7 | +in the Software without restriction, including without limitation the rights | ||
8 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
9 | +copies of the Software, and to permit persons to whom the Software is | ||
10 | +furnished to do so, subject to the following conditions: | ||
11 | + | ||
12 | +The above copyright notice and this permission notice shall be included in | ||
13 | +all copies or substantial portions of the Software. | ||
14 | + | ||
15 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
18 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
20 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
21 | +THE SOFTWARE. |
1 | +++ a/README.md | ||
@@ -0,0 +1,93 @@ | @@ -0,0 +1,93 @@ | ||
1 | +# Exception File Notifier | ||
2 | + | ||
3 | +[](https://badge.fury.io/rb/exception_file_notifier) | ||
4 | +[](http://travis-ci.org/fursich/exception_file_notifier) | ||
5 | +[](LICENSE) | ||
6 | + | ||
7 | +**Exception File Notifier** is a custom notifier for [Exception Notification](https://github.com/smartinez87/exception_notification), that records notifications onto a log file when errors occur in a Rack/Rails application. | ||
8 | + | ||
9 | +All the error logs are converted into JSON format. This would be useful typically when you wish to monitor an app with monitoring tools - e.g. Kibana + ElasticSearch + Fluentd, or anything alike. | ||
10 | + | ||
11 | +## Installation | ||
12 | + | ||
13 | +Add this line to your application's Gemfile: | ||
14 | + | ||
15 | +```ruby | ||
16 | +gem 'exception_file_notifier' | ||
17 | +``` | ||
18 | + | ||
19 | +And then execute: | ||
20 | + | ||
21 | + $ bundle | ||
22 | + | ||
23 | +Or install it yourself as: | ||
24 | + | ||
25 | + $ gem install exception_file_notifier | ||
26 | + | ||
27 | +## Usage | ||
28 | + | ||
29 | +Set proper configs in *config/initializers/exception_notification.rb* | ||
30 | + | ||
31 | +To get started, use the simplest settings: | ||
32 | + | ||
33 | +```ruby:config/initializers/exception_notification.rb | ||
34 | +ExceptionNotification.configure do |config| | ||
35 | + | ||
36 | + config.add_notifier :file, { | ||
37 | + filename: "#{Rails.root}/log/exceptions.log" | ||
38 | + } | ||
39 | +``` | ||
40 | + | ||
41 | +That works in all the environment (including test). | ||
42 | + | ||
43 | +You could also customize settings like so: | ||
44 | + | ||
45 | +```ruby:config/initializers/exception_notification.rb | ||
46 | +ExceptionNotification.configure do |config| | ||
47 | + | ||
48 | +# disable all the notifiers in certain environment | ||
49 | + config.ignore_if do |exception, options| | ||
50 | + Rails.env.test? | ||
51 | + end | ||
52 | + | ||
53 | + config.add_notifier :file, { | ||
54 | + filename: "#{Rails.root}/log/exceptions_#{Rails.env}.log", # generate different log files depending on environments | ||
55 | + shift_age: 'daily' # use shift_age/shift_size options to rotate log files | ||
56 | + } | ||
57 | +``` | ||
58 | + | ||
59 | +You could also pass as many original values as you like, which will be evaluated JSON-ified at the time when an exception occurs. For detailed setting you may wish to consult with the Exception Notification [original readme](https://github.com/smartinez87/exception_notification). | ||
60 | + | ||
61 | +#### available options: | ||
62 | + | ||
63 | +- filename: specify the log file (preferablly with its absolute path) | ||
64 | + | ||
65 | +- shift_age: option for log file rotation: directly passed to Ruby Logger | ||
66 | + | ||
67 | +- shift_size: option for log file rotation: directly passed to Ruby Logger | ||
68 | + | ||
69 | +(for the latter options see also: https://docs.ruby-lang.org/ja/latest/method/Logger/s/new.html) | ||
70 | + | ||
71 | +#### Note | ||
72 | + | ||
73 | +Due to [a bug](https://bugs.ruby-lang.org/issues/12948) with ruby Logger discovered in ruby 2.2 - 2.3, it might happen that you cannot rotate logs by using shift_age. (for those who come up with any workaround for this, please let us know / PR are welcomed) | ||
74 | + | ||
75 | +Meanwhile you could cope with either 1) upgrading your ruby version upto 2.4, or 2) rotate logs by size, not date | ||
76 | + | ||
77 | +## Development | ||
78 | + | ||
79 | +After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. | ||
80 | + | ||
81 | +To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). | ||
82 | + | ||
83 | +## Contributing | ||
84 | + | ||
85 | +Bug reports and pull requests are welcome on GitHub at https://github.com/fursich/exception_file_notifier. | ||
86 | + | ||
87 | +## Special Thanks To | ||
88 | + | ||
89 | +All the folks who have given supports, especially @motchang and @katsurak for great advises and reviews. | ||
90 | + | ||
91 | +## License | ||
92 | + | ||
93 | +The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). |
1 | +++ a/bin/console | ||
@@ -0,0 +1,14 @@ | @@ -0,0 +1,14 @@ | ||
1 | +#!/usr/bin/env ruby | ||
2 | + | ||
3 | +require "bundler/setup" | ||
4 | +require "exception_file_notifier" | ||
5 | + | ||
6 | +# You can add fixtures and/or initialization code here to make experimenting | ||
7 | +# with your gem easier. You can also use a different console, if you like. | ||
8 | + | ||
9 | +# (If you use this, don't forget to add pry to your Gemfile!) | ||
10 | +# require "pry" | ||
11 | +# Pry.start | ||
12 | + | ||
13 | +require "irb" | ||
14 | +IRB.start(__FILE__) |
1 | +++ a/exception_file_notifier.gemspec | ||
@@ -0,0 +1,42 @@ | @@ -0,0 +1,42 @@ | ||
1 | +# coding: utf-8 | ||
2 | +lib = File.expand_path('../lib', __FILE__) | ||
3 | +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) | ||
4 | +require 'exception_notifier/exception_file_notifier/version' | ||
5 | + | ||
6 | +Gem::Specification.new do |spec| | ||
7 | + spec.name = "exception_file_notifier" | ||
8 | + spec.version = ::ExceptionNotifier::ExceptionFileNotifier::VERSION | ||
9 | + spec.authors = ["Koji Onishi"] | ||
10 | + spec.email = ["fursich0@gmail.com"] | ||
11 | + | ||
12 | + spec.summary = %q{ A custom notifier for ExceptionNotification that generates exception log files in JSON format. } | ||
13 | + spec.description = %q{ Exception File Notifier records exception logs in JSON format, helping you track errors in the production environment. } | ||
14 | + spec.homepage = "https://github.com/fursich/exception_file_notifier" | ||
15 | + spec.license = "MIT" | ||
16 | + | ||
17 | + # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' | ||
18 | + # to allow pushing to a single host or delete this section to allow pushing to any host. | ||
19 | + # if spec.respond_to?(:metadata) | ||
20 | + # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com' | ||
21 | + # else | ||
22 | + # raise "RubyGems 2.0 or newer is required to protect against " \ | ||
23 | + # "public gem pushes." | ||
24 | + # end | ||
25 | + | ||
26 | + spec.files = `git ls-files -z`.split("\x0").reject do |f| | ||
27 | + f.match(%r{^(test|spec|features)/}) | ||
28 | + end | ||
29 | + spec.bindir = "exe" | ||
30 | + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } | ||
31 | + spec.require_paths = ["lib"] | ||
32 | + | ||
33 | + spec.required_ruby_version = '>= 2.3' | ||
34 | + spec.required_rubygems_version = '>= 1.8.11' | ||
35 | + spec.add_dependency "exception_notification", ">= 4.0" | ||
36 | + spec.add_dependency("activesupport", ">= 4.0", "<7") | ||
37 | + | ||
38 | + spec.add_development_dependency "bundler", ">= 2" | ||
39 | + spec.add_development_dependency "rake", ">= 10.0" | ||
40 | + spec.add_development_dependency "rspec", ">= 3.10" | ||
41 | + spec.add_development_dependency "pry" | ||
42 | +end |
lib/exception_notifier/exception_file_notifier/version.rb
0 → 100644
1 | +++ a/lib/exception_notifier/file_notifier.rb | ||
@@ -0,0 +1,155 @@ | @@ -0,0 +1,155 @@ | ||
1 | +require "exception_notification" | ||
2 | +require "action_dispatch" | ||
3 | +require "active_support/core_ext/hash" | ||
4 | +require "socket" | ||
5 | +require 'logger' | ||
6 | +require 'json' | ||
7 | + | ||
8 | +module ExceptionNotifier | ||
9 | + class FileNotifier < BaseNotifier | ||
10 | + include ExceptionNotifier::BacktraceCleaner | ||
11 | + | ||
12 | + def initialize(options={}) | ||
13 | + filename = options[:filename] || "log/error.log" | ||
14 | + shift_age = options[:shift_age] || 0 | ||
15 | + shift_size = options[:shift_size] || nil | ||
16 | + | ||
17 | + @logger_options = [filename, shift_age, shift_size] | ||
18 | + @formatter = -> (_, _, _, message) { message + "\n" } | ||
19 | + end | ||
20 | + | ||
21 | + def call(exception=nil, options={}) | ||
22 | + append_log exception_message(options[:env], exception, options) | ||
23 | + end | ||
24 | + | ||
25 | + def append_log(message) | ||
26 | + log = ::Logger.new(*@logger_options) | ||
27 | + log.formatter = @formatter | ||
28 | + error_log = generate_json(message) | ||
29 | + log.error(error_log) | ||
30 | + log.close | ||
31 | + end | ||
32 | + | ||
33 | + def generate_json(message) | ||
34 | + ::JSON.generate(deep_encode(message)) | ||
35 | + end | ||
36 | + | ||
37 | + def exception_message(env, exception, options={}) | ||
38 | + return error_message(exception) unless exception.is_a?(Exception) | ||
39 | + | ||
40 | + if env.present? | ||
41 | + exception_notification(options[:env], exception, options) | ||
42 | + else | ||
43 | + background_exception_notification(exception, options) | ||
44 | + end | ||
45 | + end | ||
46 | + | ||
47 | + def exception_notification(env, exception, options) | ||
48 | + @env = env | ||
49 | + @exception = exception | ||
50 | + @options = options.reverse_merge(@env['exception_notifier.options'] || {}).symbolize_keys | ||
51 | + @kontroller = @env['action_controller.instance'] || MissingController.new | ||
52 | + @request = ::ActionDispatch::Request.new(@env) | ||
53 | + @backtrace = exception.backtrace ? clean_backtrace(exception) : [] | ||
54 | + @timestamp = ::Time.current | ||
55 | + @data = (@env['exception_notifier.exception_data'] || {}).merge(options[:data] || {}) | ||
56 | + | ||
57 | + render_notice | ||
58 | + end | ||
59 | + | ||
60 | + def background_exception_notification(exception, options) | ||
61 | + @exception = exception | ||
62 | + @options = options.symbolize_keys | ||
63 | + @backtrace = exception.backtrace || [] | ||
64 | + @timestamp = ::Time.current | ||
65 | + @data = options[:data] || {} | ||
66 | + @env = @kontroller = nil | ||
67 | + | ||
68 | + render_notice | ||
69 | + end | ||
70 | + | ||
71 | + def error_message(exception) | ||
72 | + { | ||
73 | + message: '[FATAL] NO EXCEPTION GIVEN: Please provide Exception as argument', | ||
74 | + exception: exception.try(:to_s), | ||
75 | + timestamp: ::Time.current, | ||
76 | + } | ||
77 | + end | ||
78 | + | ||
79 | + def render_notice | ||
80 | + set_data_variables | ||
81 | + | ||
82 | + exception_details = { | ||
83 | + exception: @exception.class, | ||
84 | + controller: @kontroller.present? ? @kontroller.controller_name : '[BACKGROUND]', | ||
85 | + action: @kontroller.present? ? @kontroller.action_name : '[BACKGROUND]', | ||
86 | + exception_message: @exception.message, | ||
87 | + | ||
88 | + timestamp: @timestamp, | ||
89 | + server: ::Socket.gethostname, | ||
90 | + rails_root: (defined?(::Rails) && ::Rails.respond_to?(:root) ) ? ::Rails.root : nil, | ||
91 | + process: $$, | ||
92 | + backtrace: @backtrace, | ||
93 | + } | ||
94 | + | ||
95 | + if @env.present? | ||
96 | + exception_details.merge!({ | ||
97 | + url: @request.url, | ||
98 | + http_method: @request.request_method, | ||
99 | + ip_address: @request.remote_ip, | ||
100 | + parameters: (@request.filtered_parameters.inspect rescue "[NOT ENCODABLE]"), | ||
101 | + session_id: (@request.ssl? ? "[FILTERED]" : @request.session['session_id'] || (@request.env["rack.session.options"] and @request.env["rack.session.options"][:id])), | ||
102 | + session_data: @request.session.to_hash, | ||
103 | + }) | ||
104 | + filtered_env = @request.filtered_env | ||
105 | + filtered_env.sort_by { |key, _| key.to_s }.each do |key, value| | ||
106 | + exception_details[key] = value | ||
107 | + end | ||
108 | + end | ||
109 | + | ||
110 | + exception_details.merge!({ | ||
111 | + data: @data, | ||
112 | + }) | ||
113 | + | ||
114 | + exception_details | ||
115 | + end | ||
116 | + | ||
117 | + class MissingController | ||
118 | + def controller_name | ||
119 | + '[NO CONTROLLER]' | ||
120 | + end | ||
121 | + def action_name | ||
122 | + '[NO CONTROLLER]' | ||
123 | + end | ||
124 | + def method_missing(*args, &block) | ||
125 | + end | ||
126 | + end | ||
127 | + | ||
128 | + def set_data_variables | ||
129 | + @data.each do |name, value| | ||
130 | + instance_variable_set("@#{name}", value) | ||
131 | + end | ||
132 | + end | ||
133 | + | ||
134 | + def deep_encode(obj) | ||
135 | + case obj | ||
136 | + when Array | ||
137 | + obj.map { |o| deep_encode(o) } | ||
138 | + when Hash | ||
139 | + deep_encode(obj.to_a).to_h | ||
140 | + when String | ||
141 | + encode_to_utf8(obj) | ||
142 | + else | ||
143 | + obj.respond_to?(:to_s) ? encode_to_utf8(obj.to_s) : '[NOT ENCODABLE]' | ||
144 | + end | ||
145 | + end | ||
146 | + | ||
147 | + def encode_to_utf8(str) | ||
148 | + quick_sanitization(str).encode(::Encoding.find('UTF-8'), invalid: :replace, undef: :replace, replace: '?') | ||
149 | + end | ||
150 | + | ||
151 | + def quick_sanitization(str) # stringify any random objects in a safe (and convenient) manner | ||
152 | + str.inspect.gsub(/\A\"(.*)\"\z/,'\1') | ||
153 | + end | ||
154 | + end | ||
155 | +end |
1 | +++ a/spec/exception_notifier/exception_file_notifier_spec.rb | ||
@@ -0,0 +1,7 @@ | @@ -0,0 +1,7 @@ | ||
1 | +require "spec_helper" | ||
2 | + | ||
3 | +RSpec.describe ExceptionNotifier::ExceptionFileNotifier do | ||
4 | + it "has a version number" do | ||
5 | + expect(ExceptionNotifier::ExceptionFileNotifier::VERSION).not_to be nil | ||
6 | + end | ||
7 | +end |
1 | +++ a/spec/exception_notifier/file_notifier_spec.rb | ||
@@ -0,0 +1,161 @@ | @@ -0,0 +1,161 @@ | ||
1 | +require "spec_helper" | ||
2 | +require "exception_test_helper" | ||
3 | + | ||
4 | +RSpec.describe ExceptionNotifier::FileNotifier do | ||
5 | + include ExceptionTestHelper | ||
6 | + | ||
7 | + let(:notifier) { | ||
8 | + ExceptionNotifier::FileNotifier.new( | ||
9 | + options | ||
10 | + ) | ||
11 | + } | ||
12 | + | ||
13 | + let(:options) { | ||
14 | + { | ||
15 | + filename: filename, | ||
16 | + shift_age: shift_age, | ||
17 | + shift_size: shift_size, | ||
18 | + }.compact | ||
19 | + } | ||
20 | + | ||
21 | + let(:filename) { 'log/exceptions.log' } | ||
22 | + let(:shift_age) { 'daily' } | ||
23 | + let(:shift_size) { nil } | ||
24 | + | ||
25 | + describe '#initialize' do | ||
26 | + let(:logger_options) { | ||
27 | + notifier.instance_eval {@logger_options} | ||
28 | + } | ||
29 | + it 'constructs the right file name based on given options' do | ||
30 | + expect(logger_options).to contain_exactly(filename, shift_age, shift_size) | ||
31 | + end | ||
32 | + end | ||
33 | + | ||
34 | + describe '#generate_json' do | ||
35 | + context 'in case broken strings are given' do | ||
36 | + let (:hash_with_broken_codes) { | ||
37 | + { | ||
38 | + French: 'La langue française'.force_encoding('ASCII'), | ||
39 | + Russian: 'Русский язык'.encode('WINDOWS-31J').force_encoding('UTF-16'), | ||
40 | + Japanese: {'日本語'.encode('UTF-8').force_encoding('WINDOWS-31J') => 'にほんご'.force_encoding('ASCII') }, | ||
41 | + } | ||
42 | + } | ||
43 | + | ||
44 | + it 'jasonifies the strings properly without causing errors' do | ||
45 | + expect( JSON.parse(notifier.generate_json(hash_with_broken_codes)) ).to eq( | ||
46 | + { | ||
47 | + "French"=>"La langue fran\\xC3\\xA7aise", | ||
48 | + "Russian"=>"\\x84\\x51\\x84\\x85\\x84\\x83\\x84\\x83\\x84\\x7B\\x84\\x79\\x84\\x7A\\x20\\x84\\x91\\x84\\x78\\x84\\x8D\\x84\\x7B", | ||
49 | + "Japanese" => {"\\x{E697}\\xA5\\x{E69C}\\xAC\\x{E8AA}\\x9E"=>"\\xE3\\x81\\xAB\\xE3\\x81\\xBB\\xE3\\x82\\x93\\xE3\\x81\\x94"}, | ||
50 | + } | ||
51 | + ) | ||
52 | + end | ||
53 | + end | ||
54 | + end | ||
55 | + | ||
56 | + describe '#call' do | ||
57 | + let (:backtrace) { | ||
58 | + [ | ||
59 | + %(/test/app/controllers/api/dummy_controller.rb:123:in `build_whatever_params'), | ||
60 | + %(/test/app/controllers/api/dummy_controller.rb:321:in `any_random_loop'), | ||
61 | + ] | ||
62 | + } | ||
63 | + | ||
64 | + context 'with env options' do | ||
65 | + let (:env) { | ||
66 | + { "rack.version" => [1, 2], | ||
67 | + "action_controller.instance" => ExceptionTestHelper::DummyController.new, | ||
68 | + "REQUEST_METHOD" => "GET", | ||
69 | + "SERVER_NAME" => "example.org", | ||
70 | + "SERVER_PORT" => "80", | ||
71 | + "PATH_INFO" => "/category", | ||
72 | + "rack.url_scheme" => "http", | ||
73 | + "REMOTE_ADDR" => "127.0.0.1", | ||
74 | + "HTTP_HOST" => "example.org", | ||
75 | + } | ||
76 | + } | ||
77 | + | ||
78 | + it 'indicates the exception' do | ||
79 | + expect(notifier).to receive(:append_log).with( | ||
80 | + hash_including( | ||
81 | + exception: ExceptionTestHelper::DummyException, | ||
82 | + exception_message: 'Dummy Exception Message' | ||
83 | + ) | ||
84 | + ).once | ||
85 | + notifier.call(ExceptionTestHelper::DummyException.new(backtrace), env: env) | ||
86 | + end | ||
87 | + | ||
88 | + it 'provides controller and action name' do | ||
89 | + expect(notifier).to receive(:append_log).with( | ||
90 | + hash_including( | ||
91 | + controller: 'DummyController', | ||
92 | + action: 'TestAction' | ||
93 | + ) | ||
94 | + ).once | ||
95 | + notifier.call(ExceptionTestHelper::DummyException.new(backtrace), env: env) | ||
96 | + end | ||
97 | + | ||
98 | + it 'provides url' do | ||
99 | + expect(notifier).to receive(:append_log).with( | ||
100 | + hash_including( | ||
101 | + url: "http://example.org/category" | ||
102 | + ) | ||
103 | + ).once | ||
104 | + notifier.call(ExceptionTestHelper::DummyException.new(backtrace), env: env) | ||
105 | + end | ||
106 | + | ||
107 | + it 'provides backtrace' do | ||
108 | + expect(notifier).to receive(:append_log).with( | ||
109 | + hash_including(backtrace: backtrace) | ||
110 | + ).once | ||
111 | + notifier.call(ExceptionTestHelper::DummyException.new(backtrace), env: env) | ||
112 | + end | ||
113 | + end | ||
114 | + | ||
115 | + context 'without controller' do | ||
116 | + let (:env) { | ||
117 | + { "rack.version" => [1, 2], | ||
118 | + "REQUEST_METHOD" => "GET", | ||
119 | + "SERVER_NAME" => "example.org", | ||
120 | + "SERVER_PORT" => "80", | ||
121 | + "PATH_INFO" => "/category", | ||
122 | + "rack.url_scheme" => "http", | ||
123 | + "REMOTE_ADDR" => "127.0.0.1", | ||
124 | + "HTTP_HOST" => "example.org", | ||
125 | + } | ||
126 | + } | ||
127 | + it 'does not provide controller and action name' do | ||
128 | + expect(notifier).to receive(:append_log).with( | ||
129 | + hash_including( | ||
130 | + controller: '[NO CONTROLLER]', | ||
131 | + action: '[NO CONTROLLER]' | ||
132 | + ) | ||
133 | + ).once | ||
134 | + notifier.call(ExceptionTestHelper::DummyException.new(backtrace), env: env) | ||
135 | + end | ||
136 | + end | ||
137 | + | ||
138 | + context 'without env options' do | ||
139 | + it 'indicates the exception' do | ||
140 | + expect(notifier).to receive(:append_log).with( | ||
141 | + hash_including( | ||
142 | + exception: ExceptionTestHelper::DummyException, | ||
143 | + exception_message: 'Dummy Exception Message' | ||
144 | + ) | ||
145 | + ) | ||
146 | + notifier.call(ExceptionTestHelper::DummyException.new) | ||
147 | + end | ||
148 | + | ||
149 | + it 'does not provide controller and action name' do | ||
150 | + expect(notifier).to receive(:append_log).with( | ||
151 | + hash_including( | ||
152 | + controller: '[BACKGROUND]', | ||
153 | + action: '[BACKGROUND]' | ||
154 | + ) | ||
155 | + ) | ||
156 | + notifier.call(ExceptionTestHelper::DummyException.new) | ||
157 | + end | ||
158 | + end | ||
159 | + end | ||
160 | + | ||
161 | +end |
1 | +++ a/spec/exception_test_helper.rb | ||
@@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
1 | +module ExceptionTestHelper | ||
2 | + | ||
3 | + class DummyException < Exception | ||
4 | + def initialize(backtrace = nil) | ||
5 | + super | ||
6 | + set_backtrace(backtrace) | ||
7 | + end | ||
8 | + | ||
9 | + def message | ||
10 | + 'Dummy Exception Message' | ||
11 | + end | ||
12 | + end | ||
13 | + | ||
14 | + class DummyController | ||
15 | + def controller_name | ||
16 | + 'DummyController' | ||
17 | + end | ||
18 | + | ||
19 | + def action_name | ||
20 | + 'TestAction' | ||
21 | + end | ||
22 | + end | ||
23 | +end |
1 | +++ a/spec/spec_helper.rb | ||
@@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
1 | +require "bundler/setup" | ||
2 | +require "exception_file_notifier" | ||
3 | +require 'exception_notification' | ||
4 | + | ||
5 | +if RUBY_VERSION >= '2.7.2' | ||
6 | + # NOTE: https://bugs.ruby-lang.org/issues/17000 | ||
7 | + # this will keep us informed of deprecation warnings after Ruby 2.7.2 | ||
8 | + Warning[:deprecated] = true | ||
9 | +end | ||
10 | + | ||
11 | +RSpec.configure do |config| | ||
12 | + # Enable flags like --only-failures and --next-failure | ||
13 | + config.example_status_persistence_file_path = ".rspec_status" | ||
14 | + | ||
15 | + config.expect_with :rspec do |c| | ||
16 | + c.syntax = :expect | ||
17 | + end | ||
18 | +end | ||
19 | + | ||
20 | +# ENV["RAILS_ENV"] = "test" |