class Fluent::MailOutput

Public Class Methods

desc(description) click to toggle source
# File lib/fluent/plugin/out_mail.rb, line 14
def desc(description)
end
new() click to toggle source
Calls superclass method
# File lib/fluent/plugin/out_mail.rb, line 72
def initialize
  super
  require 'net/smtp'
  require 'string/scrub' if RUBY_VERSION.to_f < 2.1
end

Public Instance Methods

configure(conf) click to toggle source
Calls superclass method
# File lib/fluent/plugin/out_mail.rb, line 78
def configure(conf)
  super

  if @out_keys.empty? and @message.nil?
    raise Fluent::ConfigError, "Either 'message' or 'out_keys' must be specifed."
  end

  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

  if @to_key or @cc_key or @bcc_key
    @process_event_stream_proc = Proc.new {|tag, es|
      messages = []
      subjects = []
      dests = []

      es.each do |time, record|
        messages << @create_message_proc.call(tag, time, record)
        subjects << create_formatted_subject(tag, time, record)
        dests << %w(to cc bcc).each_with_object({}){|t, dest| dest[t] = create_dest_addr(t, record) }
      end

      [messages, subjects, dests]
    }
  else
    @process_event_stream_proc = Proc.new {|tag, es|
      messages = []
      subjects = []
      dests = []

      es.each do |time, record|
        messages << @create_message_proc.call(tag, time, record)
        subjects << create_formatted_subject(tag, time, record)
      end

      [messages, subjects, dests]
    }
  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
end
create_dest_addr(dest_type, record) click to toggle source
# File lib/fluent/plugin/out_mail.rb, line 279
def create_dest_addr(dest_type, record)
  addr = instance_variable_get(:"@#{dest_type}")
  dest_key = instance_variable_get(:"@#{dest_type}_key")
  if dest_key
    return record[dest_key] || addr
  else
    return addr
  end
end
create_formatted_message(tag, time, record) click to toggle source
# File lib/fluent/plugin/out_mail.rb, line 174
def create_formatted_message(tag, time, record)
  values = []

  values = @message_out_keys.map do |key|
    case key
    when @time_key
      format_time(time, @time_format)
    when @tag_key
      tag
    else
      record[key].to_s
    end
  end

  message = (@message % values)
  with_scrub(message) {|str| str.gsub(/\\n/, "\n") }
end
create_formatted_subject(tag, time, record) click to toggle source
# File lib/fluent/plugin/out_mail.rb, line 192
def create_formatted_subject(tag, time, record)
  values = []

  values = @subject_out_keys.map do |key|
    case key
    when @time_key
      format_time(time, @time_format)
    when @tag_key
      tag
    else
      record[key].to_s
    end
  end

  @subject % values
end
create_key_value_message(tag, time, record) click to toggle source

The old `key=value` format for old version compatibility

# File lib/fluent/plugin/out_mail.rb, line 157
def create_key_value_message(tag, time, record)
  values = []

  values = @out_keys.map do |key|
    case key
    when @time_key
      format_time(time, @time_format)
    when @tag_key
      tag
    else
      "#{key}: #{record[key].to_s}"
    end
  end

  values.join("\n")
end
emit(tag, es, chain) click to toggle source
# File lib/fluent/plugin/out_mail.rb, line 139
def emit(tag, es, chain)
  messages, subjects, dests = @process_event_stream_proc.call(tag, es)

  messages.each_with_index do |message, i|
    subject = subjects[i]
    dest = dests[i]
    begin
      sendmail(subject, message, dest)
    rescue => e
      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
format_time(time, time_format) click to toggle source
# File lib/fluent/plugin/out_mail.rb, line 252
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
sendmail(subject, msg, dest = nil) click to toggle source
# File lib/fluent/plugin/out_mail.rb, line 209
  def sendmail(subject, msg, dest = nil)
    smtp = Net::SMTP.new(@host, @port)

    if @user and @password
      smtp_auth_option = [@domain, @user, @password, @authtype.to_sym]
      smtp.enable_starttls if @enable_starttls_auto
      smtp.enable_tls if @enable_tls
      smtp.start(@domain, @user, @password, @authtype.to_sym)
    else
      smtp.start
    end

    subject = subject.force_encoding('binary')
    body = msg.force_encoding('binary')
    to = (dest && dest['to']) ? dest['to'] : @to
    cc = (dest && dest['cc']) ? dest['cc'] : @cc
    bcc = (dest && dest['bcc']) ? dest['bcc'] : @bcc

    # 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)
    content = <<EOF
Date: #{date}
From: #{@from}
To: #{to}
Cc: #{cc}
Bcc: #{bcc}
Subject: #{subject}
Message-Id: #{mid}
Mime-Version: 1.0
Content-Type: #{@content_type}

#{body}
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
shutdown() click to toggle source
# File lib/fluent/plugin/out_mail.rb, line 136
def shutdown
end
start() click to toggle source
# File lib/fluent/plugin/out_mail.rb, line 133
def start
end
with_scrub(string) { |string| ... } click to toggle source
# File lib/fluent/plugin/out_mail.rb, line 268
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
with_timezone(tz) { || ... } click to toggle source
# File lib/fluent/plugin/out_mail.rb, line 261
def with_timezone(tz)
  oldtz, ENV['TZ'] = ENV['TZ'], tz
  yield
ensure
  ENV['TZ'] = oldtz
end