├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Gemfile ├── Gemfile.0.12 ├── LICENSE ├── README.md ├── Rakefile ├── example ├── example.conf └── to_cc_bcc_key.conf ├── fluent-plugin-mail.gemspec ├── lib └── fluent │ └── plugin │ └── out_mail.rb └── test ├── helper.rb └── plugin └── test_out_mail.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | vendor/bundle 19 | .ruby-version 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | rvm: 2 | - 2.2.* 3 | - 2.3.* 4 | - 2.4.* 5 | gemfile: 6 | - Gemfile 7 | - Gemfile.0.12 8 | before_install: gem update bundler 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.3.0 (2017-11-25) 2 | 3 | Changes: 4 | 5 | * Drop Fluentd v0.10 support 6 | * Use array type for array like parameters (thanks to @cosmo0920) 7 | 8 | # 0.2.5 (2017-11-25) 9 | 10 | Enhancements: 11 | 12 | * Add `authtype` option (thanks to Ruilin Huang @hrl) 13 | 14 | # 0.2.4 15 | 16 | Enhancements: 17 | 18 | * Add to_key, cc_key, bcc_key options (thanks to authorNari) 19 | 20 | # 0.2.3 21 | 22 | Enhancements: 23 | 24 | * Add content_type option (thanks to okkez) 25 | * Support desc if it is available. see http://qiita.com/repeatedly/items/bce628ba75b442424bcf (thanks to okkez) 26 | 27 | # 0.2.2 28 | 29 | Enhancements: 30 | 31 | * Support secret parameter for `password` option (thanks to cosmo0920) 32 | 33 | # 0.2.1 34 | 35 | Fixes: 36 | 37 | * Fix `create_key_value_message` was broken because of 0.2.0 refactoring 38 | 39 | # 0.2.0 40 | 41 | Changes: 42 | 43 | * Refactoring 44 | 45 | Fixes: 46 | 47 | * Fix so that `time_locale` option works for time field in mail body 48 | 49 | Enhancements: 50 | 51 | * Add `localtime` option 52 | 53 | # 0.1.2 54 | 55 | Enhancement: 56 | 57 | * Send mail with Message-Id header field 58 | 59 | # 0.1.1 60 | 61 | Enhancement: 62 | 63 | * Avoid invalid byte seuqnce in UTF-8 error 64 | 65 | # 0.1.0 66 | 67 | Enhancement: 68 | 69 | * Add `enable_tls` option (thanks to @mash) 70 | * Add more debug messages (thanks to @glaci) 71 | 72 | # 0.0.5 73 | 74 | olders 75 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /Gemfile.0.12: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | gem 'fluentd', '~> 0.12.0' 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012- Yuichi UEMURA 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fluent::Plugin::Mail, a plugin for [Fluentd](http://fluentd.org) 2 | 3 | 4 | ## Installation 5 | 6 | Add this line to your application's Gemfile: 7 | 8 | gem 'fluent-plugin-mail' 9 | 10 | Or install it yourself as: 11 | 12 | $ gem install fluent-plugin-mail 13 | 14 | Or use td-agent 2: (on Ubuntu12.04) 15 | 16 | $ sudo /usr/sbin/td-agent-gem install fluent-plugin-mail 17 | 18 | Or use td-agent 1: (not recommeded. Use td-agent 2 instead) 19 | 20 | $ sudo /usr/lib/fluent/ruby/bin/fluent-gem install fluent-plugin-mail 21 | 22 | ## Mail Configuration with out_keys (no auth) 23 | 24 | 25 | type mail 26 | host SMTPSERVER 27 | port 25 28 | from SOURCE 29 | to DEST1,DEST2,DEST3 30 | subject SUBJECT: %s 31 | subject_out_keys tag 32 | out_keys tag,foo,message 33 | time_locale UTC # optional 34 | 35 | 36 | Assume following input: 37 | 38 | tag.example: {"foo":"bar","message":"awesome!"} 39 | 40 | Email is sent like 41 | 42 | From: SOURCE 43 | To: DEST1,DEST2,DEST3 44 | Subject: SUBJECT: tag.example 45 | Mime-Version: 1.0 46 | Content-Type: text/plain; charset=utf-8 47 | 48 | tag: tag.example 49 | foo: bar 50 | message: awesome! 51 | 52 | ## Mail Configuration with Message Format (no auth) 53 | 54 | You may use `message` parameter to define mail format as you like. Use `\n` to put a return code. 55 | 56 | 57 | type mail 58 | host SMTPSERVER 59 | port 25 60 | from SOURCE 61 | to DEST1,DEST2,DEST3 62 | subject SUBJECT: %s 63 | subject_out_keys tag 64 | message %s\n%s %s 65 | message_out_keys tag,foo,message 66 | time_locale UTC # optional 67 | 68 | 69 | Assume following input: 70 | 71 | tag.example: {"foo":"bar","message":"awesome!"} 72 | 73 | Email is sent like 74 | 75 | From: SOURCE 76 | To: DEST1,DEST2,DEST3 77 | Subject: SUBJECT: tag.example 78 | Mime-Version: 1.0 79 | Content-Type: text/plain; charset=utf-8 80 | 81 | tag.example 82 | bar awesome! 83 | 84 | ## Mail Configuration for Gmail(use STARTTLS) 85 | 86 | 87 | type mail 88 | host smtp.gmail.com 89 | port 587 90 | domain gmail.com 91 | from SOURCE 92 | to DEST1,DEST2,DEST3 93 | subject SUBJECT 94 | user USERNAME( ex. hoge@gmail.com) 95 | password PASSWORD 96 | enable_starttls_auto true 97 | enable_tls false 98 | out_keys tag,foo,message 99 | time_locale UTC # optional 100 | 101 | 102 | 103 | ## Usage Sample 104 | 105 | ### SingleNode's syslog check 106 | 107 | use fluent_plugin_notifier(https://github.com/tagomoris/fluent-plugin-notifier) 108 | 109 | $ gem install fluent-plugin-notifier 110 | 111 | configure td-agent.conf for single node 112 | 113 | 114 | type tail 115 | tag syslog 116 | path /var/log/syslog 117 | 118 | 119 | type notifier 120 | 121 | pattern check_syslog 122 | check string_find 123 | warn_regexp .*warn.* 124 | crit_regexp .*crit.* 125 | target_key_pattern message 126 | 127 | 128 | pattern check_syslog 129 | check string_find 130 | warn_regexp .*Error.* 131 | crit_regexp .*Down.* 132 | target_key_pattern message 133 | 134 | 135 | 136 | type mail 137 | host MAILSERVER 138 | port MAILSERVER_PORT 139 | domain DOMAIN 140 | from SOURCE_MAIL_ADDRESS 141 | to DESTINATION_MAIL_ADDRESS 142 | subject SUBJECT 143 | out_keys target_tag, pattern, value, message_time 144 | 145 | 146 | 147 | ### MultiNode's syslog check 148 | 149 | use config_expander(https://github.com/tagomoris/fluent-plugin-config-expander) 150 | 151 | $ gem install fluent-plugin-config-expander 152 | 153 | source node("/etc/td-agent/td-agent.conf") 154 | 155 | 156 | type config_expander 157 | 158 | type tail 159 | format syslog 160 | path /var/log/syslog 161 | tag syslog.${hostname} 162 | pos_file /tmp/syslog.pos 163 | 164 | a 165 | 166 | type forward 167 | 168 | host HOST_ADDRESS 169 | 170 | 171 | 172 | 173 | log server("/etc/td-agent/td-agent.conf") 174 | 175 | 176 | type forward 177 | 178 | 179 | type copy 180 | 181 | type file 182 | path /var/log-server/syslog 183 | 184 | 185 | type notifier 186 | 187 | pattern check_syslog 188 | check string_find 189 | warn_regexp .*warn.* 190 | crit_regexp .*crit.* 191 | target_key_pattern message 192 | 193 | 194 | 195 | 196 | type mail 197 | host MAILSERVER 198 | port MAILSERVER_PORT 199 | domain DOMAIN 200 | from SOURCE_MAIL_ADDRESS 201 | to DESTINATION_MAIL_ADDRESS 202 | subject SUBJECT 203 | outkeys target_tag, pattern, value 204 | time_locale UTC # optional 205 | 206 | 207 | 208 | ### Dynamic identiciation of mail destination (to, cc, bcc) 209 | 210 | You can dynamically identify mail destination (to, cc, bcc) from event records as: 211 | 212 | 213 | ``` 214 | 215 | type mail 216 | from from@example.com 217 | to_key to 218 | cc_key cc 219 | bcc_key bcc 220 | .... 221 | 222 | ``` 223 | 224 | With this example, `to`, `cc`, `bcc` are dynamically extracted from `record["to"]`, `record["cc"]`, `record["bcc"]` respectively. 225 | 226 | ## ChangeLog 227 | 228 | See [CHANGELOG.md](CHANGELOG.md) for more details. 229 | 230 | ## Development 231 | 232 | Run mail server using [mailcatcher](http://mailcatcher.me/) gem as: 233 | 234 | ``` 235 | $ gem install mailcatcher 236 | $ mailcatcher 237 | ``` 238 | 239 | It starts STMP server on localhost:1025, and has Web UI running on localhost:1080. 240 | 241 | Run Fluentd as: 242 | 243 | ``` 244 | $ bundle exec fluentd -c example/example.conf 245 | ``` 246 | 247 | Put a message to the Fluentd as: 248 | 249 | ``` 250 | $ echo '{"message":"This is a test"}' | bundle exec fluent-cat mail.test 251 | ``` 252 | 253 | ## Copyright 254 | 255 | * Copyright 256 | * Copyright (c) 2012- Yuichi UEMURA 257 | * Copyright (c) 2014- Naotoshi Seo 258 | * License 259 | * Apache License, Version 2.0 260 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require "bundler/gem_tasks" 3 | 4 | require 'rake/testtask' 5 | Rake::TestTask.new(:test) do |test| 6 | test.libs << 'lib' << 'test' 7 | test.pattern = 'test/**/test_*.rb' 8 | test.verbose = true 9 | end 10 | 11 | task :default => :test 12 | -------------------------------------------------------------------------------- /example/example.conf: -------------------------------------------------------------------------------- 1 | # echo '{"message":"This is a test"}' | bundle exec fluent-cat mail.test 2 | 3 | 4 | type forward 5 | 6 | 7 | 8 | type mail 9 | log_level debug 10 | host localhost 11 | port 1025 12 | from from@example.com 13 | to to@example.com 14 | bcc bcc@example.com 15 | subject this is a test 16 | message Hello from out_mail\n[%s] %s 17 | message_out_keys time,message 18 | time_key time 19 | time_locale Asia/Taipei 20 | # localtime false 21 | time_format %Y-%m-%d %H:%M:%S %z 22 | 23 | -------------------------------------------------------------------------------- /example/to_cc_bcc_key.conf: -------------------------------------------------------------------------------- 1 | # echo '{"message":"This is a test","bcc":"bcc@example.com"}' | bundle exec fluent-cat mail.test 2 | 3 | 4 | type forward 5 | 6 | 7 | 8 | type mail 9 | log_level debug 10 | host localhost 11 | port 1025 12 | from from@example.com 13 | to to@example.com 14 | to_key to 15 | bcc_key bcc 16 | subject this is a test 17 | message Hello from out_mail\n[%s] %s 18 | message_out_keys time,message 19 | time_key time 20 | time_locale Asia/Taipei 21 | # localtime false 22 | time_format %Y-%m-%d %H:%M:%S %z 23 | 24 | -------------------------------------------------------------------------------- /fluent-plugin-mail.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | Gem::Specification.new do |gem| 4 | gem.authors = ["Yuichi UEMURA", "Naotoshi Seo"] 5 | gem.email = ["yuichi.u@gmail.com", "sonots@gmail.com"] 6 | gem.description = %q{output plugin for Mail} 7 | gem.summary = %q{output plugin for Mail} 8 | gem.homepage = "https://github.com/u-ichi/fluent-plugin-mail" 9 | gem.files = `git ls-files`.split($\) 10 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 11 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 12 | gem.name = "fluent-plugin-mail" 13 | gem.require_paths = ["lib"] 14 | gem.version = '0.3.0' 15 | gem.license = "Apache-2.0" 16 | 17 | gem.add_runtime_dependency "fluentd", '>= 0.12.0' 18 | gem.add_runtime_dependency "string-scrub" if RUBY_VERSION.to_f < 2.1 19 | gem.add_development_dependency "rake" 20 | gem.add_development_dependency "test-unit" 21 | end 22 | -------------------------------------------------------------------------------- /lib/fluent/plugin/out_mail.rb: -------------------------------------------------------------------------------- 1 | require 'securerandom' 2 | 3 | class Fluent::MailOutput < Fluent::Output 4 | Fluent::Plugin.register_output('mail', self) 5 | 6 | # Define `log` method for v0.10.42 or earlier 7 | unless method_defined?(:log) 8 | define_method("log") { $log } 9 | end 10 | 11 | # For fluentd v0.12.16 or earlier 12 | class << self 13 | unless method_defined?(:desc) 14 | def desc(description) 15 | end 16 | end 17 | end 18 | 19 | desc "Output comma delimited keys" 20 | config_param :out_keys, :array, :default => [] 21 | desc "Format string to construct message body" 22 | config_param :message, :string, :default => nil 23 | desc "Specify comma delimited keys output to `message`" 24 | config_param :message_out_keys, :array, :default => [] 25 | desc "Identify the timestamp of the record" 26 | config_param :time_key, :string, :default => nil 27 | desc "Identify the tag of the record" 28 | config_param :tag_key, :string, :default => 'tag' 29 | desc "SMTP server hostname" 30 | config_param :host, :string 31 | desc "SMTP server port number" 32 | config_param :port, :integer, :default => 25 33 | desc "HELO domain" 34 | config_param :domain, :string, :default => 'localdomain' 35 | desc "User for SMTP Auth" 36 | config_param :user, :string, :default => nil 37 | desc "Password for SMTP Auth" 38 | config_param :password, :string, :default => nil, :secret => true 39 | desc "Type for SMTP Auth such as 'plain', 'login', and 'cram_md5'" 40 | config_param :authtype, :string, :default => 'plain' 41 | desc "MAIL FROM this value" 42 | config_param :from, :string, :default => 'localhost@localdomain' 43 | desc "Mail destination (To)" 44 | config_param :to, :string, :default => '' 45 | desc "Mail destination (Cc)" 46 | config_param :cc, :string, :default => '' 47 | desc "Mail destination (Bcc)" 48 | config_param :bcc, :string, :default => '' 49 | desc "Dyanmically identify mail destination (To) from records" 50 | config_param :to_key, :string, :default => nil 51 | desc "Dynamically identify mail destination (Cc) from records" 52 | config_param :cc_key, :string, :default => nil 53 | desc "Dynamically identify mail destination (Bcc) from records" 54 | config_param :bcc_key, :string, :default => nil 55 | desc "Format string to construct mail subject" 56 | config_param :subject, :string, :default => 'Fluent::MailOutput plugin' 57 | desc "Specify comma delimited keys output to `subject`" 58 | config_param :subject_out_keys, :array, :default => [] 59 | desc "If set to true, enable STARTTLS" 60 | config_param :enable_starttls_auto, :bool, :default => false 61 | desc "If set to true, enable TLS" 62 | config_param :enable_tls, :bool, :default => false 63 | desc "Format string to parse time" 64 | config_param :time_format, :string, :default => "%F %T %z" 65 | desc "Use local time or not" 66 | config_param :localtime, :bool, :default => true 67 | desc "Locale of time" 68 | config_param :time_locale, :default => nil 69 | desc "Specify Content-Type" 70 | config_param :content_type, :string, :default => "text/plain; charset=utf-8" 71 | 72 | def initialize 73 | super 74 | require 'net/smtp' 75 | require 'string/scrub' if RUBY_VERSION.to_f < 2.1 76 | end 77 | 78 | def configure(conf) 79 | super 80 | 81 | if @out_keys.empty? and @message.nil? 82 | raise Fluent::ConfigError, "Either 'message' or 'out_keys' must be specifed." 83 | end 84 | 85 | if @message 86 | begin 87 | @message % (['1'] * @message_out_keys.length) 88 | rescue ArgumentError 89 | raise Fluent::ConfigError, "string specifier '%s' of message and message_out_keys specification mismatch" 90 | end 91 | @create_message_proc = Proc.new {|tag, time, record| create_formatted_message(tag, time, record) } 92 | else 93 | # The default uses the old `key=value` format for old version compatibility 94 | @create_message_proc = Proc.new {|tag, time, record| create_key_value_message(tag, time, record) } 95 | end 96 | 97 | if @to_key or @cc_key or @bcc_key 98 | @process_event_stream_proc = Proc.new {|tag, es| 99 | messages = [] 100 | subjects = [] 101 | dests = [] 102 | 103 | es.each do |time, record| 104 | messages << @create_message_proc.call(tag, time, record) 105 | subjects << create_formatted_subject(tag, time, record) 106 | dests << %w(to cc bcc).each_with_object({}){|t, dest| dest[t] = create_dest_addr(t, record) } 107 | end 108 | 109 | [messages, subjects, dests] 110 | } 111 | else 112 | @process_event_stream_proc = Proc.new {|tag, es| 113 | messages = [] 114 | subjects = [] 115 | dests = [] 116 | 117 | es.each do |time, record| 118 | messages << @create_message_proc.call(tag, time, record) 119 | subjects << create_formatted_subject(tag, time, record) 120 | end 121 | 122 | [messages, subjects, dests] 123 | } 124 | end 125 | 126 | begin 127 | @subject % (['1'] * @subject_out_keys.length) 128 | rescue ArgumentError 129 | raise Fluent::ConfigError, "string specifier '%s' of subject and subject_out_keys specification mismatch" 130 | end 131 | end 132 | 133 | def start 134 | end 135 | 136 | def shutdown 137 | end 138 | 139 | def emit(tag, es, chain) 140 | messages, subjects, dests = @process_event_stream_proc.call(tag, es) 141 | 142 | messages.each_with_index do |message, i| 143 | subject = subjects[i] 144 | dest = dests[i] 145 | begin 146 | sendmail(subject, message, dest) 147 | rescue => e 148 | log.warn "out_mail: failed to send notice to #{@host}:#{@port}, subject: #{subject}, message: #{message}, " << 149 | "error_class: #{e.class}, error_message: #{e.message}, error_backtrace: #{e.backtrace.first}" 150 | end 151 | end 152 | 153 | chain.next 154 | end 155 | 156 | # The old `key=value` format for old version compatibility 157 | def create_key_value_message(tag, time, record) 158 | values = [] 159 | 160 | values = @out_keys.map do |key| 161 | case key 162 | when @time_key 163 | format_time(time, @time_format) 164 | when @tag_key 165 | tag 166 | else 167 | "#{key}: #{record[key].to_s}" 168 | end 169 | end 170 | 171 | values.join("\n") 172 | end 173 | 174 | def create_formatted_message(tag, time, record) 175 | values = [] 176 | 177 | values = @message_out_keys.map do |key| 178 | case key 179 | when @time_key 180 | format_time(time, @time_format) 181 | when @tag_key 182 | tag 183 | else 184 | record[key].to_s 185 | end 186 | end 187 | 188 | message = (@message % values) 189 | with_scrub(message) {|str| str.gsub(/\\n/, "\n") } 190 | end 191 | 192 | def create_formatted_subject(tag, time, record) 193 | values = [] 194 | 195 | values = @subject_out_keys.map do |key| 196 | case key 197 | when @time_key 198 | format_time(time, @time_format) 199 | when @tag_key 200 | tag 201 | else 202 | record[key].to_s 203 | end 204 | end 205 | 206 | @subject % values 207 | end 208 | 209 | def sendmail(subject, msg, dest = nil) 210 | smtp = Net::SMTP.new(@host, @port) 211 | 212 | if @user and @password 213 | smtp_auth_option = [@domain, @user, @password, @authtype.to_sym] 214 | smtp.enable_starttls if @enable_starttls_auto 215 | smtp.enable_tls if @enable_tls 216 | smtp.start(@domain, @user, @password, @authtype.to_sym) 217 | else 218 | smtp.start 219 | end 220 | 221 | subject = subject.force_encoding('binary') 222 | body = msg.force_encoding('binary') 223 | to = (dest && dest['to']) ? dest['to'] : @to 224 | cc = (dest && dest['cc']) ? dest['cc'] : @cc 225 | bcc = (dest && dest['bcc']) ? dest['bcc'] : @bcc 226 | 227 | # Date: header has timezone, so usually it is not necessary to set locale explicitly 228 | # But, for people who would see mail header text directly, the locale information may help something 229 | # (for example, they can tell the sender should live in Tokyo if +0900) 230 | date = format_time(Time.now, "%a, %d %b %Y %X %z") 231 | 232 | mid = sprintf("<%s@%s>", SecureRandom.uuid, SecureRandom.uuid) 233 | content = <= v0.12's TimeFormatter supports timezone, but v0.10 does not 254 | if @time_locale 255 | with_timezone(@time_locale) { Fluent::TimeFormatter.new(time_format, @localtime).format(time) } 256 | else 257 | Fluent::TimeFormatter.new(time_format, @localtime).format(time) 258 | end 259 | end 260 | 261 | def with_timezone(tz) 262 | oldtz, ENV['TZ'] = ENV['TZ'], tz 263 | yield 264 | ensure 265 | ENV['TZ'] = oldtz 266 | end 267 | 268 | def with_scrub(string) 269 | begin 270 | return yield(string) 271 | rescue ArgumentError => e 272 | raise e unless e.message.index("invalid byte sequence in") == 0 273 | log.info "out_mail: invalid byte sequence is replaced in #{string}" 274 | string.scrub!('?') 275 | retry 276 | end 277 | end 278 | 279 | def create_dest_addr(dest_type, record) 280 | addr = instance_variable_get(:"@#{dest_type}") 281 | dest_key = instance_variable_get(:"@#{dest_type}_key") 282 | if dest_key 283 | return record[dest_key] || addr 284 | else 285 | return addr 286 | end 287 | end 288 | end 289 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler' 3 | begin 4 | Bundler.setup(:default, :test) 5 | rescue Bundler::BundlerError => e 6 | $stderr.puts e.message 7 | $stderr.puts "Run `bundle install` to install missing gems" 8 | exit e.status_code 9 | end 10 | require 'test/unit' 11 | 12 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 13 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 14 | require 'fluent/test' 15 | unless ENV.has_key?('VERBOSE') 16 | nulllogger = Object.new 17 | nulllogger.instance_eval {|obj| 18 | def method_missing(method, *args) 19 | # pass 20 | end 21 | } 22 | $log = nulllogger 23 | end 24 | 25 | require 'fluent/plugin/out_mail' 26 | 27 | class Test::Unit::TestCase 28 | end 29 | -------------------------------------------------------------------------------- /test/plugin/test_out_mail.rb: -------------------------------------------------------------------------------- 1 | require_relative '../helper' 2 | 3 | class MailOutputTest < Test::Unit::TestCase 4 | def setup 5 | Fluent::Test.setup 6 | end 7 | 8 | CONFIG_OUT_KEYS = %[ 9 | out_keys tag,time,value 10 | time_key time 11 | time_format %Y/%m/%d %H:%M:%S 12 | tag_key tag 13 | subject Fluentd Notification Alarm %s 14 | subject_out_keys tag 15 | host localhost 16 | port 25 17 | from localhost@localdomain 18 | to localhost@localdomain 19 | ] 20 | 21 | CONFIG_CC_BCC = %[ 22 | out_keys tag,time,value 23 | time_key time 24 | time_format %Y/%m/%d %H:%M:%S 25 | tag_key tag 26 | subject Fluentd Notification Alarm %s 27 | subject_out_keys tag 28 | host localhost 29 | port 25 30 | from localhost@localdomain 31 | cc localhost@localdomain 32 | bcc localhost@localdomain 33 | ] 34 | 35 | CONFIG_MESSAGE = %[ 36 | message out_mail: %s [%s]\\n%s 37 | message_out_keys tag,time,value 38 | time_key time 39 | time_format %Y/%m/%d %H:%M:%S 40 | tag_key tag 41 | subject Fluentd Notification Alarm %s 42 | subject_out_keys tag 43 | host localhost 44 | port 25 45 | from localhost@localdomain 46 | to localhost@localdomain 47 | ] 48 | 49 | CONFIG_DEST_ADDR = %[ 50 | out_keys tag,time,value 51 | time_key time 52 | time_format %Y/%m/%d %H:%M:%S 53 | tag_key tag 54 | subject Fluentd Notification Alarm %s 55 | subject_out_keys tag 56 | host localhost 57 | port 25 58 | from localhost@localdomain 59 | to_key to 60 | cc_key cc 61 | bcc_key bcc 62 | ] 63 | CONFIG_KEYS_WITH_WHITESPACES = %[ 64 | out_keys tag,time, value 65 | time_key time 66 | time_format %Y/%m/%d %H:%M:%S 67 | tag_key tag 68 | message_out_keys msg, log_level 69 | subject Fluentd Notification Alarm %s 70 | subject_out_keys tag, content_id 71 | host localhost 72 | port 25 73 | from localhost@localdomain 74 | to localhost@localdomain 75 | ] 76 | 77 | def create_driver(conf=CONFIG_OUT_KEYS,tag='test') 78 | Fluent::Test::OutputTestDriver.new(Fluent::MailOutput, tag).configure(conf) 79 | end 80 | 81 | def test_configure 82 | d = create_driver(CONFIG_OUT_KEYS) 83 | assert_equal 'localhost', d.instance.host 84 | assert_equal ['tag', 'time', 'value'], d.instance.out_keys 85 | d = create_driver(CONFIG_CC_BCC) 86 | assert_equal 'localhost', d.instance.host 87 | assert_equal ['tag', 'time', 'value'], d.instance.out_keys 88 | d = create_driver(CONFIG_MESSAGE) 89 | assert_equal 'localhost', d.instance.host 90 | assert_equal ['tag', 'time', 'value'], d.instance.message_out_keys 91 | d = create_driver(CONFIG_KEYS_WITH_WHITESPACES) 92 | assert_equal 'localhost', d.instance.host 93 | assert_equal ['tag', 'time', 'value'], d.instance.out_keys 94 | assert_equal ['tag', 'content_id'], d.instance.subject_out_keys 95 | assert_equal ['msg', 'log_level'], d.instance.message_out_keys 96 | end 97 | 98 | def test_out_keys 99 | d = create_driver(CONFIG_OUT_KEYS) 100 | time = Time.now.to_i 101 | d.run do 102 | d.emit({'value' => "out_keys mail from fluentd out_mail"}, time) 103 | end 104 | end 105 | 106 | def test_message 107 | d = create_driver(CONFIG_MESSAGE) 108 | time = Time.now.to_i 109 | d.run do 110 | d.emit({'value' => "message mail from fluentd out_mail"}, time) 111 | end 112 | end 113 | 114 | def test_dest_addr 115 | d = create_driver(CONFIG_DEST_ADDR) 116 | time = Time.now.to_i 117 | d.run do 118 | d.emit({ 119 | 'value' => "message mail from fluentd out_mail", 120 | 'to' => "localhost@localdomain", 121 | 'cc' => "localhost@localdomain", 122 | 'bcc' => "localhost@localdomain", 123 | }, 124 | time) 125 | end 126 | end 127 | 128 | def test_with_scrub 129 | d = create_driver(CONFIG_MESSAGE) 130 | invalid_string = "\xff".force_encoding('UTF-8') 131 | assert_nothing_raised { 132 | res = d.instance.with_scrub(invalid_string) {|str| str.gsub(/\\n/, "\n") } 133 | assert_equal '?', res 134 | } 135 | end 136 | end 137 | 138 | --------------------------------------------------------------------------------