rusen 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +23 -0
- data/LICENSE +22 -0
- data/README.md +262 -0
- data/Rakefile +65 -0
- data/lib/rusen.rb +6 -2
- data/lib/rusen/middleware/rusen_rack.rb +13 -9
- data/lib/rusen/middleware/rusen_sidekiq.rb +19 -0
- data/lib/rusen/notification.rb +14 -6
- data/lib/rusen/notifier.rb +15 -7
- data/lib/rusen/notifiers.rb +29 -0
- data/lib/rusen/notifiers/base_notifier.rb +53 -0
- data/lib/rusen/notifiers/io_notifier.rb +17 -42
- data/lib/rusen/notifiers/log4r_notifier.rb +70 -0
- data/lib/rusen/notifiers/mail_notifier.rb +86 -0
- data/lib/rusen/notifiers/{email_notifier.rb → pony_notifier.rb} +22 -19
- data/lib/rusen/settings.rb +35 -2
- data/lib/rusen/sidekiq.rb +19 -0
- data/lib/rusen/templates/email_template.html.erb +28 -74
- data/lib/rusen/templates/io_template.txt.erb +19 -0
- data/lib/rusen/templates/log4r_template.txt.erb +19 -0
- data/lib/rusen/utils/parameter_filter.rb +68 -0
- data/lib/rusen/version.rb +3 -0
- metadata +157 -32
data/lib/rusen/notification.rb
CHANGED
@@ -5,13 +5,21 @@ module Rusen
|
|
5
5
|
|
6
6
|
attr_reader :exception, :request, :environment, :session
|
7
7
|
|
8
|
-
def initialize(exception, request =
|
9
|
-
@exception
|
10
|
-
@request
|
11
|
-
@environment = environment
|
12
|
-
@session
|
8
|
+
def initialize(exception, request = {} , environment = {}, session = {})
|
9
|
+
@exception = exception
|
10
|
+
@request = request
|
11
|
+
@environment = environment
|
12
|
+
@session = session
|
13
|
+
end
|
14
|
+
|
15
|
+
def session
|
16
|
+
if @session.respond_to?(:each)
|
17
|
+
@session
|
18
|
+
else
|
19
|
+
@session.to_hash
|
20
|
+
end
|
13
21
|
end
|
14
22
|
|
15
23
|
end
|
16
24
|
|
17
|
-
end
|
25
|
+
end
|
data/lib/rusen/notifier.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
+
require 'rusen/notifiers'
|
1
2
|
require 'rusen/notification'
|
2
|
-
require 'rusen/notifiers/io_notifier'
|
3
|
-
require 'rusen/notifiers/email_notifier'
|
4
3
|
|
5
4
|
module Rusen
|
6
5
|
|
@@ -10,11 +9,20 @@ module Rusen
|
|
10
9
|
@settings = settings
|
11
10
|
|
12
11
|
@notifiers = []
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
@settings.outputs.each do |ident|
|
13
|
+
ident = Notifiers.check_deprecation(ident)
|
14
|
+
# For notifiers bundled in this gem
|
15
|
+
klass = Notifiers.load_klass(ident)
|
16
|
+
if klass.nil?
|
17
|
+
Notifiers.constants.each do |constant|
|
18
|
+
klass = Notifiers.const_get(constant)
|
19
|
+
next unless klass.is_a?(Class)
|
20
|
+
break if ident == klass.identification_symbol
|
21
|
+
klass = nil
|
22
|
+
end
|
17
23
|
end
|
24
|
+
raise "Unable to load Output Notifier identified by: #{ident}" if klass.nil? || !klass.is_a?(Class)
|
25
|
+
register(klass)
|
18
26
|
end
|
19
27
|
end
|
20
28
|
|
@@ -28,7 +36,7 @@ module Rusen
|
|
28
36
|
# @param [Hash<Object, Object>] request The request params
|
29
37
|
# @param [Hash<Object, Object>] environment The environment status.
|
30
38
|
# @param [Hash<Object, Object>] session The session status.
|
31
|
-
def notify(exception, request =
|
39
|
+
def notify(exception, request = {}, environment = {}, session = {})
|
32
40
|
begin
|
33
41
|
notification = Notification.new(exception, request, environment, session)
|
34
42
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Rusen
|
2
|
+
module Notifiers
|
3
|
+
|
4
|
+
NOTIFIERS = {
|
5
|
+
:pony => :PonyNotifier,
|
6
|
+
:mail => :MailNotifier,
|
7
|
+
:io => :IONotifier,
|
8
|
+
:log4r => :Log4rNotifier,
|
9
|
+
}
|
10
|
+
|
11
|
+
def self.load_klass(ident, klass_sym = nil)
|
12
|
+
klass_sym ||= NOTIFIERS[ident]
|
13
|
+
if klass_sym
|
14
|
+
require "rusen/notifiers/#{ident}_notifier" unless Notifiers.constants.include?(klass_sym)
|
15
|
+
Notifiers.const_get(klass_sym)
|
16
|
+
else
|
17
|
+
return nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.check_deprecation(ident)
|
22
|
+
if ident == :email
|
23
|
+
warn ':email is a deprecated output type. :pony replaces :email. A new alternative is :mail (mail gem).'
|
24
|
+
return :pony
|
25
|
+
end
|
26
|
+
return ident
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require_relative '../utils/parameter_filter'
|
3
|
+
|
4
|
+
module Rusen
|
5
|
+
module Notifiers
|
6
|
+
|
7
|
+
class BaseNotifier
|
8
|
+
|
9
|
+
def self.identification_symbol
|
10
|
+
:base_notifier
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(settings)
|
14
|
+
@settings = settings.dup
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_sessions(notification)
|
18
|
+
result = {}
|
19
|
+
|
20
|
+
include_session(result, notification.exception.backtrace, :backtrace)
|
21
|
+
include_session(result, notification.request, :request)
|
22
|
+
include_session(result, notification.session, :session)
|
23
|
+
include_session(result, notification.environment, :environment)
|
24
|
+
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
def handle_notification_exception(exception)
|
29
|
+
name = self.class.identification_symbol.to_s
|
30
|
+
|
31
|
+
warn("Rusen: #{exception.message} prevented the #{name} notifier from login the error.")
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def include_session(sessions, session, session_key)
|
37
|
+
@settings.sections
|
38
|
+
|
39
|
+
if @settings.sections.include?(session_key) && session
|
40
|
+
session = parameter_filter.filter(session) if session_key != :backtrace
|
41
|
+
|
42
|
+
sessions[session_key.to_s.capitalize] = session
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def parameter_filter
|
47
|
+
@parameter_filter ||= ParameterFilter.new(@settings.filter_parameters)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -1,7 +1,9 @@
|
|
1
|
+
require_relative 'base_notifier'
|
2
|
+
|
1
3
|
module Rusen
|
2
4
|
module Notifiers
|
3
5
|
|
4
|
-
class IONotifier
|
6
|
+
class IONotifier < BaseNotifier
|
5
7
|
|
6
8
|
STDOUT = $stdout
|
7
9
|
|
@@ -10,58 +12,31 @@ module Rusen
|
|
10
12
|
end
|
11
13
|
|
12
14
|
def initialize(settings, output = STDOUT)
|
13
|
-
|
14
|
-
@output
|
15
|
+
super(settings)
|
16
|
+
@output = output
|
15
17
|
end
|
16
18
|
|
17
19
|
def notify(notification)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
print_title('Backtrace')
|
22
|
-
|
23
|
-
@output.puts notification.exception.message
|
24
|
-
@output.puts notification.exception.backtrace
|
25
|
-
end
|
26
|
-
|
27
|
-
if @settings.sections.include?(:request)
|
28
|
-
print_title('Request')
|
29
|
-
|
30
|
-
print_hash(notification.request)
|
31
|
-
end
|
32
|
-
|
33
|
-
if @settings.sections.include?(:session)
|
34
|
-
print_title('Session')
|
35
|
-
|
36
|
-
print_hash(notification.session)
|
37
|
-
end
|
38
|
-
|
39
|
-
if @settings.sections.include?(:environment)
|
40
|
-
print_title('Environment')
|
41
|
-
|
42
|
-
print_hash(notification.environment)
|
43
|
-
end
|
20
|
+
@notification = notification
|
21
|
+
@sessions = get_sessions(@notification)
|
44
22
|
|
45
23
|
# We need to ignore all the exceptions thrown by IONotifier#notify.
|
46
|
-
|
47
|
-
|
48
|
-
|
24
|
+
@output.puts build_content
|
25
|
+
rescue Exception => exception
|
26
|
+
handle_notification_exception(exception)
|
49
27
|
end
|
50
28
|
|
51
29
|
private
|
52
30
|
|
53
|
-
def
|
54
|
-
|
55
|
-
@output.puts "#{title}:"
|
56
|
-
@output.puts '-------------------------------'
|
57
|
-
end
|
31
|
+
def build_content
|
32
|
+
template_path = File.expand_path('../../templates/io_template.txt.erb', __FILE__)
|
58
33
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
34
|
+
template = File.open(template_path).read
|
35
|
+
rhtml = ERB.new(template, nil, '-')
|
36
|
+
rhtml.result(binding)
|
63
37
|
end
|
38
|
+
|
64
39
|
end
|
65
40
|
|
66
41
|
end
|
67
|
-
end
|
42
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require_relative 'base_notifier'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'log4r'
|
5
|
+
require 'log4r/yamlconfigurator'
|
6
|
+
rescue LoadError
|
7
|
+
puts <<-MESSAGE
|
8
|
+
[Rusen Error] To use log4r notifier add this to your Gemfile:
|
9
|
+
gem 'log4r'
|
10
|
+
MESSAGE
|
11
|
+
raise
|
12
|
+
end
|
13
|
+
|
14
|
+
module Rusen
|
15
|
+
module Notifiers
|
16
|
+
|
17
|
+
class Log4rNotifier < BaseNotifier
|
18
|
+
|
19
|
+
def self.identification_symbol
|
20
|
+
:log4r
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(settings)
|
24
|
+
super(settings)
|
25
|
+
|
26
|
+
load_config(@settings.log4r_config_file)
|
27
|
+
|
28
|
+
@logger = logger_instance(@settings.logger_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def notify(notification)
|
32
|
+
@notification = notification
|
33
|
+
@sessions = get_sessions(@notification)
|
34
|
+
|
35
|
+
# We need to ignore all the exceptions thrown by Log4rNotifier#notify.
|
36
|
+
@logger.error { build_content }
|
37
|
+
rescue Exception => exception
|
38
|
+
handle_notification_exception(exception)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def build_content
|
44
|
+
template_path = File.expand_path('../../templates/log4r_template.txt.erb', __FILE__)
|
45
|
+
|
46
|
+
template = File.open(template_path).read
|
47
|
+
rhtml = ERB.new(template, nil, '-')
|
48
|
+
rhtml.result(binding)
|
49
|
+
end
|
50
|
+
|
51
|
+
def logger_instance(name = nil)
|
52
|
+
if name
|
53
|
+
Log4r::Logger[name]
|
54
|
+
else
|
55
|
+
Log4r::Logger.root
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Loads the given config file.
|
60
|
+
#
|
61
|
+
# @param [String] config_yml Configuration file path.
|
62
|
+
def load_config(config_yml)
|
63
|
+
if config_yml
|
64
|
+
Log4r::YamlConfigurator.load_yaml_file(config_yml)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require_relative 'base_notifier'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'mail'
|
5
|
+
rescue LoadError
|
6
|
+
puts <<-MESSAGE
|
7
|
+
[Rusen Error] To use Mail notifier add this to your Gemfile:
|
8
|
+
gem 'mail'
|
9
|
+
MESSAGE
|
10
|
+
raise
|
11
|
+
end
|
12
|
+
|
13
|
+
module Rusen
|
14
|
+
module Notifiers
|
15
|
+
|
16
|
+
class MailNotifier < BaseNotifier
|
17
|
+
|
18
|
+
def self.identification_symbol
|
19
|
+
:mail
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(settings)
|
23
|
+
super(settings)
|
24
|
+
|
25
|
+
if @settings && @settings.smtp_settings.any?
|
26
|
+
smtp_settings = @settings.smtp_settings
|
27
|
+
|
28
|
+
Mail.defaults do
|
29
|
+
delivery_method :smtp, smtp_settings
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def notify(notification)
|
35
|
+
@notification = notification
|
36
|
+
@sessions = get_sessions(@notification)
|
37
|
+
|
38
|
+
options = email_options.dup
|
39
|
+
options.merge!({:body => build_body})
|
40
|
+
mail = Mail.new do
|
41
|
+
from options[:from]
|
42
|
+
to options[:to]
|
43
|
+
reply_to options[:reply_to]
|
44
|
+
cc options[:cc]
|
45
|
+
bcc options[:bcc]
|
46
|
+
subject options[:subject]
|
47
|
+
html_part do
|
48
|
+
content_type "text/html; charset=#{options[:charset]}"
|
49
|
+
body options[:body]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# We need to ignore all the exceptions thrown by MailNotifier#notify.
|
54
|
+
mail.deliver!
|
55
|
+
rescue Exception => exception
|
56
|
+
handle_notification_exception(exception)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def email_options
|
62
|
+
{
|
63
|
+
:to => @settings.exception_recipients,
|
64
|
+
:charset => 'UTF-8',
|
65
|
+
:from => @settings.sender_address,
|
66
|
+
:subject => email_subject
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def email_subject
|
71
|
+
@settings.email_prefix + "#{@notification.exception.class}: #{@notification.exception.message}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def build_body
|
75
|
+
template_path = File.expand_path('../../templates/email_template.html.erb', __FILE__)
|
76
|
+
|
77
|
+
template = File.open(template_path).read
|
78
|
+
rhtml = ERB.new(template, nil, '-')
|
79
|
+
rhtml.result(binding)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -1,30 +1,32 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative 'base_notifier'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'pony'
|
5
|
+
rescue LoadError
|
6
|
+
puts <<-MESSAGE
|
7
|
+
[Rusen Error] To use Pony notifier add this to your Gemfile:
|
8
|
+
gem 'pony'
|
9
|
+
MESSAGE
|
10
|
+
raise
|
11
|
+
end
|
3
12
|
|
4
13
|
module Rusen
|
5
14
|
module Notifiers
|
6
15
|
|
7
|
-
class
|
16
|
+
class PonyNotifier < BaseNotifier
|
8
17
|
|
9
18
|
def self.identification_symbol
|
10
|
-
:
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize(settings)
|
14
|
-
@settings = settings
|
19
|
+
:pony
|
15
20
|
end
|
16
21
|
|
17
22
|
def notify(notification)
|
18
|
-
|
19
|
-
|
23
|
+
@notification = notification
|
24
|
+
@sessions = get_sessions(@notification)
|
20
25
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
warn("Rusen: #{e.class}: #{e.message} prevented the notification email from being sent.")
|
26
|
-
puts e.backtrace
|
27
|
-
end
|
26
|
+
# We need to ignore all the exceptions thrown by PonyNotifier#notify.
|
27
|
+
Pony.mail(email_options.merge({:body => build_body}))
|
28
|
+
rescue Exception => exception
|
29
|
+
handle_notification_exception(exception)
|
28
30
|
end
|
29
31
|
|
30
32
|
private
|
@@ -32,7 +34,7 @@ module Rusen
|
|
32
34
|
def email_options
|
33
35
|
{
|
34
36
|
:to => @settings.exception_recipients,
|
35
|
-
:via =>
|
37
|
+
:via => @settings.email_via,
|
36
38
|
:charset => 'utf-8',
|
37
39
|
:from => @settings.sender_address,
|
38
40
|
:headers => { 'Content-Type' => 'text/html' },
|
@@ -49,11 +51,12 @@ module Rusen
|
|
49
51
|
template_path = File.expand_path('../../templates/email_template.html.erb', __FILE__)
|
50
52
|
|
51
53
|
template = File.open(template_path).read
|
52
|
-
rhtml = ERB.new(template)
|
54
|
+
rhtml = ERB.new(template, nil, '-')
|
53
55
|
rhtml.result(binding)
|
54
56
|
end
|
55
57
|
|
56
58
|
end
|
57
59
|
|
58
60
|
end
|
61
|
+
|
59
62
|
end
|