logstash-input-file_progress 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8ce74ef18daca1f4dee56f79adf5c680423dac47
4
+ data.tar.gz: 639c3ae4c64b2d7224668ffbf2a6bb219f9499ed
5
+ SHA512:
6
+ metadata.gz: 1f2a51b2364f190771284c31baaa9723f613957ce70fd5918707533ed3b6ce815c930fa3fc41a30d87a5b00612ccb14c074f2f1014718f6dc66f18bb5c076613
7
+ data.tar.gz: 8e02ebb1782de1c4c5151f7d5f0c4471e4e03394e0cc7566044f4414ae81332030205045e613ad428ec478953bbd9d4713c199455608e08b6c7853ec6418a6b7
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ Gemfile.lock
2
+ .ruby-version
3
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2012-2014 Elasticsearch <http://www.elasticsearch.org>
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.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ @files=[]
2
+
3
+ task :default do
4
+ system("rake -T")
5
+ end
6
+
7
+ require "logstash/devutils/rake"
@@ -0,0 +1,149 @@
1
+ require "logstash/inputs/base"
2
+ require "logstash/namespace"
3
+
4
+ require "pathname"
5
+ require "socket" # for Socket.gethostname
6
+
7
+ # Stream events from files.
8
+ #
9
+ # By default, each event is assumed to be one line. If you
10
+ # want to join lines, you'll want to use the multiline filter.
11
+ #
12
+ # Files are followed in a manner similar to "tail -0F". File rotation
13
+ # is detected and handled by this input.
14
+ #
15
+ # In addition to 'normal' file input we add events with sincedb-data to the pipe.
16
+ # This can be later used to serve progress date with e.g. faye-output
17
+ class LogStash::Inputs::FileProgress < LogStash::Inputs::Base
18
+ config_name "file_progress"
19
+
20
+ # TODO(sissel): This should switch to use the 'line' codec by default
21
+ # once file following
22
+ default :codec, "line"
23
+
24
+ # The path to the file to use as an input.
25
+ # You can use globs here, such as `/var/log/*.log`
26
+ # Paths must be absolute and cannot be relative.
27
+ config :path, :validate => :array, :required => true
28
+
29
+ # Exclusions (matched against the filename, not full path). Globs
30
+ # are valid here, too. For example, if you have
31
+ #
32
+ # path => "/var/log/*"
33
+ #
34
+ # you might want to exclude gzipped files:
35
+ #
36
+ # exclude => "*.gz"
37
+ config :exclude, :validate => :array
38
+
39
+ # How often we stat files to see if they have been modified. Increasing
40
+ # this interval will decrease the number of system calls we make, but
41
+ # increase the time to detect new log lines.
42
+ config :stat_interval, :validate => :number, :default => 1
43
+
44
+ # How often we expand globs to discover new files to watch.
45
+ config :discover_interval, :validate => :number, :default => 15
46
+
47
+ # Where to write the since database (keeps track of the current
48
+ # position of monitored log files). The default will write
49
+ # sincedb files to some path matching "$HOME/.sincedb*"
50
+ config :sincedb_path, :validate => :string, :required => true
51
+
52
+ # How often to write a since database with the current position of
53
+ # monitored log files.
54
+ config :sincedb_write_interval, :validate => :number, :default => 15
55
+
56
+ # Choose where logstash starts initially reading files - at the beginning or
57
+ # at the end. The default behavior treats files like live streams and thus
58
+ # starts at the end. If you have old data you want to import, set this
59
+ # to 'beginning'
60
+ #
61
+ # This option only modifieds "first contact" situations where a file is new
62
+ # and not seen before. If a file has already been seen before, this option
63
+ # has no effect.
64
+ config :start_position, :validate => [ "beginning", "end"], :default => "beginning"
65
+
66
+ # Should the progressdb events be send to the pipeline
67
+ config :progressdb, :validate => :boolean, :default => false
68
+
69
+ # Should the processdb entry be deleted after file-deletion
70
+ config :progressdb_del, :validate => :boolean, :default => false
71
+
72
+ # Close the file when end is reached
73
+ # This make sense when reading a file once from the beginning and want to e.g.
74
+ # proceed renaming or deleting the parent folder
75
+ config :eof_close, :validate => :boolean, :default => false
76
+
77
+ public
78
+ def register
79
+ require "addressable/uri"
80
+ require "filewatch/ext/filetail"
81
+ require "digest/md5"
82
+ @logger.info("Registering file input", :path => @path)
83
+
84
+ @tail_config = {
85
+ :exclude => @exclude,
86
+ :stat_interval => @stat_interval,
87
+ :discover_interval => @discover_interval,
88
+ :sincedb_write_interval => @sincedb_write_interval,
89
+ :logger => @logger,
90
+ :progressdb => @progressdb,
91
+ :progressdb_del => @progressdb_del,
92
+ :eof_close => @eof_close,
93
+ }
94
+
95
+ @path.each do |path|
96
+ if Pathname.new(path).relative?
97
+ raise ArgumentError.new("File paths must be absolute, relative path specified: #{path}")
98
+ end
99
+ end
100
+
101
+ @tail_config[:sincedb_path] = @sincedb_path
102
+
103
+ if @start_position == "beginning"
104
+ @tail_config[:start_new_files_at] = :beginning
105
+ end
106
+
107
+ @codec_plain = LogStash::Codecs::Plain.new
108
+ end # def register
109
+
110
+ public
111
+ def run(queue)
112
+ @tail = FileWatch::Ext::FileTail.new(@tail_config)
113
+ @tail.logger = @logger
114
+ @path.each { |path| @tail.tail(path) }
115
+ hostname = Socket.gethostname
116
+
117
+ @tail.subscribe do |path, data, type|
118
+ @logger.debug("Received line", :path => path, :data => data) if logger.debug?
119
+
120
+ if type == :log
121
+ @codec.decode(data) do |event|
122
+
123
+ decorate(event)
124
+ event["host"] = hostname
125
+ event["path"] = path
126
+
127
+ queue << event
128
+ end
129
+ elsif type == :progressdb
130
+ @codec_plain.decode(data) do |event|
131
+
132
+ decorate(event)
133
+ event["host"] = hostname
134
+ event["path"] = path
135
+ event["type"] = "progressdb";
136
+
137
+ queue << event
138
+ end
139
+ end # if
140
+ end # subscribe
141
+ finished
142
+ end # def run
143
+
144
+ public
145
+ def teardown
146
+ @tail.sincedb_write
147
+ @tail.quit
148
+ end # def teardown
149
+ end # class LogStash::Inputs::TriggeredPackage
@@ -0,0 +1,32 @@
1
+ Gem::Specification.new do |s|
2
+
3
+ s.name = 'logstash-input-file_progress'
4
+ s.version = '0.1.0'
5
+ s.licenses = ['Apache License (2.0)']
6
+ s.summary = "Stream events from files with progress-events."
7
+ s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
8
+ s.authors = ["Signify"]
9
+ s.email = 'dietmar@signifydata.com'
10
+ s.homepage = "http://www.signifydata.com"
11
+ s.require_paths = ["lib"]
12
+
13
+ # Files
14
+ s.files = `git ls-files`.split($\)
15
+
16
+ # Tests
17
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
+
19
+ # Special flag to let us know this is actually a logstash plugin
20
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "input" }
21
+
22
+ # Gem dependencies
23
+ s.add_runtime_dependency 'logstash-core', '>= 1.4.0', '< 2.0.0'
24
+
25
+ s.add_runtime_dependency 'logstash-codec-line'
26
+ s.add_runtime_dependency 'addressable'
27
+ s.add_runtime_dependency "filewatch-ext", ["~> 0.3.0"]
28
+
29
+
30
+ s.add_development_dependency 'logstash-devutils'
31
+ end
32
+
@@ -0,0 +1,141 @@
1
+ # encoding: utf-8
2
+
3
+ require "logstash/devutils/rspec/spec_helper"
4
+ require "logstash/inputs/file_progress"
5
+ require "tempfile"
6
+
7
+ describe "inputs/file_progress" do
8
+
9
+
10
+ describe "starts at the end of an existing file" do
11
+ tmp_file = Tempfile.new('logstash-spec-input-file')
12
+
13
+ config = %q[
14
+ input {
15
+ file_progress {
16
+ type => "blah"
17
+ path => "#{tmp_file.path}"
18
+ sincedb_path => "/dev/null"
19
+ }
20
+ }
21
+ ]
22
+
23
+ #input do |pipeline, queue|
24
+ File.open(tmp_file, "w") do |fd|
25
+ fd.puts("ignore me 1")
26
+ fd.puts("ignore me 2")
27
+ end
28
+
29
+ pipeline = LogStash::Pipeline.new(config)
30
+ queue = Queue.new
31
+ pipeline.instance_eval do
32
+ @output_func = lambda { |event| queue << event }
33
+ end
34
+ pipeline_thread = Thread.new { pipeline.run }
35
+ event = queue.pop
36
+
37
+ # at this point even if pipeline.ready? == true the plugins
38
+ # threads might still be initializing so we cannot know when the
39
+ # file plugin will have seen the original file, it could see it
40
+ # after the first(s) hello world appends below, hence the
41
+ # retry logic.
42
+
43
+ retries = 0
44
+ loop do
45
+ insist { retries } < 20 # 2 secs should be plenty?
46
+
47
+ File.open(tmp_file, "a") do |fd|
48
+ fd.puts("hello")
49
+ fd.puts("world")
50
+ end
51
+
52
+ if queue.size >= 2
53
+ events = 2.times.collect { queue.pop }
54
+ insist { events[0]["message"] } == "hello"
55
+ insist { events[1]["message"] } == "world"
56
+ break
57
+ end
58
+
59
+ sleep(0.1)
60
+ retries += 1
61
+ end
62
+
63
+ pipeline_thread.join
64
+
65
+ #end
66
+ end
67
+
68
+ #describe "can start at the beginning of an existing file" do
69
+ # tmp_file = Tempfile.new('logstash-spec-input-file')#
70
+
71
+ # config <<-CONFIG
72
+ # input {
73
+ # file_progress {
74
+ # type => "blah"
75
+ # path => "#{tmp_file.path}"
76
+ # start_position => "beginning"
77
+ # sincedb_path => "/dev/null"
78
+ # }
79
+ # }
80
+ # CONFIG#
81
+
82
+ # input do |pipeline, queue|
83
+ # File.open(tmp_file, "a") do |fd|
84
+ # fd.puts("hello")
85
+ # fd.puts("world")
86
+ # end#
87
+
88
+ # Thread.new { pipeline.run }
89
+ # sleep 0.1 while !pipeline.ready?#
90
+
91
+ # events = 2.times.collect { queue.pop }
92
+ # insist { events[0]["message"] } == "hello"
93
+ # insist { events[1]["message"] } == "world"
94
+ # end
95
+ # end#
96
+
97
+ # describe "restarts at the sincedb value" do
98
+ # tmp_file = Tempfile.new('logstash-spec-input-file')
99
+ # tmp_sincedb = Tempfile.new('logstash-spec-input-file-sincedb')#
100
+
101
+ # config <<-CONFIG
102
+ # input {
103
+ # file_progress {
104
+ # type => "blah"
105
+ # path => "#{tmp_file.path}"
106
+ # start_position => "beginning"
107
+ # sincedb_path => "#{tmp_sincedb.path}"
108
+ # }
109
+ # }
110
+ # CONFIG#
111
+
112
+ # input do |pipeline, queue|
113
+ # File.open(tmp_file, "w") do |fd|
114
+ # fd.puts("hello")
115
+ # fd.puts("world")
116
+ # end#
117
+
118
+ # t = Thread.new { pipeline.run }
119
+ # sleep 0.1 while !pipeline.ready?#
120
+
121
+ # events = 2.times.collect { queue.pop }
122
+ # pipeline.shutdown
123
+ # t.join#
124
+
125
+ # File.open(tmp_file, "a") do |fd|
126
+ # fd.puts("foo")
127
+ # fd.puts("bar")
128
+ # fd.puts("baz")
129
+ # end#
130
+
131
+ # Thread.new { pipeline.run }
132
+ # sleep 0.1 while !pipeline.ready?#
133
+
134
+ # events = 3.times.collect { queue.pop }#
135
+
136
+ # insist { events[0]["message"] } == "foo"
137
+ # insist { events[1]["message"] } == "bar"
138
+ # insist { events[2]["message"] } == "baz"
139
+ # end
140
+ # end
141
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-input-file_progress
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Signify
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - '>='
17
+ - !ruby/object:Gem::Version
18
+ version: 1.4.0
19
+ - - <
20
+ - !ruby/object:Gem::Version
21
+ version: 2.0.0
22
+ name: logstash-core
23
+ prerelease: false
24
+ type: :runtime
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.4.0
30
+ - - <
31
+ - !ruby/object:Gem::Version
32
+ version: 2.0.0
33
+ - !ruby/object:Gem::Dependency
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ name: logstash-codec-line
40
+ prerelease: false
41
+ type: :runtime
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ name: addressable
54
+ prerelease: false
55
+ type: :runtime
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ~>
65
+ - !ruby/object:Gem::Version
66
+ version: 0.3.0
67
+ name: filewatch-ext
68
+ prerelease: false
69
+ type: :runtime
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ~>
73
+ - !ruby/object:Gem::Version
74
+ version: 0.3.0
75
+ - !ruby/object:Gem::Dependency
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ name: logstash-devutils
82
+ prerelease: false
83
+ type: :development
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ description: This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program
90
+ email: dietmar@signifydata.com
91
+ executables: []
92
+ extensions: []
93
+ extra_rdoc_files: []
94
+ files:
95
+ - .gitignore
96
+ - Gemfile
97
+ - LICENSE
98
+ - Rakefile
99
+ - lib/logstash/inputs/file_progress.rb
100
+ - logstash-input-file_progress.gemspec
101
+ - spec/inputs/file_progress_spec.rb
102
+ homepage: http://www.signifydata.com
103
+ licenses:
104
+ - Apache License (2.0)
105
+ metadata:
106
+ logstash_plugin: 'true'
107
+ logstash_group: input
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.1.9
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Stream events from files with progress-events.
128
+ test_files:
129
+ - spec/inputs/file_progress_spec.rb