lib/fluent/plugin/out_mail.rb in fluent-plugin-mail-0.1.2 vs lib/fluent/plugin/out_mail.rb in fluent-plugin-mail-0.2.0

- old
+ new

@@ -6,30 +6,31 @@ # Define `log` method for v0.10.42 or earlier unless method_defined?(:log) define_method("log") { $log } end - config_param :out_keys, :string, :default => "" - config_param :message, :string, :default => nil - config_param :message_out_keys, :string, :default => "" - config_param :time_key, :string, :default => nil - config_param :time_format, :string, :default => nil - config_param :tag_key, :string, :default => 'tag' - config_param :host, :string - config_param :port, :integer, :default => 25 - config_param :domain, :string, :default => 'localdomain' - config_param :user, :string, :default => nil - config_param :password, :string, :default => nil - config_param :from, :string, :default => 'localhost@localdomain' - config_param :to, :string, :default => '' - config_param :cc, :string, :default => '' - config_param :bcc, :string, :default => '' - config_param :subject, :string, :default => 'Fluent::MailOutput plugin' - config_param :subject_out_keys, :string, :default => "" - config_param :enable_starttls_auto, :bool, :default => false - config_param :enable_tls, :bool, :default => false - config_param :time_locale, :default => nil + config_param :out_keys, :string, :default => "" + config_param :message, :string, :default => nil + config_param :message_out_keys, :string, :default => "" + config_param :time_key, :string, :default => nil + config_param :tag_key, :string, :default => 'tag' + config_param :host, :string + config_param :port, :integer, :default => 25 + config_param :domain, :string, :default => 'localdomain' + config_param :user, :string, :default => nil + config_param :password, :string, :default => nil + config_param :from, :string, :default => 'localhost@localdomain' + config_param :to, :string, :default => '' + config_param :cc, :string, :default => '' + config_param :bcc, :string, :default => '' + config_param :subject, :string, :default => 'Fluent::MailOutput plugin' + config_param :subject_out_keys, :string, :default => "" + config_param :enable_starttls_auto, :bool, :default => false + config_param :enable_tls, :bool, :default => false + config_param :time_format, :string, :default => "%F %T %z" + config_param :localtime, :bool, :default => true + config_param :time_locale, :default => nil def initialize super require 'net/smtp' require 'kconv' @@ -45,82 +46,70 @@ if @out_keys.empty? and @message.nil? raise Fluent::ConfigError, "Either 'message' or 'out_keys' must be specifed." end - begin - @message % (['1'] * @message_out_keys.length) if @message - rescue ArgumentError - raise Fluent::ConfigError, "string specifier '%s' of message and message_out_keys specification mismatch" + if @message + begin + @message % (['1'] * @message_out_keys.length) + rescue ArgumentError + raise Fluent::ConfigError, "string specifier '%s' of message and message_out_keys specification mismatch" + end + @create_message_proc = Proc.new {|tag, time, record| create_formatted_message(tag, time, record) } + else + # The default uses the old `key=value` format for old version compatibility + @create_message_proc = Proc.new {|tag, time, record| create_key_value_message(tag, time, record) } end begin @subject % (['1'] * @subject_out_keys.length) rescue ArgumentError raise Fluent::ConfigError, "string specifier '%s' of subject and subject_out_keys specification mismatch" end - - if @time_key - if @time_format - f = @time_format - tf = Fluent::TimeFormatter.new(f, @localtime) - @time_format_proc = tf.method(:format) - @time_parse_proc = Proc.new {|str| Time.strptime(str, f).to_i } - else - @time_format_proc = Proc.new {|time| time.to_s } - @time_parse_proc = Proc.new {|str| str.to_i } - end - end end def start - end def shutdown end def emit(tag, es, chain) messages = [] subjects = [] es.each {|time,record| - if @message - messages << create_formatted_message(tag, time, record) - else - messages << create_key_value_message(tag, time, record) - end + messages << @create_message_proc.call(tag, time, record) subjects << create_formatted_subject(tag, time, record) } - messages.each_with_index do |msg, i| + (0...messages.size).each do |i| + message = messages[i] subject = subjects[i] begin - res = sendmail(subject, msg) + sendmail(subject, message) rescue => e - log.warn "out_mail: failed to send notice to #{@host}:#{@port}, subject: #{subject}, message: #{msg}, error_class: #{e.class}, error_message: #{e.message}, error_backtrace: #{e.backtrace.first}" + log.warn "out_mail: failed to send notice to #{@host}:#{@port}, subject: #{subject}, message: #{message}, " << + "error_class: #{e.class}, error_message: #{e.message}, error_backtrace: #{e.backtrace.first}" end end chain.next end - def format(tag, time, record) - "#{Time.at(time).strftime('%Y/%m/%d %H:%M:%S')}\t#{tag}\t#{record.to_json}\n" - end - + # The old `key=value` format for old version compatibility def create_key_value_message(tag, time, record) values = [] - @out_keys.each do |key| + values << @out_keys.each do |key| case key when @time_key - values << @time_format_proc.call(time) + format_time(time, @time_format) when @tag_key - values << tag + tag else - values << "#{key}: #{record[key].to_s}" + "#{key}: #{record[key].to_s}" end end values.join("\n") end @@ -129,11 +118,11 @@ values = [] values = @message_out_keys.map do |key| case key when @time_key - @time_format_proc.call(time) + format_time(time, @time_format) when @tag_key tag else record[key].to_s end @@ -141,28 +130,17 @@ message = (@message % values) with_scrub(message) {|str| str.gsub(/\\n/, "\n") } end - def with_scrub(string) - begin - return yield(string) - rescue ArgumentError => e - raise e unless e.message.index("invalid byte sequence in") == 0 - log.info "out_mail: invalid byte sequence is replaced in #{string}" - string.scrub!('?') - retry - end - end - def create_formatted_subject(tag, time, record) values = [] values = @subject_out_keys.map do |key| case key when @time_key - @time_format_proc.call(time) + format_time(time, @time_format) when @tag_key tag else record[key].to_s end @@ -184,42 +162,57 @@ end subject = subject.force_encoding('binary') body = msg.force_encoding('binary') - if time_locale - date = Time::now.timezone(time_locale) - else - date = Time::now - end + # Date: header has timezone, so usually it is not necessary to set locale explicitly + # But, for people who would see mail header text directly, the locale information may help something + # (for example, they can tell the sender should live in Tokyo if +0900) + date = format_time(Time.now, "%a, %d %b %Y %X %z") mid = sprintf("<%s@%s>", SecureRandom.uuid, SecureRandom.uuid) - - debug_msg = smtp.send_mail(<<EOS, @from, @to.split(/,/), @cc.split(/,/), @bcc.split(/,/)) -Date: #{date.strftime("%a, %d %b %Y %X %z")} + content = <<EOF +Date: #{date} From: #{@from} To: #{@to} Cc: #{@cc} Bcc: #{@bcc} Subject: #{subject} Message-Id: #{mid} Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 #{body} -EOS - log.debug "out_mail: email send response: #{debug_msg}" +EOF + response = smtp.send_mail(content, @from, @to.split(/,/), @cc.split(/,/), @bcc.split(/,/)) + log.debug "out_mail: content: #{content.gsub("\n", "\\n")}" + log.debug "out_mail: email send response: #{response.string.chomp}" smtp.finish end -end + def format_time(time, time_format) + # Fluentd >= v0.12's TimeFormatter supports timezone, but v0.10 does not + if @time_locale + with_timezone(@time_locale) { Fluent::TimeFormatter.new(time_format, @localtime).format(time) } + else + Fluent::TimeFormatter.new(time_format, @localtime).format(time) + end + end -class Time - def timezone(timezone = 'UTC') - old = ENV['TZ'] - utc = self.dup.utc - ENV['TZ'] = timezone - output = utc.localtime - ENV['TZ'] = old - output + def with_timezone(tz) + oldtz, ENV['TZ'] = ENV['TZ'], tz + yield + ensure + ENV['TZ'] = oldtz + end + + def with_scrub(string) + begin + return yield(string) + rescue ArgumentError => e + raise e unless e.message.index("invalid byte sequence in") == 0 + log.info "out_mail: invalid byte sequence is replaced in #{string}" + string.scrub!('?') + retry + end end end