exceptions_begone_notifier 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ /pkg
2
+ *.gemspec
3
+ coverage/*
4
+ /rdoc
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 {XING AG}[http://www.xing.com/]
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.
data/Manifest ADDED
File without changes
data/README.rdoc ADDED
@@ -0,0 +1,96 @@
1
+ =ExceptionsBegoneIdentifier
2
+
3
+ This gem catch all exceptions from a Rails application and send them
4
+ to a ExceptionsBegone server. Actually it doesn't has to be an exception.
5
+ You can easily send *any* *kind* *of* *notification* and let the server aggregate it for you. It could be for example
6
+ a cron or nagios message.
7
+
8
+ This project is currently under development, however it is stable enough to use
9
+ it in production environment.
10
+
11
+ ==Prerequisites
12
+
13
+ This gem is <tt>require 'rubygems'</tt> free. See {here}[http://tomayko.com/writings/require-rubygems-antipattern] for explanation.
14
+ If you want to use rubygems as a gems source do <tt>export RUBYOPT="rubygems"</tt>
15
+
16
+ This gem send informations to an {Exceptions Begone Server}[http://github.com/xing/exceptions_begone].
17
+ See the README for the installation guide.
18
+
19
+ The gem relies that the Rails.logger responds to <tt>error</tt> method (which is a default behavior in Rails applications).
20
+
21
+ ==Downloading gem
22
+
23
+ <tt>gem sources -a http://gemcutter.org</tt>
24
+
25
+ or if you have the gemcutter gem installed:
26
+
27
+ <tt>gem install exceptions_begone_notifier</tt>
28
+
29
+ ==Enabling functionality
30
+
31
+ Add <tt>require "exceptions_begone_notifier"</tt> in the proper place (e.g. in <tt>config/environment.rb</tt>).
32
+
33
+ To activate the gem add following lines to you application configuration file (e.g. <tt>config/environments/production.rb</tt>).
34
+
35
+ ExceptionsBegone::Catcher.catch_exceptions do |catcher|
36
+ catcher.host = "exceptions_begone_server"
37
+ end
38
+
39
+ Sender expects following parameters for configuration:
40
+ * <tt>project</tt>: name of project (this project has to exist on ExceptionsBegone server)
41
+ * <tt>host</tt>: host name of the ExceptionsBegone server
42
+ * <tt>port</tt>: same as above
43
+ * <tt>open_timeout</tt>: http timeout (in sec)
44
+ * <tt>read_timeout</tt>: same as above
45
+
46
+ ==Usage
47
+
48
+ After activation all exceptions will be send via HTTP POST (JSON) to the ExceptionsBegone server.
49
+
50
+ If you want to send a notification use:
51
+
52
+ <tt>ExceptionsBegone::Sender.send_YOUR_CATEGORY_GOES_HERE({:identifier => "NotificationName", :payload => {:anything => "you want"}})</tt>
53
+
54
+ or
55
+
56
+ <tt>ExceptionsBegone::Sender.send_YOUR_CATEGORY_GOES_HERE({:identifier => "NotificationName", :payload => {:anything => "you want"}}, :host => "new host address", :port => "new port")</tt>
57
+
58
+ if want to override the settings from the ExceptionsBegone::Catcher. As you can see the category of your notification (e.g. nagios_message or warning)
59
+ is taken from the method name: "send_ *category*()".
60
+
61
+ ==Throttling traffic between servers
62
+
63
+ If you really need to throttle number of send exceptions you will need a caching layer. You could write your own or use
64
+ following implementation ExceptionsBegone::Cache and enable it with:
65
+
66
+ <tt>ExceptionsBegone::Sender.extend ExceptionsBegone::Cache</tt>
67
+
68
+ ==Authors
69
+
70
+ {Patryk Peszko}[http://github.com/ppeszko]
71
+
72
+ ==Contributors
73
+
74
+ ==License
75
+
76
+ The MIT License
77
+
78
+ Copyright (c) 2009 {XING AG}[http://www.xing.com/]
79
+
80
+ Permission is hereby granted, free of charge, to any person obtaining a copy
81
+ of this software and associated documentation files (the "Software"), to deal
82
+ in the Software without restriction, including without limitation the rights
83
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
84
+ copies of the Software, and to permit persons to whom the Software is
85
+ furnished to do so, subject to the following conditions:
86
+
87
+ The above copyright notice and this permission notice shall be included in
88
+ all copies or substantial portions of the Software.
89
+
90
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
91
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
92
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
93
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
94
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
95
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
96
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gemspec|
6
+ gemspec.name = "exceptions_begone_notifier"
7
+ gemspec.description = "Catch and send exceptions to exceptions_begone service"
8
+ gemspec.summary = "Catch and send exceptions to exceptions_begone service"
9
+ gemspec.homepage = "http://github.com/ppeszko/exceptions_begone_notifier"
10
+ gemspec.authors = ["Patryk Peszko"]
11
+ gemspec.add_dependency('activesupport')
12
+ end
13
+ Jeweler::GemcutterTasks.new
14
+ rescue LoadError
15
+ puts "Jeweler not available. Install it with: sudo gem install jeweler"
16
+ end
17
+
18
+ require 'rake/testtask'
19
+ Rake::TestTask.new(:test) do |test|
20
+ test.libs << 'lib' << 'test'
21
+ test.pattern = 'test/**/test_*.rb'
22
+ test.verbose = true
23
+ end
24
+
25
+ begin
26
+ require 'rcov/rcovtask'
27
+ Rcov::RcovTask.new do |test|
28
+ test.libs << 'test'
29
+ test.pattern = 'test/**/test_*.rb'
30
+ test.verbose = true
31
+ test.rcov_opts << '--exclude "gems/*"'
32
+ end
33
+ rescue LoadError
34
+ task :rcov do
35
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
36
+ end
37
+ end
38
+
39
+ task :test => :check_dependencies
40
+
41
+ task :default => :test
42
+
43
+ require 'rake/rdoctask'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "exceptions_begone_notifier #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
data/TODO ADDED
@@ -0,0 +1 @@
1
+ [OPEN] log exceptions in easy to read format
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.3
@@ -0,0 +1,52 @@
1
+ module ExceptionsBegone
2
+ class ConnectionConfigurator
3
+ @@defaults = {
4
+ :project => "production",
5
+ :port => 80,
6
+ :open_timeout => 5,
7
+ :read_timeout => 5,
8
+ :host => "127.0.0.1"
9
+ }
10
+
11
+ class << self
12
+ attr_writer :global_connection
13
+
14
+ def global_connection
15
+ @global_connection ||= ConnectionConfigurator.new
16
+ end
17
+
18
+ def global_parameters=(parameters = {})
19
+ parameters = parameters.marshal_dump if parameters.respond_to?(:marshal_dump)
20
+ self.global_connection = ConnectionConfigurator.build(parameters)
21
+ end
22
+
23
+ def build(parameters = {})
24
+ parameters.blank? ? self.global_connection : new(parameters)
25
+ end
26
+
27
+ end
28
+
29
+ def initialize(parameters = {})
30
+ parameters.each do |method_name, key|
31
+ self.__send__("#{method_name}=", key)
32
+ end
33
+ end
34
+
35
+ def path
36
+ "/projects/#{project}/notifications"
37
+ end
38
+
39
+ def method_missing(method_id, *args, &block)
40
+ method_id.to_s =~ /(\w*)(=)?/
41
+ name, setter = $1, $2
42
+
43
+ super unless @@defaults.include?(name.to_sym)
44
+
45
+ if setter
46
+ instance_variable_set("@#{name}", *args)
47
+ else
48
+ instance_variable_get("@#{name}") || @@defaults[method_id]
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,44 @@
1
+ require 'digest/md5'
2
+
3
+ module ExceptionsBegone::Cache
4
+
5
+ HOURLY_SEND_LIMIT = 500
6
+ TTL = 60
7
+
8
+ def self.extended(target)
9
+ target.instance_eval do
10
+ class << self
11
+ alias_method :send_exception_without_cache, :send_exception
12
+ alias_method :send_exception, :send_exception_with_cache
13
+ end
14
+ end
15
+ end
16
+
17
+ def send_exception_with_cache(exception, controller, request, connection_options = {})
18
+ exception_signature = Digest::MD5.hexdigest(exception.backtrace.join.to_s)
19
+ return if skip_sending?(exception_signature)
20
+
21
+ save_signature_in_cache(exception_signature)
22
+ send_exception_without_cache(exception, controller, request, connection_options = {})
23
+ end
24
+
25
+ def skip_sending?(exception_signature)
26
+ signature_equal?(exception_signature) || hourly_send_limit_reached?
27
+ end
28
+
29
+ def signature_equal?(exception_signature)
30
+ Rails.cache.read('last_exception_signature', :raw => true) == exception_signature
31
+ end
32
+
33
+ def hourly_send_limit_reached?
34
+ key = "exceptions_sent_in_last_hour.#{Time.now.hour}"
35
+ sent_number = Rails.cache.fetch(key, :expires_in => 1.hour, :raw => true) do
36
+ 0
37
+ end
38
+ Rails.cache.increment(key) > HOURLY_SEND_LIMIT
39
+ end
40
+
41
+ def save_signature_in_cache(exception_signature)
42
+ Rails.cache.write('last_exception_signature', exception_signature, :expires_in => TTL, :raw => true)
43
+ end
44
+ end
@@ -0,0 +1,23 @@
1
+ module ExceptionsBegone
2
+ module Catcher
3
+ def catch_exceptions(&block)
4
+ ActionController::Base.__send__(:include, InstanceMethods)
5
+ parameters = OpenStruct.new
6
+ block.call(parameters) if block_given?
7
+ ConnectionConfigurator.global_parameters = parameters
8
+ end
9
+ module_function :catch_exceptions
10
+
11
+ module InstanceMethods
12
+ def self.included(target)
13
+ target.send(:alias_method, :rescue_action_in_public_without_catcher, :rescue_action_in_public)
14
+ target.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_catcher)
15
+ end
16
+
17
+ def rescue_action_in_public_with_catcher(exception)
18
+ Sender.send_exception(exception, self, request)
19
+ rescue_action_in_public_without_catcher(exception)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,51 @@
1
+ module ExceptionsBegone
2
+ class Formatter
3
+ class << self
4
+ def format_data(category, notification)
5
+ notification.symbolize_keys!
6
+ notification.merge!(:category => category)
7
+ serialized_notification = serialize_data(notification)
8
+ to_json(serialized_notification)
9
+ end
10
+
11
+ def format_exception_data(exception, controller, request)
12
+ { :identifier => generate_identifier(controller, exception),
13
+ :payload => {
14
+ :parameters => request.parameters,
15
+ :url => request.url,
16
+ :ip => request.ip,
17
+ :request_environment => request.env,
18
+ :session => request.session,
19
+ :environment => ENV.to_hash,
20
+ :backtrace => exception.backtrace
21
+ }
22
+ }
23
+ end
24
+
25
+ def generate_identifier(controller, exception)
26
+ "#{controller.controller_name}##{controller.action_name} (#{exception.class}) #{exception.message.inspect}"
27
+ end
28
+
29
+ def serialize_data(data)
30
+ case data
31
+ when String
32
+ data
33
+ when Hash
34
+ data.inject({}) do |result, (key, value)|
35
+ result.update(key => serialize_data(value))
36
+ end
37
+ when Array
38
+ data.map do |elem|
39
+ serialize_data(elem)
40
+ end
41
+ else
42
+ data.to_s
43
+ end
44
+ end
45
+
46
+ def to_json(attributes)
47
+ ActiveSupport::JSON.encode(:notification => attributes)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,45 @@
1
+ module ExceptionsBegone
2
+ class Sender
3
+ class << self
4
+ # ugly
5
+ attr_accessor :mailer
6
+
7
+ def post(data, connection_options = {})
8
+ conn_conf = ConnectionConfigurator.build(connection_options)
9
+
10
+ http = Net::HTTP.new(conn_conf.host, conn_conf.port)
11
+ http.read_timeout = conn_conf.read_timeout
12
+ http.open_timeout = conn_conf.open_timeout
13
+
14
+ log(data)
15
+ http.post(conn_conf.path, data, "Content-type" => "application/json", "Accept" => "application/json")
16
+ rescue TimeoutError, Errno::ECONNREFUSED => e
17
+ log(e.inspect)
18
+ log(data)
19
+ mailer.deliver_error(e.message, "#{e.inspect} #{data}") if mailer
20
+ end
21
+
22
+ def send_exception(exception, controller, request, connection_options = {})
23
+ notification = Formatter.format_exception_data(exception, controller, request)
24
+ send_generic("exception", notification, connection_options = {})
25
+ end
26
+
27
+ def send_generic(category, notification, connection_options = {})
28
+ data = Formatter.format_data(category, notification)
29
+ post(data, connection_options)
30
+ end
31
+
32
+ def log(message)
33
+ Rails.logger.error("[EXCEPTIONS_BEGONE]: #{message}")
34
+ end
35
+
36
+ def method_missing(method_name, *args)
37
+ if method_name.to_s =~ /^send_(.*)/
38
+ Sender.__send__(:send_generic, "#{$1}", *args)
39
+ else
40
+ super(method_name, *args)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,9 @@
1
+ require 'active_support'
2
+ require 'active_support/json'
3
+ require 'ostruct'
4
+
5
+ require 'exceptions_begone/connection_configurator'
6
+ require 'exceptions_begone/sender'
7
+ require 'exceptions_begone/formatter'
8
+ require 'exceptions_begone/exceptions_support/catcher'
9
+ require 'exceptions_begone/exceptions_support/cache'
@@ -0,0 +1,48 @@
1
+ require 'test_helper'
2
+
3
+ module Rails
4
+ end
5
+
6
+ class TestCachingInterface < Test::Unit::TestCase
7
+
8
+ class ExceptionsBegone::TestSender
9
+ class << self
10
+ def send_exception(exception, controller, request, connection_options = {})
11
+ end
12
+ end
13
+ end
14
+
15
+ ExceptionsBegone::TestSender.extend ExceptionsBegone::Cache
16
+
17
+ def setup
18
+ @cache = stub("cache")
19
+ Rails.stubs(:cache).returns(@cache)
20
+ @exception = Exception.new
21
+ @exception.stubs(:backtrace).returns([])
22
+ @hashed_backtrace = Digest::MD5.hexdigest(@exception.backtrace.join.to_s)
23
+ end
24
+
25
+ def test_cache_should_be_enabled_after_including
26
+ ExceptionsBegone::TestSender.expects(:skip_sending?).returns(true)
27
+
28
+ ExceptionsBegone::TestSender.send_exception(@exception, "controller", "request")
29
+ end
30
+
31
+ def test_should_send_exception_on_new_exception_if_the_hourly_limit_is_not_reached
32
+ ExceptionsBegone::TestSender.expects(:signature_equal?).with(@hashed_backtrace).returns(false)
33
+ ExceptionsBegone::TestSender.expects(:hourly_send_limit_reached?).returns(false)
34
+
35
+ ExceptionsBegone::TestSender.expects(:save_signature_in_cache).with(@hashed_backtrace)
36
+ ExceptionsBegone::TestSender.expects(:send_exception_without_cache).with(@exception, "controller", "request", {})
37
+ ExceptionsBegone::TestSender.send_exception(@exception, "controller", "request")
38
+ end
39
+
40
+ def test_should_not_send_exception_on_new_exception_if_the_hourly_limit_is_reached
41
+ ExceptionsBegone::TestSender.expects(:signature_equal?).with(@hashed_backtrace).returns(false)
42
+ ExceptionsBegone::TestSender.expects(:hourly_send_limit_reached?).returns(true)
43
+
44
+ ExceptionsBegone::TestSender.expects(:save_signature_in_cache).never
45
+ ExceptionsBegone::TestSender.expects(:send_exception_without_cache).never
46
+ ExceptionsBegone::TestSender.send_exception(@exception, "controller", "request")
47
+ end
48
+ end
@@ -0,0 +1,73 @@
1
+ require 'test_helper'
2
+ require 'action_controller'
3
+ require 'net/http'
4
+
5
+ ActionController::Routing::Routes.draw do |map|
6
+ map.connect ':controller/:action/:id'
7
+ end
8
+
9
+ class TestCatchingExceptions < ActionController::TestCase
10
+
11
+ class CatchingExceptionsController < ActionController::Base
12
+ CatchingExceptionsController.consider_all_requests_local = false
13
+
14
+ def action_with_exception
15
+ raise "Exception from #{self.class.name}"
16
+ end
17
+
18
+ ExceptionsBegone::Catcher.catch_exceptions
19
+ end
20
+
21
+ tests CatchingExceptionsController
22
+ def setup
23
+ @request.remote_addr = '1.2.3.4'
24
+ @request.host = 'example.com'
25
+ end
26
+
27
+ def test_should_catch_all_exceptions_from_controller
28
+ CatchingExceptionsController.any_instance.expects(:rescue_action_in_public)
29
+
30
+ get :action_with_exception
31
+ end
32
+
33
+ def test_should_send_the_exception
34
+ ExceptionsBegone::Sender.expects(:send_generic).with("exception", anything, anything)
35
+ CatchingExceptionsController.any_instance.expects(:rescue_action_in_public_without_catcher)
36
+
37
+ get :action_with_exception
38
+ end
39
+ end
40
+
41
+ class TestConfiguringCatcher < ActionController::TestCase
42
+ class ConfiguringExceptionsController < ActionController::Base
43
+ ConfiguringExceptionsController.consider_all_requests_local = false
44
+
45
+ def action_with_exception
46
+ raise "Exception from #{self.class.name}"
47
+ end
48
+
49
+ ExceptionsBegone::Catcher.catch_exceptions do |catcher|
50
+ catcher.project = "test_configuration_project"
51
+ catcher.host = "my_host"
52
+ catcher.port = 987
53
+ end
54
+ end
55
+
56
+ tests ConfiguringExceptionsController
57
+ def test_should_send_exceptions_to_chosen_service
58
+ ExceptionsBegone::Sender.stubs(:log)
59
+ @request.remote_addr = '1.2.3.4'
60
+ @request.host = 'my_host'
61
+
62
+ notification = {:status => "ok"}
63
+ parameters = {:port => 987, :host => "my_host", :project => "test_configuration_project"}
64
+ net_http = Net::HTTP.new(parameters[:host], parameters[:port])
65
+
66
+ Net::HTTP.expects(:new).with(parameters[:host], parameters[:port]).returns(net_http)
67
+ Net::HTTP.any_instance.expects(:post).with("/projects/#{parameters[:project]}/notifications", anything, anything)
68
+ ConfiguringExceptionsController.any_instance.expects(:rescue_action_in_public_without_catcher)
69
+
70
+ get :action_with_exception
71
+ end
72
+ end
73
+
@@ -0,0 +1,12 @@
1
+ require "test_helper"
2
+
3
+ class TestConnectionConfigurator < Test::Unit::TestCase
4
+ def setup
5
+ ExceptionsBegone::ConnectionConfigurator.instance_variable_set("@global_connection", nil)
6
+ end
7
+
8
+ def test_build_should_return_a_connection_cofigurator_with_default_settings
9
+ conn_conf = ExceptionsBegone::ConnectionConfigurator.build
10
+ assert_equal "127.0.0.1", conn_conf.host
11
+ end
12
+ end
@@ -0,0 +1,61 @@
1
+ require 'test_helper'
2
+ require 'action_controller'
3
+
4
+ class TestFormatter < Test::Unit::TestCase
5
+ def test_serialize_data
6
+ object = Object.new
7
+ nested_object = Object.new
8
+ object_in_array = Object.new
9
+ notification = {:string => "", :hash => {}, :array => [object_in_array], :object => object, :nested => {:nested_object => nested_object}}
10
+
11
+ object.expects(:to_s).returns("object")
12
+ nested_object.expects(:to_s).returns("nested_object")
13
+ object_in_array.expects(:to_s).returns("object_in_array")
14
+
15
+ serialize_data = ExceptionsBegone::Formatter.serialize_data(notification)
16
+ assert_equal "object", serialize_data[:object]
17
+ assert_equal "nested_object", serialize_data[:nested][:nested_object]
18
+ assert_equal ["object_in_array"], serialize_data[:array]
19
+ end
20
+
21
+ def test_all_data_should_be_serialized
22
+ ExceptionsBegone::Formatter.expects(:serialize_data).returns("")
23
+
24
+ ExceptionsBegone::Formatter.format_data("test_category", :payload => {})
25
+ end
26
+ end
27
+
28
+ class TestFormattingExceptionsData < Test::Unit::TestCase
29
+
30
+ def setup
31
+ @exception = Exception.new("some exception")
32
+ @controller = ActionController::Base.new
33
+ request = stub(:parameters => "",
34
+ :url => "http://www.example.com",
35
+ :ip => "127.0.0.1",
36
+ :env => ENV.to_hash,
37
+ :session => "")
38
+ @controller.stubs(:request).returns(request)
39
+ end
40
+
41
+ def test_generate_identifier
42
+ assert_equal "#{@controller.controller_name}##{@controller.action_name} (#{@exception.class}) #{@exception.message.inspect}", ExceptionsBegone::Formatter.generate_identifier(@controller, @exception)
43
+ end
44
+
45
+ def test_format_exception_data_should_automatically_set_identifier
46
+ identifier = ExceptionsBegone::Formatter.format_exception_data(@exception, @controller, @controller.request)[:identifier]
47
+
48
+ assert_match(/#{@controller.controller_name}/, identifier)
49
+ assert_match(/#{@controller.action_name}/, identifier)
50
+ assert_match(/#{@exception.class}/, identifier)
51
+ assert_match(/#{@exception.message.inspect}/, identifier)
52
+ end
53
+
54
+ def test_format_exception_data_should_automatically_set_payload
55
+ payload = ExceptionsBegone::Formatter.format_exception_data(@exception, @controller, @controller.request)[:payload]
56
+
57
+ assert_equal(@controller.request.session, payload[:session])
58
+ assert_equal(@exception.backtrace, payload[:backtrace])
59
+ assert_equal(ENV.to_hash, payload[:environment])
60
+ end
61
+ end
@@ -0,0 +1,14 @@
1
+ require 'test/unit'
2
+ require 'mocha'
3
+
4
+ begin
5
+ require 'redgreen' unless ENV['TM_FILENAME']
6
+ rescue LoadError
7
+ end
8
+
9
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
10
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
11
+ require 'exceptions_begone_notifier'
12
+
13
+ class Test::Unit::TestCase
14
+ end
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+ require 'net/http'
3
+
4
+ class TestSendingNotifications < Test::Unit::TestCase
5
+
6
+ def setup
7
+ ExceptionsBegone::Sender.stubs(:log)
8
+ @notification = { :identifier => "NotificationName", :payload => "payload" }
9
+ @parameters = {:port => 987, :host => "my_host", :project => "my_project"}
10
+ end
11
+
12
+ def test_should_be_possible_to_send_notification
13
+ Net::HTTP.any_instance.expects(:post).with("/projects/production/notifications", anything, anything)
14
+
15
+ ExceptionsBegone::Sender.send_notification({:status => "ok"}, :host => "my_host")
16
+ end
17
+
18
+ def test_should_be_possible_to_specify_parameters_connection_parameters
19
+ @notification = {:status => "ok"}
20
+ @parameters = {:port => 987, :host => "my_host", :project => "my_project"}
21
+ net_http = Net::HTTP.new(@parameters[:host], @parameters[:port])
22
+
23
+ Net::HTTP.expects(:new).with(@parameters[:host], @parameters[:port]).returns(net_http)
24
+ Net::HTTP.any_instance.expects(:post).with("/projects/#{@parameters[:project]}/notifications", anything, anything)
25
+
26
+ ExceptionsBegone::Sender.send_notification(@notification, @parameters)
27
+ end
28
+
29
+ def test_deliver_notfications_as_json
30
+ Net::HTTP.any_instance.expects(:post).with("/projects/#{@parameters[:project]}/notifications", @notification, 'Content-type' => 'application/json', 'Accept' => 'application/json')
31
+
32
+ ExceptionsBegone::Sender.post(@notification, @parameters)
33
+ end
34
+
35
+ def test_should_handle_timeouts
36
+ Net::HTTP.any_instance.expects(:post).with("/projects/#{@parameters[:project]}/notifications", @notification, 'Content-type' => 'application/json', 'Accept' => 'application/json').raises(TimeoutError)
37
+
38
+ assert_nothing_raised do
39
+ ExceptionsBegone::Sender.post(@notification, @parameters)
40
+ end
41
+ end
42
+
43
+ def test_category_should_be_automatically_generated_from_method_name
44
+ encoded_notification = ActiveSupport::JSON.encode(:notification => {:category => 'warning'}.merge(@notification))
45
+ Net::HTTP.any_instance.expects(:post).with("/projects/#{@parameters[:project]}/notifications", encoded_notification, "Content-type" => "application/json", "Accept" => "application/json")
46
+
47
+ ExceptionsBegone::Sender.send_warning(@notification, @parameters)
48
+ end
49
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: exceptions_begone_notifier
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.3
5
+ platform: ruby
6
+ authors:
7
+ - Patryk Peszko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-03 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: Catch and send exceptions to exceptions_begone service
26
+ email:
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.rdoc
34
+ - TODO
35
+ files:
36
+ - .gitignore
37
+ - LICENSE
38
+ - Manifest
39
+ - README.rdoc
40
+ - Rakefile
41
+ - TODO
42
+ - VERSION
43
+ - lib/exceptions_begone/connection_configurator.rb
44
+ - lib/exceptions_begone/exceptions_support/cache.rb
45
+ - lib/exceptions_begone/exceptions_support/catcher.rb
46
+ - lib/exceptions_begone/formatter.rb
47
+ - lib/exceptions_begone/sender.rb
48
+ - lib/exceptions_begone_notifier.rb
49
+ - test/test_cache.rb
50
+ - test/test_catcher.rb
51
+ - test/test_connection_configurator.rb
52
+ - test/test_formatter.rb
53
+ - test/test_helper.rb
54
+ - test/test_sender.rb
55
+ has_rdoc: true
56
+ homepage: http://github.com/ppeszko/exceptions_begone_notifier
57
+ licenses: []
58
+
59
+ post_install_message:
60
+ rdoc_options:
61
+ - --charset=UTF-8
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
76
+ requirements: []
77
+
78
+ rubyforge_project:
79
+ rubygems_version: 1.3.5
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: Catch and send exceptions to exceptions_begone service
83
+ test_files:
84
+ - test/test_cache.rb
85
+ - test/test_catcher.rb
86
+ - test/test_connection_configurator.rb
87
+ - test/test_formatter.rb
88
+ - test/test_helper.rb
89
+ - test/test_sender.rb