lib/racknga/exception_mail_notifier.rb in racknga-0.9.1 vs lib/racknga/exception_mail_notifier.rb in racknga-0.9.2
- old
+ new
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2010 Kouhei Sutou <kou@clear-code.com>
+# Copyright (C) 2010-2011 Kouhei Sutou <kou@clear-code.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
@@ -27,53 +27,86 @@
module Racknga
# Ruby 1.9 only. 1.8 isn't supported.
class ExceptionMailNotifier
def initialize(options)
@options = Utils.normalize_options(options || {})
+ reset_limitation
end
def notify(exception, environment)
- host = @options[:host] || "localhost"
return if to.empty?
- mail = format(exception, environment)
+
+ if limitation_expired?
+ send_summaries unless @summaries.empty?
+ reset_limitation
+ end
+
+ if @mail_count < max_mail_count_in_limit_duration
+ send_notification(exception, environment)
+ else
+ @summaries << summarize(exception, environment)
+ end
+
+ @mail_count += 1
+ end
+
+ private
+ def reset_limitation
+ @mail_count = 0
+ @count_start_time = Time.now
+ @summaries = []
+ end
+
+ def limitation_expired?
+ (Time.now - @count_start_time) > limit_duration
+ end
+
+ def send(mail)
+ host = @options[:host] || "localhost"
Net::SMTP.start(host, @options[:port]) do |smtp|
smtp.send_message(mail, from, *to)
end
end
- private
- def format(exception, environment)
- header = format_header(exception, environment)
- body = format_body(exception, environment)
+ def create_mail(options)
+ subject = [@options[:subject_label], options[:subject]].compact.join(' ')
+ header = header(:subject => subject)
+
+ body = options[:body]
+
mail = "#{header}\r\n#{body}"
mail.force_encoding("utf-8")
begin
mail = mail.encode(charset)
rescue EncodingError
end
mail.force_encoding("ASCII-8BIT")
end
- def format_header(exception, environment)
+ def header(options)
<<-EOH
MIME-Version: 1.0
Content-Type: Text/Plain; charset=#{charset}
Content-Transfer-Encoding: #{transfer_encoding}
From: #{from}
To: #{to.join(', ')}
-Subject: #{encode_subject(subject(exception, environment))}
+Subject: #{encode_subject(options[:subject])}
Date: #{Time.now.rfc2822}
EOH
end
- def format_body(exception, environment)
+ def send_notification(exception, environment)
+ mail = create_mail(:subject => exception.to_s,
+ :body => notification_body(exception, environment))
+ send(mail)
+ end
+
+ def notification_body(exception, environment)
request = Rack::Request.new(environment)
body = <<-EOB
-URL: #{request.url}
+#{summarize(exception, environment)}
--
-#{exception.class}: #{exception}
---
#{exception.backtrace.join("\n")}
EOB
params = request.params
max_key_size = (environment.keys.collect(&:size) +
params.keys.collect(&:size)).max
@@ -96,14 +129,32 @@
end
body
end
- def subject(exception, environment)
- [@options[:subject_label], exception.to_s].compact.join(' ')
+ def send_summaries
+ subject = "summaries of #{@summaries.size} notifications"
+ mail = create_mail(:subject => subject,
+ :body => report_body)
+ send(mail)
end
+ def report_body
+ @summaries[0..10].join("\n\n")
+ end
+
+ def summarize(exception, environment)
+ request = Rack::Request.new(environment)
+ <<-EOB
+Timestamp: #{Time.now.rfc2822}
+--
+URL: #{request.url}
+--
+#{exception.class}: #{exception}
+EOB
+ end
+
def to
@to ||= ensure_array(@options[:to]) || []
end
def ensure_array(maybe_array)
@@ -121,9 +172,19 @@
"#{name}@#{host}"
end
def charset
@options[:charset] || 'utf-8'
+ end
+
+ DEFAULT_MAX_MAIL_COUNT_IN_LIMIT_DURATION = 2
+ def max_mail_count_in_limit_duration
+ @options[:max_mail_count_in_limit_duration] || DEFAULT_MAX_MAIL_COUNT_IN_LIMIT_DURATION
+ end
+
+ DEFAULT_LIMIT_DURATION = 60 # one minute
+ def limit_duration
+ @options[:limit_duration] || DEFAULT_LIMIT_DURATION
end
def transfer_encoding
case charset
when /\Autf-8\z/i