fluent-plugin-sendmail 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +11 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +246 -0
- data/Rakefile +12 -0
- data/fluent-plugin-sendmail.gemspec +24 -0
- data/lib/fluent/plugin/in_sendmail.rb +159 -0
- data/lib/fluent/plugin/sendmailparser.rb +133 -0
- data/test/data/data1 +9 -0
- data/test/data/data1_bundle_result_expect +1 -0
- data/test/data/data1_unbundle_result_expect +7 -0
- data/test/helper.rb +28 -0
- data/test/plugin/test_in_sendmail.rb +93 -0
- metadata +134 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 762057e467dfc06c0841eea887bd8e9fc72e8a75
|
4
|
+
data.tar.gz: 6a9091a8f37b174451ab2d988279914d608e215f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f6da0f543c49864a99534a42576d6bb80e39be4f44b4b7a7a8473fcdd6357e217eb73e5a0b6b42de5ba8992190fea70f5a669172237c25ec10ee2f2cd9ee9619
|
7
|
+
data.tar.gz: ee3372514f3b16bbedcb1397256745066c048e5e233ffa0dba2d717204920f4e1511981d78758595d796b02230e7825b64eba1f8e3690d4a6c98eb3c6e568696
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 muddydixon
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
# Fluent::Plugin::Sendmail
|
2
|
+
|
3
|
+
Fluentd plugin to parse and merge sendmail syslog.
|
4
|
+
|
5
|
+
## Configuration
|
6
|
+
|
7
|
+
```
|
8
|
+
<source>
|
9
|
+
type sendmail
|
10
|
+
path ./syslog.log
|
11
|
+
pos_file ./syslog.log.pos
|
12
|
+
tag sendmail
|
13
|
+
</source>
|
14
|
+
```
|
15
|
+
|
16
|
+
example of sendmail log
|
17
|
+
|
18
|
+
```
|
19
|
+
Apr 2 00:15:25 mta001 sendmail[32300]: u31FFPtp032300: Milter: no active filter
|
20
|
+
Apr 2 00:15:25 mta001 sendmail[32300]: u31FFPtp032300: from=<grandeur09@gmail.com>, size=5938, class=0, nrcpts=5, msgid=<201604011515.u31FFIAj012911@gmail.com>, proto=ESMTP, daemon=MTA, relay=[64.233.187.27]
|
21
|
+
Apr 2 00:15:25 mta001 sendmail[32302]: u31FFPtp032300: SMTP outgoing connect on [192.168.198.81]
|
22
|
+
Apr 2 00:15:25 mta001 sendmail[32302]: u31FFPtp032300: to=<sent1@example.com>,<sent2@example.com>, 00:00:00, xdelay=00:00:00, mailer=esmtp, pri=245938, relay=[93.184.216.34] [93.184.216.34], dsn=2.0.0, stat=Sent (ok: Message 40279894 accepted)
|
23
|
+
Apr 2 00:15:25 mta001 sendmail[12566]: u31FFPtp032300: to=<deferred1@example.com>, delay=00:00:15, xdelay=00:00:15, mailer=esmtp, pri=34527, relay=[93.184.216.34] [93.184.216.34], dsn=4.3.5, stat=Deferred: 451 4.3.5 Server configuration problem
|
24
|
+
Apr 2 00:15:26 mta001 sendmail[32302]: u31FFPtp032300: to=<sent3@example2.com>,<sent4@example2.com>, delay=00:00:00, xdelay=00:00:00, mailer=esmtp, pri=245938, relay=[93.184.216.34] [93.184.216.34], dsn=2.0.0, stat=Sent (ok: Message 40279895 accepted)
|
25
|
+
Apr 2 00:18:50 mta001 sendmail[32302]: u31FFPtp032300: to=<deferred1@example.com>, delay=00:00:00, xdelay=00:00:00, mailer=esmtp, pri=245938, relay=[93.184.216.34] [93.184.216.34], dsn=2.0.0, stat=Sent (ok: Message 40279894 accepted)
|
26
|
+
Apr 2 00:15:25 mta001 sendmail[32302]: u31FFPtp032300: done; delay=00:00:00, ntries=2
|
27
|
+
```
|
28
|
+
|
29
|
+
This plugin emit record like below:
|
30
|
+
|
31
|
+
```
|
32
|
+
2014-01-10 01:00:01 +0900 sendmail: {
|
33
|
+
"mta":"mta001",
|
34
|
+
"from":"<grandeur09@gmail.com>",
|
35
|
+
"relay":{
|
36
|
+
"ip":"64.233.187.27",
|
37
|
+
"host":null
|
38
|
+
},
|
39
|
+
"count":"5",
|
40
|
+
"size":"5938",
|
41
|
+
"msgid":"<201604011515.u31FFIAj012911@gmail.com>",
|
42
|
+
"popid":null,
|
43
|
+
"authid":null,
|
44
|
+
"to":[
|
45
|
+
{
|
46
|
+
"to":[
|
47
|
+
"<sent1@example.com>",
|
48
|
+
"<sent2@example.com>"
|
49
|
+
],
|
50
|
+
"00:00:00":null,
|
51
|
+
"xdelay":"00:00:00",
|
52
|
+
"mailer":"esmtp",
|
53
|
+
"pri":"245938",
|
54
|
+
"relay":{
|
55
|
+
"ip":"93.184.216.34",
|
56
|
+
"host":null
|
57
|
+
},
|
58
|
+
"dsn":"2.0.0",
|
59
|
+
"stat":"Sent (ok: Message 40279894 accepted)"
|
60
|
+
},
|
61
|
+
{
|
62
|
+
"to":[
|
63
|
+
"<sent3@example2.com>",
|
64
|
+
"<sent4@example2.com>"
|
65
|
+
],
|
66
|
+
"delay":"00:00:00",
|
67
|
+
"xdelay":"00:00:00",
|
68
|
+
"mailer":"esmtp",
|
69
|
+
"pri":"245938",
|
70
|
+
"relay":{
|
71
|
+
"ip":"93.184.216.34",
|
72
|
+
"host":null
|
73
|
+
},
|
74
|
+
"dsn":"2.0.0",
|
75
|
+
"stat":"Sent (ok: Message 40279895 accepted)"
|
76
|
+
},
|
77
|
+
{
|
78
|
+
"to":[
|
79
|
+
"<deferred1@example.com>"
|
80
|
+
],
|
81
|
+
"delay":"00:00:00",
|
82
|
+
"xdelay":"00:00:00",
|
83
|
+
"mailer":"esmtp",
|
84
|
+
"pri":"245938",
|
85
|
+
"relay":{
|
86
|
+
"ip":"93.184.216.34",
|
87
|
+
"host":null
|
88
|
+
},
|
89
|
+
"dsn":"2.0.0",
|
90
|
+
"stat":"Sent (ok: Message 40279894 accepted)"
|
91
|
+
}
|
92
|
+
]
|
93
|
+
}
|
94
|
+
```
|
95
|
+
|
96
|
+
### unbundle
|
97
|
+
|
98
|
+
unbundle mode
|
99
|
+
|
100
|
+
```
|
101
|
+
<source>
|
102
|
+
type sendmail
|
103
|
+
path ./syslog.log
|
104
|
+
pos_file ./syslog.log.pos
|
105
|
+
tag sendmail
|
106
|
+
unbundle yes
|
107
|
+
</source>
|
108
|
+
```
|
109
|
+
|
110
|
+
This plugin emit record like below:
|
111
|
+
|
112
|
+
```
|
113
|
+
2014-01-10 01:00:01 +0900 sendmail: {
|
114
|
+
"mta":"mta001",
|
115
|
+
"from":"<grandeur09@gmail.com>",
|
116
|
+
"relay":{
|
117
|
+
"ip":"93.184.216.34",
|
118
|
+
"host":null
|
119
|
+
},
|
120
|
+
"count":"5",
|
121
|
+
"size":"5938",
|
122
|
+
"msgid":"<201604011515.u31FFIAj012911@gmail.com>",
|
123
|
+
"popid":null,
|
124
|
+
"authid":null,
|
125
|
+
"to":"<sent1@example.com>",
|
126
|
+
"stat":"Sent (ok: Message 40279894 accepted)",
|
127
|
+
"dsn":"2.0.0",
|
128
|
+
"delay":null,
|
129
|
+
"xdelay":"00:00:00"
|
130
|
+
}
|
131
|
+
|
132
|
+
2014-01-10 01:00:01 +0900 sendmail: {
|
133
|
+
"mta":"mta001",
|
134
|
+
"from":"<grandeur09@gmail.com>",
|
135
|
+
"relay":{
|
136
|
+
"ip":"93.184.216.34",
|
137
|
+
"host":null
|
138
|
+
},
|
139
|
+
"count":"5",
|
140
|
+
"size":"5938",
|
141
|
+
"msgid":"<201604011515.u31FFIAj012911@gmail.com>",
|
142
|
+
"popid":null,
|
143
|
+
"authid":null,
|
144
|
+
"to":"<sent2@example.com>",
|
145
|
+
"stat":"Sent (ok: Message 40279894 accepted)",
|
146
|
+
"dsn":"2.0.0",
|
147
|
+
"delay":null,
|
148
|
+
"xdelay":"00:00:00"
|
149
|
+
}
|
150
|
+
|
151
|
+
2014-01-10 01:00:01 +0900 sendmail: {
|
152
|
+
"mta":"mta001",
|
153
|
+
"from":"<grandeur09@gmail.com>",
|
154
|
+
"relay":{
|
155
|
+
"ip":"93.184.216.34",
|
156
|
+
"host":null
|
157
|
+
},
|
158
|
+
"count":"5",
|
159
|
+
"size":"5938",
|
160
|
+
"msgid":"<201604011515.u31FFIAj012911@gmail.com>",
|
161
|
+
"popid":null,
|
162
|
+
"authid":null,
|
163
|
+
"to":"<deferred1@example.com>",
|
164
|
+
"stat":"Deferred: 451 4.3.5 Server configuration problem",
|
165
|
+
"dsn":"4.3.5",
|
166
|
+
"delay":"00:00:15",
|
167
|
+
"xdelay":"00:00:15"
|
168
|
+
}
|
169
|
+
|
170
|
+
2014-01-10 01:00:01 +0900 sendmail: {
|
171
|
+
"mta":"mta001",
|
172
|
+
"from":"<grandeur09@gmail.com>",
|
173
|
+
"relay":{
|
174
|
+
"ip":"93.184.216.34",
|
175
|
+
"host":null
|
176
|
+
},
|
177
|
+
"count":"5",
|
178
|
+
"size":"5938",
|
179
|
+
"msgid":"<201604011515.u31FFIAj012911@gmail.com>",
|
180
|
+
"popid":null,
|
181
|
+
"authid":null,
|
182
|
+
"to":"<sent3@example2.com>",
|
183
|
+
"stat":"Sent (ok: Message 40279895 accepted)",
|
184
|
+
"dsn":"2.0.0",
|
185
|
+
"delay":"00:00:00",
|
186
|
+
"xdelay":"00:00:00"
|
187
|
+
}
|
188
|
+
|
189
|
+
2014-01-10 01:00:01 +0900 sendmail: {
|
190
|
+
"mta":"mta001",
|
191
|
+
"from":"<grandeur09@gmail.com>",
|
192
|
+
"relay":{
|
193
|
+
"ip":"93.184.216.34",
|
194
|
+
"host":null
|
195
|
+
},
|
196
|
+
"count":"5",
|
197
|
+
"size":"5938",
|
198
|
+
"msgid":"<201604011515.u31FFIAj012911@gmail.com>",
|
199
|
+
"popid":null,
|
200
|
+
"authid":null,
|
201
|
+
"to":"<sent4@example2.com>",
|
202
|
+
"stat":"Sent (ok: Message 40279895 accepted)",
|
203
|
+
"dsn":"2.0.0",
|
204
|
+
"delay":"00:00:00",
|
205
|
+
"xdelay":"00:00:00"
|
206
|
+
}
|
207
|
+
|
208
|
+
2014-01-10 01:00:01 +0900 sendmail: {
|
209
|
+
"mta":"mta001",
|
210
|
+
"from":"<grandeur09@gmail.com>",
|
211
|
+
"relay":{
|
212
|
+
"ip":"93.184.216.34",
|
213
|
+
"host":null
|
214
|
+
},
|
215
|
+
"count":"5",
|
216
|
+
"size":"5938",
|
217
|
+
"msgid":"<201604011515.u31FFIAj012911@gmail.com>",
|
218
|
+
"popid":null,
|
219
|
+
"authid":null,
|
220
|
+
"to":"<deferred1@example.com>",
|
221
|
+
"stat":"Sent (ok: Message 40279894 accepted)",
|
222
|
+
"dsn":"2.0.0",
|
223
|
+
"delay":"00:00:00",
|
224
|
+
"xdelay":"00:00:00"
|
225
|
+
}
|
226
|
+
```
|
227
|
+
|
228
|
+
## TODO
|
229
|
+
|
230
|
+
tracking bounce.
|
231
|
+
|
232
|
+
## ChangeLog
|
233
|
+
|
234
|
+
See [CHANGELOG.md](CHANGELOG.md) for details.
|
235
|
+
|
236
|
+
## Contributing
|
237
|
+
|
238
|
+
1. Fork it
|
239
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
240
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
241
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
242
|
+
5. Create new Pull Request
|
243
|
+
|
244
|
+
## Copyright
|
245
|
+
|
246
|
+
Copyright (c) 2014 muddydixon. See [LICENSE](LICENSE) for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "fluent-plugin-sendmail"
|
7
|
+
spec.version = "0.1.0"
|
8
|
+
spec.authors = ["muddydixon"]
|
9
|
+
spec.email = ["muddydixon@gmail.com"]
|
10
|
+
spec.summary = "Fluentd plugin to parse and merge sendmail syslog."
|
11
|
+
spec.description = spec.summary
|
12
|
+
spec.homepage = "https://github.com/muddydixon/fluent-plugin-sendmail"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
spec.add_runtime_dependency("lru_redux", [">= 0.8.4"])
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
21
|
+
spec.add_development_dependency 'test-unit'
|
22
|
+
spec.add_runtime_dependency "fluentd"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
class Fluent::SendmailInput < Fluent::TailInput
|
3
|
+
config_param :lrucache_size, :integer, :default => (1024*1024)
|
4
|
+
|
5
|
+
Fluent::Plugin.register_input('sendmail', self)
|
6
|
+
|
7
|
+
require_relative 'sendmailparser'
|
8
|
+
require 'pathname'
|
9
|
+
require 'lru_redux'
|
10
|
+
|
11
|
+
config_param :types, :string, :default => 'from,sent'
|
12
|
+
config_param :unbundle, :string, :default => 'no'
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
super
|
16
|
+
@delivers = LruRedux::ThreadSafeCache.new(@lrucache_size)
|
17
|
+
end
|
18
|
+
|
19
|
+
def configure_parser(conf)
|
20
|
+
@parser = SendmailParser.new(conf)
|
21
|
+
if @unbundle == 'yes'
|
22
|
+
@do_unbundle = true
|
23
|
+
else
|
24
|
+
@do_unbundle = false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def receive_lines(lines)
|
29
|
+
es = Fluent::MultiEventStream.new
|
30
|
+
lines.each {|line|
|
31
|
+
begin
|
32
|
+
line.chomp! # remove \n
|
33
|
+
logline = parse_line(line)
|
34
|
+
|
35
|
+
if logline.nil?
|
36
|
+
next
|
37
|
+
end
|
38
|
+
|
39
|
+
type = logline["type"]
|
40
|
+
mta = logline["mta"]
|
41
|
+
qid = logline["qid"]
|
42
|
+
time = logline["time"]
|
43
|
+
# a qid is not uniq worldwide.
|
44
|
+
# make delivery id uniq even if multiple MTA's log are mixed.
|
45
|
+
deliveryid = mta + qid
|
46
|
+
type = logline["type"]
|
47
|
+
noncommon = logline["noncommon"]
|
48
|
+
|
49
|
+
case type
|
50
|
+
when :from
|
51
|
+
# new log
|
52
|
+
from = noncommon
|
53
|
+
record = from.record
|
54
|
+
@delivers[deliveryid] = SendmailLog.new(mta, time, record)
|
55
|
+
when :to
|
56
|
+
to = noncommon
|
57
|
+
status = to.status
|
58
|
+
record = noncommon.record
|
59
|
+
if @delivers.has_key?(deliveryid)
|
60
|
+
to = noncommon
|
61
|
+
status = to.status
|
62
|
+
case status
|
63
|
+
when :sent, :sent_local, :bounced
|
64
|
+
@delivers[deliveryid].dequeued(time, record)
|
65
|
+
if @do_unbundle
|
66
|
+
log_single(es, deliveryid, time, record)
|
67
|
+
end
|
68
|
+
# bulked queue in an attempt have been dequeued completely.
|
69
|
+
if @delivers[deliveryid].status == :all_dequeued
|
70
|
+
# bundled logs are outputed here
|
71
|
+
if not @do_unbundle
|
72
|
+
log_bundled(es, deliveryid, time)
|
73
|
+
end
|
74
|
+
# log.destroy
|
75
|
+
@delivers.delete(qid)
|
76
|
+
end
|
77
|
+
when :deferred
|
78
|
+
if @do_unbundle
|
79
|
+
log_single(es, deliveryid, time, record)
|
80
|
+
end
|
81
|
+
when :other
|
82
|
+
$log.warn "cannot find this kind of delivery status: " + line.dump
|
83
|
+
end
|
84
|
+
else
|
85
|
+
# cannot find any 'from' line corresponded to the 'to' line
|
86
|
+
end
|
87
|
+
end
|
88
|
+
rescue
|
89
|
+
$log.warn line.dump, :error=>$!.to_s
|
90
|
+
$log.debug_backtrace
|
91
|
+
end
|
92
|
+
}
|
93
|
+
|
94
|
+
unless es.empty?
|
95
|
+
begin
|
96
|
+
Fluent::Engine.emit_stream(@tag, es)
|
97
|
+
rescue
|
98
|
+
# ignore errors. Engine shows logs and backtraces.
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def log_single(es, deliveryid, time, record)
|
104
|
+
log = @delivers[deliveryid]
|
105
|
+
records = log.record_unbundle(time, record)
|
106
|
+
for record in records do
|
107
|
+
es.add(time, record)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def log_bundled(es, deliveryid, time)
|
112
|
+
log = @delivers[deliveryid]
|
113
|
+
es.add(time, log.record)
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
class SendmailLog
|
119
|
+
attr_reader :status
|
120
|
+
attr_reader :time
|
121
|
+
def initialize(mta, time, record)
|
122
|
+
@mta = mta
|
123
|
+
@status = :init
|
124
|
+
@time = time
|
125
|
+
@tos = []
|
126
|
+
@from = record
|
127
|
+
@count = record["nrcpts"].to_i
|
128
|
+
end
|
129
|
+
|
130
|
+
def record
|
131
|
+
return {
|
132
|
+
"mta" => @mta,
|
133
|
+
"from" => @from,
|
134
|
+
"to" => @tos
|
135
|
+
}
|
136
|
+
end
|
137
|
+
|
138
|
+
def record_unbundle(time, record)
|
139
|
+
records_unbundled = []
|
140
|
+
for to in record["to"] do
|
141
|
+
record_single = record.dup
|
142
|
+
record_single["to"] = to
|
143
|
+
records_unbundled.push({
|
144
|
+
"mta" => @mta,
|
145
|
+
"from" => @from,
|
146
|
+
"to" => record_single
|
147
|
+
})
|
148
|
+
end
|
149
|
+
return records_unbundled
|
150
|
+
end
|
151
|
+
|
152
|
+
def dequeued(time, record)
|
153
|
+
@count = @count - record["to"].size
|
154
|
+
@tos.push(record)
|
155
|
+
if @count == 0
|
156
|
+
@status = :all_dequeued
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
class SendmailParser
|
2
|
+
def initialize(conf)
|
3
|
+
@base_regexp = /^(?<time>\w+\s+\w+\s+\d+:\d+:\d+) (?<host>[^ ]+) (?<procowner>[^\[]+)\[(?<procid>\d+)\]: (?<qid>[^ ]+): (?<entry>(?<type>[^=]+).+)$/;
|
4
|
+
end
|
5
|
+
|
6
|
+
def parse(value)
|
7
|
+
m = @base_regexp.match(value)
|
8
|
+
unless m
|
9
|
+
# $log.warn "sendmail: pattern not match: #{value.inspect}"
|
10
|
+
return nil
|
11
|
+
end
|
12
|
+
|
13
|
+
logtype = m["type"]
|
14
|
+
entry = m["entry"]
|
15
|
+
mta = m["host"]
|
16
|
+
qid = m["qid"]
|
17
|
+
time = Time.parse(m["time"]).to_i || Fluent::Engine.now.to_i
|
18
|
+
|
19
|
+
logline = {
|
20
|
+
"type" => :from,
|
21
|
+
"mta" => mta,
|
22
|
+
"qid" => qid,
|
23
|
+
"time" => time,
|
24
|
+
"type" => nil,
|
25
|
+
"noncommon" => nil
|
26
|
+
}
|
27
|
+
|
28
|
+
case logtype
|
29
|
+
when "from"
|
30
|
+
fromline = self.from_parser(entry)
|
31
|
+
logline["type"] = :from
|
32
|
+
logline["noncommon"] = fromline
|
33
|
+
when "to"
|
34
|
+
toline = self.to_parser(entry)
|
35
|
+
logline["type"] = :to
|
36
|
+
logline["noncommon"] = toline
|
37
|
+
else
|
38
|
+
# not match
|
39
|
+
logline = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
logline
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_line(entry)
|
46
|
+
record = {}
|
47
|
+
status = nil
|
48
|
+
|
49
|
+
status = status_parser(entry)
|
50
|
+
|
51
|
+
entry.split(", ").each {|param|
|
52
|
+
key, val = param.split("=")
|
53
|
+
record[key] = val
|
54
|
+
}
|
55
|
+
record["to"] = record["to"].split(",")
|
56
|
+
|
57
|
+
if record.has_key?("relay")
|
58
|
+
record["relay"] = relay_parser(record["relay"])
|
59
|
+
end
|
60
|
+
ToLine.new(status, record)
|
61
|
+
end
|
62
|
+
|
63
|
+
def from_line(entry)
|
64
|
+
record = {}
|
65
|
+
|
66
|
+
entry.split(", ").each {|param|
|
67
|
+
key, val = param.split("=")
|
68
|
+
record[key] = val
|
69
|
+
}
|
70
|
+
if record.has_key?("relay")
|
71
|
+
record["relay"] = relay_parser(record["relay"])
|
72
|
+
end
|
73
|
+
FromLine.new(record)
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_parser(entry)
|
77
|
+
to_line(entry)
|
78
|
+
end
|
79
|
+
|
80
|
+
def from_parser(entry)
|
81
|
+
from_line(entry)
|
82
|
+
end
|
83
|
+
|
84
|
+
def relay_parser(relays)
|
85
|
+
relay_host = nil
|
86
|
+
relay_ip = nil
|
87
|
+
relays.split(" ").each {|relay|
|
88
|
+
if relay.index("[") == 0
|
89
|
+
return {"ip" => trim_bracket(relay), "host" => relay_host}
|
90
|
+
else
|
91
|
+
relay_host = relay
|
92
|
+
end
|
93
|
+
}
|
94
|
+
return {"ip" => relay_ip, "host" => relay_host}
|
95
|
+
end
|
96
|
+
|
97
|
+
def trim_bracket(val)
|
98
|
+
val[1..-2]
|
99
|
+
end
|
100
|
+
|
101
|
+
def status_parser(entry)
|
102
|
+
if entry.include?("stat=Sent")
|
103
|
+
if entry.include?("mailer=local,")
|
104
|
+
return :sent_local
|
105
|
+
else
|
106
|
+
return :sent
|
107
|
+
end
|
108
|
+
elsif entry.include?("dsn=5.")
|
109
|
+
return :bounced
|
110
|
+
elsif entry.include?("stat=Deferred")
|
111
|
+
return :deferred
|
112
|
+
else
|
113
|
+
return :other
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class FromLine
|
119
|
+
attr_reader :record
|
120
|
+
def initialize(record)
|
121
|
+
@record = record
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class ToLine
|
126
|
+
attr_reader :status
|
127
|
+
attr_reader :record
|
128
|
+
def initialize(status, record)
|
129
|
+
@status = status
|
130
|
+
@record = record
|
131
|
+
@record["canonical_status"] = status.to_s
|
132
|
+
end
|
133
|
+
end
|
data/test/data/data1
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Apr 2 00:15:25 mta001 sendmail[32300]: u31FFPtp032300: Milter: no active filter
|
2
|
+
Apr 2 00:15:25 mta001 sendmail[32300]: u31FFPtp032300: from=<grandeur09@gmail.com>, size=5938, class=0, nrcpts=6, msgid=<201604011515.u31FFIAj012911@gmail.com>, proto=ESMTP, daemon=MTA, relay=[64.233.187.27]
|
3
|
+
Apr 2 00:15:25 mta001 sendmail[32302]: u31FFPtp032300: SMTP outgoing connect on [192.168.198.81]
|
4
|
+
Apr 2 00:15:25 mta001 sendmail[32302]: u31FFPtp032300: to=<sent1@example.com>,<sent2@example.com>, 00:00:00, xdelay=00:00:00, mailer=esmtp, pri=245938, relay=[93.184.216.34] [93.184.216.34], dsn=2.0.0, stat=Sent (ok: Message 40279894 accepted)
|
5
|
+
Apr 2 00:15:25 mta001 sendmail[12566]: u31FFPtp032300: to=<deferred1@example.com>, delay=00:00:15, xdelay=00:00:15, mailer=esmtp, pri=34527, relay=[93.184.216.34] [93.184.216.34], dsn=4.3.5, stat=Deferred: 451 4.3.5 Server configuration problem
|
6
|
+
Apr 2 00:15:26 mta001 sendmail[32302]: u31FFPtp032300: to=<sent3@example2.com>,<sent4@example2.com>, delay=00:00:00, xdelay=00:00:00, mailer=esmtp, pri=245938, relay=[93.184.216.34] [93.184.216.34], dsn=2.0.0, stat=Sent (ok: Message 40279895 accepted)
|
7
|
+
Apr 2 00:18:50 mta001 sendmail[32302]: u31FFPtp032300: to=<deferred1@example.com>, delay=00:00:00, xdelay=00:00:00, mailer=esmtp, pri=245938, relay=[93.184.216.34] [93.184.216.34], dsn=2.0.0, stat=Sent (ok: Message 40279894 accepted)
|
8
|
+
Apr 2 00:15:25 mta001 sendmail[32302]: u31FFPtp032300: done; delay=00:00:00, ntries=2
|
9
|
+
Apr 2 00:15:25 mta001 sendmail[32302]: u31FFPtp032300: to=root@localhost.localdomain, ctladdr=root@localhost.localdomain (0/0), delay=00:00:00, xdelay=00:00:00, mailer=local, pri=245938, dsn=2.0.0, stat=Sent
|
@@ -0,0 +1 @@
|
|
1
|
+
{"mta":"mta001","from": {"from":"<grandeur09@gmail.com>","size":"5938", "class":"0", "nrcpts":"6","msgid":"<201604011515.u31FFIAj012911@gmail.com>","proto":"ESMTP","daemon":"MTA","relay":{"ip":"64.233.187.27", "host":null}},"to":[{"to":["<sent1@example.com>", "<sent2@example.com>"],"00:00:00":null,"xdelay":"00:00:00","mailer":"esmtp","pri":"245938","relay":{"ip":"93.184.216.34", "host":null},"dsn":"2.0.0","stat":"Sent (ok: Message 40279894 accepted)","canonical_status":"sent"},{"to":["<sent3@example2.com>", "<sent4@example2.com>"],"delay":"00:00:00","xdelay":"00:00:00","mailer":"esmtp","pri":"245938","relay":{"ip":"93.184.216.34", "host":null},"dsn":"2.0.0","stat":"Sent (ok: Message 40279895 accepted)","canonical_status":"sent"},{"to":["<deferred1@example.com>"],"delay":"00:00:00","xdelay":"00:00:00","mailer":"esmtp","pri":"245938","relay":{"ip":"93.184.216.34", "host":null},"dsn":"2.0.0","stat":"Sent (ok: Message 40279894 accepted)","canonical_status":"sent"},{"to":["root@localhost.localdomain"],"ctladdr":"root@localhost.localdomain (0/0)","delay":"00:00:00","xdelay":"00:00:00","mailer":"local","pri":"245938","dsn":"2.0.0","stat":"Sent","canonical_status":"sent_local"}]}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
{"mta":"mta001", "from":{"from":"<grandeur09@gmail.com>", "size":"5938", "class":"0", "nrcpts":"6", "msgid":"<201604011515.u31FFIAj012911@gmail.com>", "proto":"ESMTP", "daemon":"MTA", "relay":{"ip":"64.233.187.27", "host":null}}, "to":{"to":"<sent1@example.com>", "00:00:00":null, "xdelay":"00:00:00", "mailer":"esmtp", "pri":"245938", "relay":{"ip":"93.184.216.34", "host":null}, "dsn":"2.0.0", "stat":"Sent (ok: Message 40279894 accepted)", "canonical_status":"sent"}}
|
2
|
+
{"mta":"mta001", "from":{"from":"<grandeur09@gmail.com>", "size":"5938", "class":"0", "nrcpts":"6", "msgid":"<201604011515.u31FFIAj012911@gmail.com>", "proto":"ESMTP", "daemon":"MTA", "relay":{"ip":"64.233.187.27", "host":null}}, "to":{"to":"<sent2@example.com>", "00:00:00":null, "xdelay":"00:00:00", "mailer":"esmtp", "pri":"245938", "relay":{"ip":"93.184.216.34", "host":null}, "dsn":"2.0.0", "stat":"Sent (ok: Message 40279894 accepted)", "canonical_status":"sent"}}
|
3
|
+
{"mta":"mta001", "from":{"from":"<grandeur09@gmail.com>", "size":"5938", "class":"0", "nrcpts":"6", "msgid":"<201604011515.u31FFIAj012911@gmail.com>", "proto":"ESMTP", "daemon":"MTA", "relay":{"ip":"64.233.187.27", "host":null}}, "to":{"to":"<deferred1@example.com>", "delay":"00:00:15", "xdelay":"00:00:15", "mailer":"esmtp", "pri":"34527", "relay":{"ip":"93.184.216.34", "host":null}, "dsn":"4.3.5", "stat":"Deferred: 451 4.3.5 Server configuration problem", "canonical_status":"deferred"}}
|
4
|
+
{"mta":"mta001", "from":{"from":"<grandeur09@gmail.com>", "size":"5938", "class":"0", "nrcpts":"6", "msgid":"<201604011515.u31FFIAj012911@gmail.com>", "proto":"ESMTP", "daemon":"MTA", "relay":{"ip":"64.233.187.27", "host":null}}, "to":{"to":"<sent3@example2.com>", "delay":"00:00:00", "xdelay":"00:00:00", "mailer":"esmtp", "pri":"245938", "relay":{"ip":"93.184.216.34", "host":null}, "dsn":"2.0.0", "stat":"Sent (ok: Message 40279895 accepted)", "canonical_status":"sent"}}
|
5
|
+
{"mta":"mta001", "from":{"from":"<grandeur09@gmail.com>", "size":"5938", "class":"0", "nrcpts":"6", "msgid":"<201604011515.u31FFIAj012911@gmail.com>", "proto":"ESMTP", "daemon":"MTA", "relay":{"ip":"64.233.187.27", "host":null}}, "to":{"to":"<sent4@example2.com>", "delay":"00:00:00", "xdelay":"00:00:00", "mailer":"esmtp", "pri":"245938", "relay":{"ip":"93.184.216.34", "host":null}, "dsn":"2.0.0", "stat":"Sent (ok: Message 40279895 accepted)", "canonical_status":"sent"}}
|
6
|
+
{"mta":"mta001", "from":{"from":"<grandeur09@gmail.com>", "size":"5938", "class":"0", "nrcpts":"6", "msgid":"<201604011515.u31FFIAj012911@gmail.com>", "proto":"ESMTP", "daemon":"MTA", "relay":{"ip":"64.233.187.27", "host":null}}, "to":{"to":"<deferred1@example.com>", "delay":"00:00:00", "xdelay":"00:00:00", "mailer":"esmtp", "pri":"245938", "relay":{"ip":"93.184.216.34", "host":null}, "dsn":"2.0.0", "stat":"Sent (ok: Message 40279894 accepted)", "canonical_status":"sent"}}
|
7
|
+
{"mta":"mta001", "from":{"from":"<grandeur09@gmail.com>", "size":"5938", "class":"0", "nrcpts":"6", "msgid":"<201604011515.u31FFIAj012911@gmail.com>", "proto":"ESMTP", "daemon":"MTA", "relay":{"ip":"64.233.187.27", "host":null}}, "to":{"to":"root@localhost.localdomain", "ctladdr":"root@localhost.localdomain (0/0)", "delay":"00:00:00", "xdelay":"00:00:00", "mailer":"local", "pri":"245938", "dsn":"2.0.0", "stat":"Sent", "canonical_status":"sent_local"}}
|
data/test/helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
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
|
+
end
|
23
|
+
|
24
|
+
require 'fluent/plugin/in_tail'
|
25
|
+
require 'fluent/plugin/in_sendmail'
|
26
|
+
|
27
|
+
class Test::Unit::TestCase
|
28
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class SendmailInputTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
Fluent::Test.setup
|
7
|
+
end
|
8
|
+
|
9
|
+
TMP_DIR = File.dirname(__FILE__) + "/../tmp"
|
10
|
+
DATA_DIR = File.dirname(__FILE__) + "/../data"
|
11
|
+
|
12
|
+
CONFIG_UNBUNDLE = %[
|
13
|
+
path #{TMP_DIR}/sendmaillog
|
14
|
+
tag sendmail
|
15
|
+
unbundle yes
|
16
|
+
]
|
17
|
+
|
18
|
+
CONFIG_BUNDLE = %[
|
19
|
+
path #{TMP_DIR}/sendmaillog
|
20
|
+
tag sendmail
|
21
|
+
unbundle no
|
22
|
+
]
|
23
|
+
|
24
|
+
def setup
|
25
|
+
Fluent::Test.setup
|
26
|
+
FileUtils.rm_rf(TMP_DIR)
|
27
|
+
FileUtils.mkdir_p(TMP_DIR)
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_driver(conf = CONFIG_UNBUNDLE, tag='test')
|
31
|
+
driver = Fluent::Test::InputTestDriver.new(Fluent::SendmailInput)
|
32
|
+
driver.configure(conf)
|
33
|
+
driver
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_configure
|
37
|
+
#### set configurations
|
38
|
+
# d = create_driver %[
|
39
|
+
# path test_path
|
40
|
+
# compress gz
|
41
|
+
# ]
|
42
|
+
#### check configurations
|
43
|
+
# assert_equal 'test_path', d.instance.path
|
44
|
+
# assert_equal :gz, d.instance.compress
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_unbundled
|
48
|
+
data_file = "#{DATA_DIR}/data1"
|
49
|
+
expect_file = "#{DATA_DIR}/data1_unbundle_result_expect"
|
50
|
+
driver = create_driver
|
51
|
+
do_test(driver, data_file, expect_file)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_bundled
|
55
|
+
data_file = "#{DATA_DIR}/data1"
|
56
|
+
expect_file = "#{DATA_DIR}/data1_bundle_result_expect"
|
57
|
+
driver = create_driver(conf=CONFIG_BUNDLE)
|
58
|
+
do_test(driver, data_file, expect_file)
|
59
|
+
end
|
60
|
+
|
61
|
+
def do_test(driver, data_file, expect_file)
|
62
|
+
lines = nil
|
63
|
+
path = driver.instance.paths[0].to_s
|
64
|
+
|
65
|
+
# touch tail file
|
66
|
+
File.open(path, "a") {}
|
67
|
+
|
68
|
+
# result_expect
|
69
|
+
expects = []
|
70
|
+
File.open(expect_file, "r") {|expectfile|
|
71
|
+
expectfile.each_line {|line|
|
72
|
+
expects.push(JSON.parse(line))
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
driver.run do
|
77
|
+
sleep 1
|
78
|
+
File.open(data_file, "r") {|srcfile|
|
79
|
+
File.open(path, "a") {|dstfile|
|
80
|
+
srcfile.each_line {|line|
|
81
|
+
dstfile.puts(line)
|
82
|
+
}
|
83
|
+
}
|
84
|
+
}
|
85
|
+
sleep 1
|
86
|
+
end
|
87
|
+
|
88
|
+
emits = driver.emits
|
89
|
+
emits.each_index {|i|
|
90
|
+
assert_equal(expects[i], emits[i][2])
|
91
|
+
}
|
92
|
+
end
|
93
|
+
end
|
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-plugin-sendmail
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- muddydixon
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: lru_redux
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.8.4
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.8.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: test-unit
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: fluentd
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Fluentd plugin to parse and merge sendmail syslog.
|
84
|
+
email:
|
85
|
+
- muddydixon@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".travis.yml"
|
92
|
+
- CHANGELOG.md
|
93
|
+
- Gemfile
|
94
|
+
- LICENSE
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- fluent-plugin-sendmail.gemspec
|
98
|
+
- lib/fluent/plugin/in_sendmail.rb
|
99
|
+
- lib/fluent/plugin/sendmailparser.rb
|
100
|
+
- test/data/data1
|
101
|
+
- test/data/data1_bundle_result_expect
|
102
|
+
- test/data/data1_unbundle_result_expect
|
103
|
+
- test/helper.rb
|
104
|
+
- test/plugin/test_in_sendmail.rb
|
105
|
+
homepage: https://github.com/muddydixon/fluent-plugin-sendmail
|
106
|
+
licenses:
|
107
|
+
- MIT
|
108
|
+
metadata: {}
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 2.4.8
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: Fluentd plugin to parse and merge sendmail syslog.
|
129
|
+
test_files:
|
130
|
+
- test/data/data1
|
131
|
+
- test/data/data1_bundle_result_expect
|
132
|
+
- test/data/data1_unbundle_result_expect
|
133
|
+
- test/helper.rb
|
134
|
+
- test/plugin/test_in_sendmail.rb
|