├── .rspec ├── lib ├── fluent_plugin_filter_parse_postfix │ └── version.rb └── fluent │ └── plugin │ └── filter_parse_postfix.rb ├── Gemfile ├── Rakefile ├── .gitignore ├── bin ├── setup └── console ├── .travis.yml ├── spec ├── spec_helper.rb ├── filter_parse_postfix_parse_header_checks_spec.rb └── filter_parse_postfix_spec.rb ├── LICENSE.txt ├── fluent-plugin-filter-parse-postfix.gemspec └── README.md /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /lib/fluent_plugin_filter_parse_postfix/version.rb: -------------------------------------------------------------------------------- 1 | module FluentPluginFilterParsePostfix 2 | VERSION = '0.2.7' 3 | end 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in fluent-plugin-filter-parse-postfix.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rspec/core/rake_task' 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task :default => :spec 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | fluent.conf 11 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: ruby 3 | rvm: 4 | - 2.1.10 5 | - 2.2.6 6 | - 2.3.3 7 | - 2.4.0 8 | script: 9 | - bundle install 10 | - bundle exec rake 11 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "fluent/plugin/filter/parse/postfix" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start 15 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 2 | require 'fluent/test' 3 | require 'fluent/plugin/filter_parse_postfix' 4 | require 'time' 5 | require 'timecop' 6 | 7 | ENV['TZ'] = 'UTC' 8 | 9 | # Disable Test::Unit 10 | module Test::Unit::RunCount; def run(*); end; end 11 | Test::Unit.run = true if defined?(Test::Unit) && Test::Unit.respond_to?(:run=) 12 | 13 | RSpec.configure do |config| 14 | config.before(:all) do 15 | Fluent::Test.setup 16 | end 17 | end 18 | 19 | def create_driver(options = {}) 20 | fluentd_conf = <<-EOS 21 | type parse_postfix 22 | EOS 23 | 24 | options.each do |key, value| 25 | fluentd_conf << <<-EOS 26 | #{key} #{value} 27 | EOS 28 | end 29 | 30 | tag = options[:tag] || 'test.default' 31 | Fluent::Test::FilterTestDriver.new(Fluent::ParsePostfixFilter, tag).configure(fluentd_conf) 32 | end 33 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Genki Sugawara 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /fluent-plugin-filter-parse-postfix.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'fluent_plugin_filter_parse_postfix/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'fluent-plugin-filter-parse-postfix' 8 | spec.version = FluentPluginFilterParsePostfix::VERSION 9 | spec.authors = ['Genki Sugawara'] 10 | spec.email = ['sugawara@cookpad.com'] 11 | 12 | spec.summary = %q{Filter Plugin to parse Postfix status line log.} 13 | spec.description = %q{Filter Plugin to parse Postfix status line log.} 14 | spec.homepage = 'https://github.com/winebarrel/fluent-plugin-filter-parse-postfix' 15 | spec.license = 'MIT' 16 | 17 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 18 | spec.bindir = 'exe' 19 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 20 | spec.require_paths = ['lib'] 21 | 22 | spec.add_dependency 'fluentd', '>= 0.12' 23 | spec.add_dependency 'postfix_status_line', '~> 0.2.8' 24 | 25 | spec.add_development_dependency 'bundler' 26 | spec.add_development_dependency 'rake' 27 | spec.add_development_dependency 'rspec', '>= 3.0.0' 28 | spec.add_development_dependency 'test-unit', '>= 3.1.0' 29 | spec.add_development_dependency 'timecop' 30 | end 31 | -------------------------------------------------------------------------------- /lib/fluent/plugin/filter_parse_postfix.rb: -------------------------------------------------------------------------------- 1 | require 'fluent_plugin_filter_parse_postfix/version' 2 | require 'postfix_status_line' 3 | require 'time' 4 | 5 | module Fluent 6 | class ParsePostfixFilter < Filter 7 | Plugin.register_filter('parse_postfix', self) 8 | 9 | config_param :key, :string, :default => 'message' 10 | config_param :mask, :bool, :default => true 11 | config_param :use_log_time , :bool, :default => false 12 | config_param :include_hash, :bool, :default => false 13 | config_param :salt, :string, :default => nil 14 | config_param :sha_algorithm, :integer, :default => nil 15 | config_param :parse_header_checks, :bool, :default => false 16 | 17 | def filter(tag, time, record) 18 | line = record[@key] 19 | return record unless line 20 | 21 | options = {mask: @mask, hash: @include_hash, salt: @salt, parse_time: @use_log_time, sha_algorithm: @sha_algorithm} 22 | 23 | if @parse_header_checks 24 | parsed = PostfixStatusLine.parse_header_checks(line, options) 25 | else 26 | parsed = PostfixStatusLine.parse(line, options) 27 | end 28 | 29 | unless parsed 30 | log.warn "cannot parse a postfix log: #{line}" 31 | return record 32 | end 33 | 34 | if @use_log_time and parsed['epoch'] 35 | time = parsed.delete('epoch') 36 | end 37 | 38 | parsed 39 | rescue => e 40 | log.warn "failed to parse a postfix log: #{line}", :error_class => e.class, :error => e.message 41 | log.warn_backtrace 42 | record 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec/filter_parse_postfix_parse_header_checks_spec.rb: -------------------------------------------------------------------------------- 1 | describe Fluent::ParsePostfixFilter do 2 | let(:fluentd_conf) { {} } 3 | let(:driver) { create_driver(fluentd_conf) } 4 | let(:today) { Time.parse('2015/05/24 18:30 UTC') } 5 | let(:time) { today.to_i } 6 | let!(:parsed_time) { Time.parse('02/27 09:02:37 +0000').to_i } 7 | 8 | let(:records) do 9 | [ 10 | {"message"=>"Mar 4 14:44:19 P788 postfix/cleanup[7426]: E80A9DF6F7E: warning: header To: sgwr_dts@yahoo.co.jp from local; from= to="}, 11 | {"message"=>"Mar 4 14:44:19 P788 postfix/cleanup[7426]: E80A9DF6F7E: warning: header Subject: test from local; from= to="}, 12 | ] 13 | end 14 | 15 | before do 16 | Timecop.freeze(today) 17 | end 18 | 19 | after do 20 | Timecop.return 21 | end 22 | 23 | subject do 24 | records.each do |record| 25 | driver.emit(record, time) 26 | end 27 | 28 | driver.run 29 | driver.emits 30 | end 31 | 32 | context 'when parse_header_checks_warning mask' do 33 | let(:fluentd_conf) do 34 | {parse_header_checks: true} 35 | end 36 | 37 | it do 38 | is_expected.to match_array [ 39 | ["test.default", 1432492200, {"time"=>"Mar 4 14:44:19", "hostname"=>"P788", "process"=>"postfix/cleanup[7426]", "queue_id"=>"E80A9DF6F7E", "to"=>"********@yahoo.co.jp", "domain"=>"yahoo.co.jp", "from"=>"********@P788.local", "header_from"=>"local", "priority"=>"warning", "To"=>"********@yahoo.co.jp"}], 40 | ["test.default", 1432492200, {"time"=>"Mar 4 14:44:19", "hostname"=>"P788", "process"=>"postfix/cleanup[7426]", "queue_id"=>"E80A9DF6F7E", "to"=>"********@yahoo.co.jp", "domain"=>"yahoo.co.jp", "from"=>"********@P788.local", "header_from"=>"local", "priority"=>"warning", "Subject"=>"test"}], 41 | ] 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fluent-plugin-filter-parse-postfix 2 | 3 | Filter Plugin to parse Postfix status line log. 4 | 5 | [![Build Status](https://travis-ci.org/winebarrel/fluent-plugin-filter-parse-postfix.svg)](https://travis-ci.org/winebarrel/fluent-plugin-filter-parse-postfix) 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | ```ruby 12 | gem 'fluent-plugin-filter-parse-postfix' 13 | ``` 14 | 15 | And then execute: 16 | 17 | $ bundle 18 | 19 | Or install it yourself as: 20 | 21 | $ gem install fluent-plugin-filter-parse-postfix 22 | 23 | ## Configuration 24 | 25 | ```apache 26 | 27 | @type parse_postfix 28 | #key message 29 | #mask true 30 | #use_log_time false 31 | #include_hash false 32 | #salt my_salt 33 | #sha_algorithm 512 # 1, 224, 256, 384, 512 (default) 34 | #parse_header_checks false 35 | 36 | ``` 37 | 38 | ## Usage 39 | 40 | ```sh 41 | $ cat fluent.conf 42 | 43 | @type forward 44 | 45 | 46 | 47 | @type tail 48 | path /var/log/maillog 49 | pos_file /var/log/td-agent/postfix-maillog.pos 50 | tag postfix.maillog 51 | format none 52 | 53 | 54 | 55 | @type grep 56 | regexp1 message status= 57 | 58 | 59 | 60 | @type parse_postfix 61 | 62 | 63 | 64 | @type stdout 65 | 66 | 67 | $ fluentd -c fluent.conf 68 | ``` 69 | 70 | ```sh 71 | $ echo '{"message":"Feb 27 09:02:38 MyHOSTNAME postfix/smtp[26490]: 5E31727A35D: to=, relay=gateway-f1.isp.att.net[204.127.217.17]:25, conn_use=2, delay=0.58, delays=0.11/0.03/0.23/0.20, dsn=2.0.0, status=sent (250 ok ; id=en4req0070M63004172202102)"}' | fluent-cat postfix.maillog 72 | #=> 2015-12-22 02:02:22 +0900 postfix.maillog: {"time":"Feb 27 09:02:38","hostname":"MyHOSTNAME","process":"postfix/smtp[26490]","queue_id":"5E31727A35D","to":"<*********@myemail.net>","domain":"myemail.net","relay":"gateway-f1.isp.att.net[204.127.217.17]:25","conn_use":2,delay":0.58,"delays":"0.11/0.03/0.23/0.20","dsn":"2.0.0","status":"sent","status_detail":"(250 ok ; id=en4req0070M63004172202102)"} 73 | ``` 74 | 75 | ## Output 76 | 77 | see https://github.com/winebarrel/postfix_status_line 78 | 79 | ```json 80 | { 81 | "time":"Feb 27 09:02:38", 82 | "hostname":"MyHOSTNAME", 83 | "process":"postfix/smtp[26490]", 84 | "queue_id":"5E31727A35D", 85 | "to":"*********@myemail.net", 86 | "domain":"myemail.net", 87 | "relay":"gateway-f1.isp.att.net[204.127.217.17]:25", 88 | "conn_use":2, 89 | "delay":0.58, 90 | "delays":"0.11/0.03/0.23/0.20", 91 | "dsn":"2.0.0", 92 | "status":"sent", 93 | "status_detail":"(250 ok ; id=en4req0070M63004172202102)" 94 | } 95 | ``` 96 | 97 | ### Parse [header_checks](http://www.postfix.org/header_checks.5.html) 98 | 99 | ```sh 100 | $ cat fluent.conf 101 | ... 102 | 103 | @type grep 104 | regexp1 message warning: header 105 | 106 | 107 | 108 | @type parse_postfix 109 | parse_header_checks true 110 | 111 | ... 112 | 113 | $ fluentd -c fluent.conf 114 | ``` 115 | 116 | ```sh 117 | $ echo '{"message":"Mar 4 14:44:19 P788 postfix/cleanup[7426]: E80A9DF6F7E: warning: header Subject: test from local; from= to="}' | fluent-cat postfix.maillog 118 | #=> 2017-03-04 18:26:46.146399000 +0900 postfix.maillog: { 119 | # "time":"Mar 4 14:44:19", 120 | # "hostname":"P788", 121 | # "process":"postfix/cleanup[7426]", 122 | # "queue_id":"E80A9DF6F7E", 123 | # "to":"********@yahoo.co.jp", 124 | # "domain":"yahoo.co.jp", 125 | # "from":"********@P788.local", 126 | # "header_from":"local", 127 | # "priority":"warning", 128 | # "Subject":"test"} 129 | ``` 130 | -------------------------------------------------------------------------------- /spec/filter_parse_postfix_spec.rb: -------------------------------------------------------------------------------- 1 | describe Fluent::ParsePostfixFilter do 2 | let(:fluentd_conf) { {} } 3 | let(:driver) { create_driver(fluentd_conf) } 4 | let(:today) { Time.parse('2015/05/24 18:30 UTC') } 5 | let(:time) { today.to_i } 6 | let!(:parsed_time) { Time.parse('02/27 09:02:37 +0000').to_i } 7 | 8 | let(:records) do 9 | [ 10 | {"message"=>"Feb 27 09:02:37 MyHOSTNAME postfix/smtp[26490]: D53A72713E5: to=, relay=gateway-f1.isp.att.net[204.127.217.16]:25, conn_use=2, delay=0.57, delays=0.11/0.03/0.23/0.19, dsn=2.0.0, status=sent (250 ok ; id=20120227140036M0700qer4ne)"}, 11 | {"message"=>"Feb 27 09:02:38 MyHOSTNAME postfix/smtp[26490]: 5E31727A35D: to=, relay=gateway-f1.isp.att.net[204.127.217.17]:25, conn_use=3, delay=0.58, delays=0.11/0.03/0.23/0.20, dsn=2.0.0, status=sent (250 ok ; id=en4req0070M63004172202102)"}, 12 | ] 13 | end 14 | 15 | before do 16 | Timecop.freeze(today) 17 | end 18 | 19 | after do 20 | Timecop.return 21 | end 22 | 23 | subject do 24 | records.each do |record| 25 | driver.emit(record, time) 26 | end 27 | 28 | driver.run 29 | driver.emits 30 | end 31 | 32 | context 'with mask' do 33 | it do 34 | is_expected.to match_array [ 35 | ["test.default", 1432492200, {"time"=>"Feb 27 09:02:37", "hostname"=>"MyHOSTNAME", "process"=>"postfix/smtp[26490]", "queue_id"=>"D53A72713E5", "to"=>"*******@bellsouth.net", "domain"=>"bellsouth.net", "relay"=>"gateway-f1.isp.att.net[204.127.217.16]:25", "conn_use"=>2, "delay"=>0.57, "delays"=>"0.11/0.03/0.23/0.19", "dsn"=>"2.0.0", "status"=>"sent", "status_detail"=>"(250 ok ; id=20120227140036M0700qer4ne)"}], 36 | ["test.default", 1432492200, {"time"=>"Feb 27 09:02:38", "hostname"=>"MyHOSTNAME", "process"=>"postfix/smtp[26490]", "queue_id"=>"5E31727A35D", "to"=>"*********@myemail.net", "domain"=>"myemail.net", "relay"=>"gateway-f1.isp.att.net[204.127.217.17]:25", "conn_use"=>3, "delay"=>0.58, "delays"=>"0.11/0.03/0.23/0.20", "dsn"=>"2.0.0", "status"=>"sent", "status_detail"=>"(250 ok ; id=en4req0070M63004172202102)"}], 37 | ] 38 | end 39 | end 40 | 41 | context 'when use log time' do 42 | let(:fluentd_conf) do 43 | {use_log_time: true} 44 | end 45 | 46 | it do 47 | is_expected.to match_array [ 48 | ["test.default", 1432492200, {"time"=>"Feb 27 09:02:37", "hostname"=>"MyHOSTNAME", "process"=>"postfix/smtp[26490]", "queue_id"=>"D53A72713E5", "to"=>"*******@bellsouth.net", "domain"=>"bellsouth.net", "relay"=>"gateway-f1.isp.att.net[204.127.217.16]:25", "conn_use"=>2, "delay"=>0.57, "delays"=>"0.11/0.03/0.23/0.19", "dsn"=>"2.0.0", "status_detail"=>"(250 ok ; id=20120227140036M0700qer4ne)", "status"=>"sent"}], 49 | ["test.default", 1432492200, {"time"=>"Feb 27 09:02:38", "hostname"=>"MyHOSTNAME", "process"=>"postfix/smtp[26490]", "queue_id"=>"5E31727A35D", "to"=>"*********@myemail.net", "domain"=>"myemail.net", "relay"=>"gateway-f1.isp.att.net[204.127.217.17]:25", "conn_use"=>3, "delay"=>0.58, "delays"=>"0.11/0.03/0.23/0.20", "dsn"=>"2.0.0", "status_detail"=>"(250 ok ; id=en4req0070M63004172202102)", "status"=>"sent"}], 50 | ] 51 | end 52 | end 53 | 54 | context 'without mask' do 55 | let(:fluentd_conf) do 56 | {mask: false} 57 | end 58 | 59 | it do 60 | is_expected.to match_array [ 61 | ["test.default", 1432492200, {"time"=>"Feb 27 09:02:37", "hostname"=>"MyHOSTNAME", "process"=>"postfix/smtp[26490]", "queue_id"=>"D53A72713E5", "to"=>"myemail@bellsouth.net", "domain"=>"bellsouth.net", "relay"=>"gateway-f1.isp.att.net[204.127.217.16]:25", "conn_use"=>2, "delay"=>0.57, "delays"=>"0.11/0.03/0.23/0.19", "dsn"=>"2.0.0", "status"=>"sent", "status_detail"=>"(250 ok ; id=20120227140036M0700qer4ne)"}], 62 | ["test.default", 1432492200, {"time"=>"Feb 27 09:02:38", "hostname"=>"MyHOSTNAME", "process"=>"postfix/smtp[26490]", "queue_id"=>"5E31727A35D", "to"=>"bellsouth@myemail.net", "domain"=>"myemail.net", "relay"=>"gateway-f1.isp.att.net[204.127.217.17]:25", "conn_use"=>3, "delay"=>0.58, "delays"=>"0.11/0.03/0.23/0.20", "dsn"=>"2.0.0", "status"=>"sent", "status_detail"=>"(250 ok ; id=en4req0070M63004172202102)"}], 63 | ] 64 | end 65 | end 66 | 67 | context 'when cannot parse' do 68 | let(:records) do 69 | [ 70 | {"message"=>"Feb 27 09:02:37 MyHOSTNAME postfix/smtp[26490] x D53A72713E5: to=, relay=gateway-f1.isp.att.net[204.127.217.16]:25, delay=0.57, delays=0.11/0.03/0.23/0.19, dsn=2.0.0, status=sent (250 ok ; id=20120227140036M0700qer4ne)"}, 71 | {"message"=>"Feb 27 09:02:38 MyHOSTNAME postfix/smtp[26490]: 5E31727A35D: to=, relay=gateway-f1.isp.att.net[204.127.217.17]:25, delay=0.58, delays=0.11/0.03/0.23/0.20, dsn=2.0.0, status=sent (250 ok ; id=en4req0070M63004172202102)"}, 72 | ] 73 | end 74 | 75 | before do 76 | expect(driver.instance.log).to receive(:warn).with('cannot parse a postfix log: Feb 27 09:02:37 MyHOSTNAME postfix/smtp[26490] x D53A72713E5: to=, relay=gateway-f1.isp.att.net[204.127.217.16]:25, delay=0.57, delays=0.11/0.03/0.23/0.19, dsn=2.0.0, status=sent (250 ok ; id=20120227140036M0700qer4ne)') 77 | end 78 | 79 | it do 80 | is_expected.to match_array [ 81 | ["test.default", 1432492200, {"message"=>"Feb 27 09:02:37 MyHOSTNAME postfix/smtp[26490] x D53A72713E5: to=, relay=gateway-f1.isp.att.net[204.127.217.16]:25, delay=0.57, delays=0.11/0.03/0.23/0.19, dsn=2.0.0, status=sent (250 ok ; id=20120227140036M0700qer4ne)"}], 82 | ["test.default", 1432492200, {"time"=>"Feb 27 09:02:38", "hostname"=>"MyHOSTNAME", "process"=>"postfix/smtp[26490]", "queue_id"=>"5E31727A35D", "to"=>"*********@myemail.net", "domain"=>"myemail.net", "relay"=>"gateway-f1.isp.att.net[204.127.217.17]:25", "delay"=>0.58, "delays"=>"0.11/0.03/0.23/0.20", "dsn"=>"2.0.0", "status_detail"=>"(250 ok ; id=en4req0070M63004172202102)", "status"=>"sent"}], 83 | ] 84 | end 85 | end 86 | 87 | context 'when expired' do 88 | let(:records) do 89 | [ 90 | {"message"=>"May 29 19:21:17 testserver postfix/qmgr[4833]: 9D7FE1D0051: from=, status=expired, returned to sender"}, 91 | {"message"=>"Feb 27 09:02:38 MyHOSTNAME postfix/smtp[26490]: 5E31727A35D: to=, relay=gateway-f1.isp.att.net[204.127.217.17]:25, delay=0.58, delays=0.11/0.03/0.23/0.20, dsn=2.0.0, status=sent (250 ok ; id=en4req0070M63004172202102)"}, 92 | ] 93 | end 94 | 95 | it do 96 | is_expected.to match_array [ 97 | ["test.default", 1432492200, {"time"=>"May 29 19:21:17", "hostname"=>"testserver", "process"=>"postfix/qmgr[4833]", "queue_id"=>"9D7FE1D0051", "from"=>"****@test.hogehoge", "status_detail"=>" returned to sender", "status"=>"expired"}], 98 | ["test.default", 1432492200, {"time"=>"Feb 27 09:02:38", "hostname"=>"MyHOSTNAME", "process"=>"postfix/smtp[26490]", "queue_id"=>"5E31727A35D", "to"=>"*********@myemail.net", "domain"=>"myemail.net", "relay"=>"gateway-f1.isp.att.net[204.127.217.17]:25", "delay"=>0.58, "delays"=>"0.11/0.03/0.23/0.20", "dsn"=>"2.0.0", "status_detail"=>"(250 ok ; id=en4req0070M63004172202102)", "status"=>"sent"}], 99 | ] 100 | end 101 | end 102 | 103 | context 'include hash' do 104 | let(:fluentd_conf) do 105 | {include_hash: true, salt: 'my_salt'} 106 | end 107 | 108 | it do 109 | is_expected.to match_array [ 110 | ["test.default", 1432492200, {"time"=>"Feb 27 09:02:37", "hostname"=>"MyHOSTNAME", "process"=>"postfix/smtp[26490]", "queue_id"=>"D53A72713E5", "hash"=>"f275e00cdebc8ae2e85e632cd9ad1e795c631f10c91058f880693ba1c4f3c28029e642ebb2b73050bd0e0123d8a8a4513946c5832f12f14ab2338482bd703799", "to"=>"*******@bellsouth.net", "domain"=>"bellsouth.net", "relay"=>"gateway-f1.isp.att.net[204.127.217.16]:25", "conn_use"=>2, "delay"=>0.57, "delays"=>"0.11/0.03/0.23/0.19", "dsn"=>"2.0.0", "status_detail"=>"(250 ok ; id=20120227140036M0700qer4ne)", "status"=>"sent"}], 111 | ["test.default", 1432492200, {"time"=>"Feb 27 09:02:38", "hostname"=>"MyHOSTNAME", "process"=>"postfix/smtp[26490]", "queue_id"=>"5E31727A35D", "hash"=>"c56ab6964ac53f423f849ddd8befd65fbd94db3b3fbe2ef018d933ec066e73e1666eea05c345d66fc2b7eabf7208019fc8bd3fa705d17c275d5859131a49cccc", "to"=>"*********@myemail.net", "domain"=>"myemail.net", "relay"=>"gateway-f1.isp.att.net[204.127.217.17]:25", "conn_use"=>3, "delay"=>0.58, "delays"=>"0.11/0.03/0.23/0.20", "dsn"=>"2.0.0", "status_detail"=>"(250 ok ; id=en4req0070M63004172202102)", "status"=>"sent"}], 112 | ] 113 | end 114 | end 115 | 116 | context 'include hash (sha1)' do 117 | let(:fluentd_conf) do 118 | {include_hash: true, salt: 'my_salt', sha_algorithm: 1} 119 | end 120 | 121 | it do 122 | is_expected.to match_array [ 123 | ["test.default", 1432492200, {"time"=>"Feb 27 09:02:37", "hostname"=>"MyHOSTNAME", "process"=>"postfix/smtp[26490]", "queue_id"=>"D53A72713E5", "hash"=>"86c53c0fc7e7f7d4b68e319d565611cc6c2c5b5b", "to"=>"*******@bellsouth.net", "domain"=>"bellsouth.net", "relay"=>"gateway-f1.isp.att.net[204.127.217.16]:25", "conn_use"=>2, "delay"=>0.57, "delays"=>"0.11/0.03/0.23/0.19", "dsn"=>"2.0.0", "status_detail"=>"(250 ok ; id=20120227140036M0700qer4ne)", "status"=>"sent"}], 124 | ["test.default", 1432492200, {"time"=>"Feb 27 09:02:38", "hostname"=>"MyHOSTNAME", "process"=>"postfix/smtp[26490]", "queue_id"=>"5E31727A35D", "hash"=>"d31a27746e3f9f1a59fd6eba9e28bb7540ec07a3", "to"=>"*********@myemail.net", "domain"=>"myemail.net", "relay"=>"gateway-f1.isp.att.net[204.127.217.17]:25", "conn_use"=>3, "delay"=>0.58, "delays"=>"0.11/0.03/0.23/0.20", "dsn"=>"2.0.0", "status_detail"=>"(250 ok ; id=en4req0070M63004172202102)", "status"=>"sent"}], 125 | ] 126 | end 127 | end 128 | 129 | context 'when error happen' do 130 | before do 131 | expect(PostfixStatusLine).to receive(:parse).and_raise('unknown error') 132 | expect(PostfixStatusLine).to receive(:parse).and_return('parse' => 'OK') 133 | 134 | expect(driver.instance.log).to receive(:warn).with( 135 | "failed to parse a postfix log: Feb 27 09:02:37 MyHOSTNAME postfix/smtp[26490]: D53A72713E5: to=, relay=gateway-f1.isp.att.net[204.127.217.16]:25, conn_use=2, delay=0.57, delays=0.11/0.03/0.23/0.19, dsn=2.0.0, status=sent (250 ok ; id=20120227140036M0700qer4ne)", 136 | {:error_class=>RuntimeError, :error=>"unknown error"}) 137 | expect(driver.instance.log).to receive(:warn_backtrace) 138 | end 139 | 140 | it do 141 | is_expected.to match_array [ 142 | ["test.default", 1432492200, {"message"=>"Feb 27 09:02:37 MyHOSTNAME postfix/smtp[26490]: D53A72713E5: to=, relay=gateway-f1.isp.att.net[204.127.217.16]:25, conn_use=2, delay=0.57, delays=0.11/0.03/0.23/0.19, dsn=2.0.0, status=sent (250 ok ; id=20120227140036M0700qer4ne)"}], 143 | ["test.default", 1432492200, {"parse"=>"OK"}], 144 | ] 145 | end 146 | end 147 | end 148 | --------------------------------------------------------------------------------