fluent-plugin-tailpath 0.0.1
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 +14 -0
- data/Gemfile +3 -0
- data/LICENSE +38 -0
- data/README.md +27 -0
- data/fluent-plugin-tailpath.gemspec +25 -0
- data/lib/fluent/plugin/in_tailpath.rb +497 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ebda3e465349195927fef2fab4ca4b0926b960ae
|
4
|
+
data.tar.gz: 7bce2f30a26b4c2d5085f38ccdfba842f134d006
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c9c77945298c37e6f2da4ee3de05c9f39da1b14c048a2d651e41d3bf011bd4098a691e714b8345a081bab52f889006685c9ba1e31bc045ac31e3619d02ed4cb1
|
7
|
+
data.tar.gz: ee91142c0cb273a69a5d84da297df1b73b31992cc122ee58cf4c8f2575a160ea07ac3b0069560c1a55e555c67a6545464bdac2fcce0ae0031b537581323c965a
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
Copyright (c) 2014 Jacob Wirth
|
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.
|
23
|
+
|
24
|
+
Apache 2.0 License
|
25
|
+
|
26
|
+
Copyright (C) 2011 FURUHASHI Sadayuki
|
27
|
+
|
28
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
29
|
+
you may not use this file except in compliance with the License.
|
30
|
+
You may obtain a copy of the License at
|
31
|
+
|
32
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
33
|
+
|
34
|
+
Unless required by applicable law or agreed to in writing, software
|
35
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
36
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
37
|
+
See the License for the specific language governing permissions and
|
38
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# fluent-plugin-tailpath
|
2
|
+
|
3
|
+
Fluentd plugin to tail files and add the file path to the message
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Use RubyGems:
|
8
|
+
|
9
|
+
gem install fluent-plugin-tailpath
|
10
|
+
|
11
|
+
## Configuration
|
12
|
+
|
13
|
+
For more info, the configuration is identical to [im_file](http://docs.fluentd.org/articles/in_tail)
|
14
|
+
|
15
|
+
Example:
|
16
|
+
|
17
|
+
<source>
|
18
|
+
type tailpath
|
19
|
+
path /var/log/httpd-access.log
|
20
|
+
pos_file /var/log/td-agent/httpd-access.log.pos
|
21
|
+
tag apache.access
|
22
|
+
format apache2
|
23
|
+
</source>
|
24
|
+
|
25
|
+
## Copyright
|
26
|
+
|
27
|
+
Copyright (c) 2014 Jacob Wirth. See [LICENSE](LICENSE) for details.
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "fluent-plugin-tailpath"
|
6
|
+
gem.version = "0.0.1"
|
7
|
+
gem.authors = ["Jacob Wirth"]
|
8
|
+
gem.email = "xthexder@live.com"
|
9
|
+
gem.homepage = "https://github.com/xthexder/fluent-plugin-tailpath"
|
10
|
+
gem.description = "Fluentd plugin to tail files and add the file path to the message"
|
11
|
+
gem.summary = gem.description
|
12
|
+
gem.licenses = ["MIT"]
|
13
|
+
gem.has_rdoc = false
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split("\n")
|
16
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
|
20
|
+
gem.add_dependency "fluentd", "~> 0.10.17"
|
21
|
+
gem.add_development_dependency "rake"
|
22
|
+
gem.add_development_dependency "rspec"
|
23
|
+
gem.add_development_dependency "pry"
|
24
|
+
gem.add_development_dependency "pry-nav"
|
25
|
+
end
|
@@ -0,0 +1,497 @@
|
|
1
|
+
#
|
2
|
+
# Fluent
|
3
|
+
#
|
4
|
+
# Copyright (C) 2011 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module Fluent
|
19
|
+
class TailPathInput < Input
|
20
|
+
Plugin.register_input('tailpath', self)
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
super
|
24
|
+
@paths = []
|
25
|
+
end
|
26
|
+
|
27
|
+
config_param :path, :string
|
28
|
+
config_param :tag, :string
|
29
|
+
config_param :rotate_wait, :time, :default => 5
|
30
|
+
config_param :pos_file, :string, :default => nil
|
31
|
+
|
32
|
+
attr_reader :paths
|
33
|
+
|
34
|
+
def configure(conf)
|
35
|
+
super
|
36
|
+
|
37
|
+
@paths = @path.split(',').map {|path| path.strip }
|
38
|
+
if @paths.empty?
|
39
|
+
raise ConfigError, "tailpath: 'path' parameter is required on tail input"
|
40
|
+
end
|
41
|
+
|
42
|
+
unless @pos_file
|
43
|
+
$log.warn "'pos_file PATH' parameter is not set to a 'tail' source."
|
44
|
+
$log.warn "this parameter is highly recommended to save the position to resume tailing."
|
45
|
+
end
|
46
|
+
|
47
|
+
configure_parser(conf)
|
48
|
+
end
|
49
|
+
|
50
|
+
def configure_parser(conf)
|
51
|
+
@parser = TextParser.new
|
52
|
+
@parser.configure(conf)
|
53
|
+
end
|
54
|
+
|
55
|
+
def start
|
56
|
+
if @pos_file
|
57
|
+
@pf_file = File.open(@pos_file, File::RDWR|File::CREAT, DEFAULT_FILE_PERMISSION)
|
58
|
+
@pf_file.sync = true
|
59
|
+
@pf = PositionFile.parse(@pf_file)
|
60
|
+
end
|
61
|
+
|
62
|
+
@loop = Coolio::Loop.new
|
63
|
+
@tails = @paths.map {|path|
|
64
|
+
pe = @pf ? @pf[path] : MemoryPositionEntry.new
|
65
|
+
TailWatcher.new(path, @rotate_wait, pe, &method(:receive_lines))
|
66
|
+
}
|
67
|
+
@tails.each {|tail|
|
68
|
+
tail.attach(@loop)
|
69
|
+
}
|
70
|
+
@thread = Thread.new(&method(:run))
|
71
|
+
end
|
72
|
+
|
73
|
+
def shutdown
|
74
|
+
@tails.each {|tail|
|
75
|
+
tail.close
|
76
|
+
}
|
77
|
+
@loop.stop
|
78
|
+
@thread.join
|
79
|
+
@pf_file.close if @pf_file
|
80
|
+
end
|
81
|
+
|
82
|
+
def run
|
83
|
+
@loop.run
|
84
|
+
rescue
|
85
|
+
$log.error "unexpected error", :error=>$!.to_s
|
86
|
+
$log.error_backtrace
|
87
|
+
end
|
88
|
+
|
89
|
+
def receive_lines(path, lines)
|
90
|
+
es = MultiEventStream.new
|
91
|
+
lines.each {|line|
|
92
|
+
begin
|
93
|
+
line.chomp! # remove \n
|
94
|
+
time, record = parse_line(line)
|
95
|
+
if time && record
|
96
|
+
record['path'] = path
|
97
|
+
es.add(time, record)
|
98
|
+
end
|
99
|
+
rescue
|
100
|
+
$log.warn line.dump, :error=>$!.to_s
|
101
|
+
$log.debug_backtrace
|
102
|
+
end
|
103
|
+
}
|
104
|
+
|
105
|
+
unless es.empty?
|
106
|
+
begin
|
107
|
+
Engine.emit_stream(@tag, es)
|
108
|
+
rescue
|
109
|
+
# ignore errors. Engine shows logs and backtraces.
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def parse_line(line)
|
115
|
+
return @parser.parse(line)
|
116
|
+
end
|
117
|
+
|
118
|
+
class TailWatcher
|
119
|
+
def initialize(path, rotate_wait, pe, &receive_lines)
|
120
|
+
@path = path
|
121
|
+
@rotate_wait = rotate_wait
|
122
|
+
@pe = pe || MemoryPositionEntry.new
|
123
|
+
@receive_lines = receive_lines
|
124
|
+
|
125
|
+
@rotate_queue = []
|
126
|
+
|
127
|
+
@timer_trigger = TimerWatcher.new(1, true, &method(:on_notify))
|
128
|
+
@stat_trigger = StatWatcher.new(path, &method(:on_notify))
|
129
|
+
|
130
|
+
@rotate_handler = RotateHandler.new(path, &method(:on_rotate))
|
131
|
+
@io_handler = nil
|
132
|
+
end
|
133
|
+
|
134
|
+
def attach(loop)
|
135
|
+
@timer_trigger.attach(loop)
|
136
|
+
@stat_trigger.attach(loop)
|
137
|
+
on_notify
|
138
|
+
end
|
139
|
+
|
140
|
+
def detach
|
141
|
+
@timer_trigger.detach if @timer_trigger.attached?
|
142
|
+
@stat_trigger.detach if @stat_trigger.attached?
|
143
|
+
end
|
144
|
+
|
145
|
+
def close
|
146
|
+
@rotate_queue.reject! {|req|
|
147
|
+
req.io.close
|
148
|
+
true
|
149
|
+
}
|
150
|
+
detach
|
151
|
+
end
|
152
|
+
|
153
|
+
def on_notify
|
154
|
+
@rotate_handler.on_notify
|
155
|
+
return unless @io_handler
|
156
|
+
@io_handler.on_notify
|
157
|
+
|
158
|
+
# proceeds rotate queue
|
159
|
+
return if @rotate_queue.empty?
|
160
|
+
@rotate_queue.first.tick
|
161
|
+
|
162
|
+
while @rotate_queue.first.ready?
|
163
|
+
if io = @rotate_queue.first.io
|
164
|
+
stat = io.stat
|
165
|
+
inode = stat.ino
|
166
|
+
if inode == @pe.read_inode
|
167
|
+
# rotated file has the same inode number with the last file.
|
168
|
+
# assuming following situation:
|
169
|
+
# a) file was once renamed and backed, or
|
170
|
+
# b) symlink or hardlink to the same file is recreated
|
171
|
+
# in either case, seek to the saved position
|
172
|
+
pos = @pe.read_pos
|
173
|
+
else
|
174
|
+
pos = io.pos
|
175
|
+
end
|
176
|
+
@pe.update(inode, pos)
|
177
|
+
io_handler = IOHandler.new(io, @pe, &@receive_lines)
|
178
|
+
else
|
179
|
+
io_handler = NullIOHandler.new
|
180
|
+
end
|
181
|
+
@io_handler.close
|
182
|
+
@io_handler = io_handler
|
183
|
+
@rotate_queue.shift
|
184
|
+
break if @rotate_queue.empty?
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def on_rotate(io)
|
189
|
+
if @io_handler == nil
|
190
|
+
if io
|
191
|
+
# first time
|
192
|
+
stat = io.stat
|
193
|
+
fsize = stat.size
|
194
|
+
inode = stat.ino
|
195
|
+
|
196
|
+
last_inode = @pe.read_inode
|
197
|
+
if inode == last_inode
|
198
|
+
# seek to the saved position
|
199
|
+
pos = @pe.read_pos
|
200
|
+
elsif last_inode != 0
|
201
|
+
# this is FilePositionEntry and fluentd once started.
|
202
|
+
# read data from the head of the rotated file.
|
203
|
+
# logs never duplicate because this file is a rotated new file.
|
204
|
+
pos = 0
|
205
|
+
@pe.update(inode, pos)
|
206
|
+
else
|
207
|
+
# this is MemoryPositionEntry or this is the first time fluentd started.
|
208
|
+
# seek to the end of the any files.
|
209
|
+
# logs may duplicate without this seek because it's not sure the file is
|
210
|
+
# existent file or rotated new file.
|
211
|
+
pos = fsize
|
212
|
+
@pe.update(inode, pos)
|
213
|
+
end
|
214
|
+
io.seek(pos)
|
215
|
+
|
216
|
+
@io_handler = IOHandler.new(io, @pe, &@receive_lines)
|
217
|
+
else
|
218
|
+
@io_handler = NullIOHandler.new
|
219
|
+
end
|
220
|
+
|
221
|
+
else
|
222
|
+
if io && @rotate_queue.find {|req| req.io == io }
|
223
|
+
return
|
224
|
+
end
|
225
|
+
last_io = @rotate_queue.empty? ? @io_handler.io : @rotate_queue.last.io
|
226
|
+
if last_io == nil
|
227
|
+
$log.info "detected rotation of #{@path}"
|
228
|
+
# rotate imeediately if previous file is nil
|
229
|
+
wait = 0
|
230
|
+
else
|
231
|
+
$log.info "detected rotation of #{@path}; waiting #{@rotate_wait} seconds"
|
232
|
+
wait = @rotate_wait
|
233
|
+
wait -= @rotate_queue.first.wait unless @rotate_queue.empty?
|
234
|
+
end
|
235
|
+
@rotate_queue << RotationRequest.new(io, wait)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
class TimerWatcher < Coolio::TimerWatcher
|
240
|
+
def initialize(interval, repeat, &callback)
|
241
|
+
@callback = callback
|
242
|
+
super(interval, repeat)
|
243
|
+
end
|
244
|
+
|
245
|
+
def on_timer
|
246
|
+
@callback.call
|
247
|
+
rescue
|
248
|
+
# TODO log?
|
249
|
+
$log.error $!.to_s
|
250
|
+
$log.error_backtrace
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
class StatWatcher < Coolio::StatWatcher
|
255
|
+
def initialize(path, &callback)
|
256
|
+
@callback = callback
|
257
|
+
super(path)
|
258
|
+
end
|
259
|
+
|
260
|
+
def on_change(prev, cur)
|
261
|
+
@callback.call
|
262
|
+
rescue
|
263
|
+
# TODO log?
|
264
|
+
$log.error $!.to_s
|
265
|
+
$log.error_backtrace
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
class RotationRequest
|
270
|
+
def initialize(io, wait)
|
271
|
+
@io = io
|
272
|
+
@wait = wait
|
273
|
+
end
|
274
|
+
|
275
|
+
attr_reader :io, :wait
|
276
|
+
|
277
|
+
def tick
|
278
|
+
@wait -= 1
|
279
|
+
end
|
280
|
+
|
281
|
+
def ready?
|
282
|
+
@wait <= 0
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
MAX_LINES_AT_ONCE = 1000
|
287
|
+
|
288
|
+
class IOHandler
|
289
|
+
def initialize(io, pe, &receive_lines)
|
290
|
+
$log.info "following tail of #{io.path}"
|
291
|
+
@io = io
|
292
|
+
@pe = pe
|
293
|
+
@receive_lines = receive_lines
|
294
|
+
@buffer = ''.force_encoding('ASCII-8BIT')
|
295
|
+
@iobuf = ''.force_encoding('ASCII-8BIT')
|
296
|
+
end
|
297
|
+
|
298
|
+
attr_reader :io
|
299
|
+
|
300
|
+
def on_notify
|
301
|
+
begin
|
302
|
+
lines = []
|
303
|
+
read_more = false
|
304
|
+
|
305
|
+
begin
|
306
|
+
while true
|
307
|
+
if @buffer.empty?
|
308
|
+
@io.read_nonblock(2048, @buffer)
|
309
|
+
else
|
310
|
+
@buffer << @io.read_nonblock(2048, @iobuf)
|
311
|
+
end
|
312
|
+
while line = @buffer.slice!(/.*?\n/m)
|
313
|
+
lines << line
|
314
|
+
end
|
315
|
+
if lines.size >= MAX_LINES_AT_ONCE
|
316
|
+
# not to use too much memory in case the file is very large
|
317
|
+
read_more = true
|
318
|
+
break
|
319
|
+
end
|
320
|
+
end
|
321
|
+
rescue EOFError
|
322
|
+
end
|
323
|
+
|
324
|
+
unless lines.empty?
|
325
|
+
@receive_lines.call(@io.path, lines)
|
326
|
+
@pe.update_pos(@io.pos - @buffer.bytesize)
|
327
|
+
end
|
328
|
+
|
329
|
+
end while read_more
|
330
|
+
|
331
|
+
rescue
|
332
|
+
$log.error $!.to_s
|
333
|
+
$log.error_backtrace
|
334
|
+
close
|
335
|
+
end
|
336
|
+
|
337
|
+
def close
|
338
|
+
@io.close unless @io.closed?
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
class NullIOHandler
|
343
|
+
def initialize
|
344
|
+
end
|
345
|
+
|
346
|
+
def io
|
347
|
+
end
|
348
|
+
|
349
|
+
def on_notify
|
350
|
+
end
|
351
|
+
|
352
|
+
def close
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
class RotateHandler
|
357
|
+
def initialize(path, &on_rotate)
|
358
|
+
@path = path
|
359
|
+
@inode = nil
|
360
|
+
@fsize = -1 # first
|
361
|
+
@on_rotate = on_rotate
|
362
|
+
end
|
363
|
+
|
364
|
+
def on_notify
|
365
|
+
begin
|
366
|
+
io = File.open(@path)
|
367
|
+
stat = io.stat
|
368
|
+
inode = stat.ino
|
369
|
+
fsize = stat.size
|
370
|
+
rescue Errno::ENOENT
|
371
|
+
# moved or deleted
|
372
|
+
inode = nil
|
373
|
+
fsize = 0
|
374
|
+
end
|
375
|
+
|
376
|
+
begin
|
377
|
+
if @inode != inode || fsize < @fsize
|
378
|
+
# rotated or truncated
|
379
|
+
@on_rotate.call(io)
|
380
|
+
io = nil
|
381
|
+
end
|
382
|
+
|
383
|
+
@inode = inode
|
384
|
+
@fsize = fsize
|
385
|
+
ensure
|
386
|
+
io.close if io
|
387
|
+
end
|
388
|
+
|
389
|
+
rescue
|
390
|
+
$log.error $!.to_s
|
391
|
+
$log.error_backtrace
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
|
397
|
+
class PositionFile
|
398
|
+
def initialize(file, map, last_pos)
|
399
|
+
@file = file
|
400
|
+
@map = map
|
401
|
+
@last_pos = last_pos
|
402
|
+
end
|
403
|
+
|
404
|
+
def [](path)
|
405
|
+
if m = @map[path]
|
406
|
+
return m
|
407
|
+
end
|
408
|
+
|
409
|
+
@file.pos = @last_pos
|
410
|
+
@file.write path
|
411
|
+
@file.write "\t"
|
412
|
+
seek = @file.pos
|
413
|
+
@file.write "0000000000000000\t00000000\n"
|
414
|
+
@last_pos = @file.pos
|
415
|
+
|
416
|
+
@map[path] = FilePositionEntry.new(@file, seek)
|
417
|
+
end
|
418
|
+
|
419
|
+
def self.parse(file)
|
420
|
+
map = {}
|
421
|
+
file.pos = 0
|
422
|
+
file.each_line {|line|
|
423
|
+
m = /^([^\t]+)\t([0-9a-fA-F]+)\t([0-9a-fA-F]+)/.match(line)
|
424
|
+
next unless m
|
425
|
+
path = m[1]
|
426
|
+
pos = m[2].to_i(16)
|
427
|
+
ino = m[3].to_i(16)
|
428
|
+
seek = file.pos - line.bytesize + path.bytesize + 1
|
429
|
+
map[path] = FilePositionEntry.new(file, seek)
|
430
|
+
}
|
431
|
+
new(file, map, file.pos)
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
# pos inode
|
436
|
+
# ffffffffffffffff\tffffffff\n
|
437
|
+
class FilePositionEntry
|
438
|
+
POS_SIZE = 16
|
439
|
+
INO_OFFSET = 17
|
440
|
+
INO_SIZE = 8
|
441
|
+
LN_OFFSET = 25
|
442
|
+
SIZE = 26
|
443
|
+
|
444
|
+
def initialize(file, seek)
|
445
|
+
@file = file
|
446
|
+
@seek = seek
|
447
|
+
end
|
448
|
+
|
449
|
+
def update(ino, pos)
|
450
|
+
@file.pos = @seek
|
451
|
+
@file.write "%016x\t%08x" % [pos, ino]
|
452
|
+
@inode = ino
|
453
|
+
end
|
454
|
+
|
455
|
+
def update_pos(pos)
|
456
|
+
@file.pos = @seek
|
457
|
+
@file.write "%016x" % pos
|
458
|
+
end
|
459
|
+
|
460
|
+
def read_inode
|
461
|
+
@file.pos = @seek + INO_OFFSET
|
462
|
+
raw = @file.read(8)
|
463
|
+
raw ? raw.to_i(16) : 0
|
464
|
+
end
|
465
|
+
|
466
|
+
def read_pos
|
467
|
+
@file.pos = @seek
|
468
|
+
raw = @file.read(16)
|
469
|
+
raw ? raw.to_i(16) : 0
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
class MemoryPositionEntry
|
474
|
+
def initialize
|
475
|
+
@pos = 0
|
476
|
+
@inode = 0
|
477
|
+
end
|
478
|
+
|
479
|
+
def update(ino, pos)
|
480
|
+
@inode = ino
|
481
|
+
@pos = pos
|
482
|
+
end
|
483
|
+
|
484
|
+
def update_pos(pos)
|
485
|
+
@pos = pos
|
486
|
+
end
|
487
|
+
|
488
|
+
def read_pos
|
489
|
+
@pos
|
490
|
+
end
|
491
|
+
|
492
|
+
def read_inode
|
493
|
+
@inode
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-plugin-tailpath
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jacob Wirth
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: fluentd
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.10.17
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.10.17
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
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: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
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: pry-nav
|
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 tail files and add the file path to the message
|
84
|
+
email: xthexder@live.com
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files: []
|
88
|
+
files:
|
89
|
+
- .gitignore
|
90
|
+
- Gemfile
|
91
|
+
- LICENSE
|
92
|
+
- README.md
|
93
|
+
- fluent-plugin-tailpath.gemspec
|
94
|
+
- lib/fluent/plugin/in_tailpath.rb
|
95
|
+
homepage: https://github.com/xthexder/fluent-plugin-tailpath
|
96
|
+
licenses:
|
97
|
+
- MIT
|
98
|
+
metadata: {}
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - '>='
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 2.0.14
|
116
|
+
signing_key:
|
117
|
+
specification_version: 4
|
118
|
+
summary: Fluentd plugin to tail files and add the file path to the message
|
119
|
+
test_files: []
|