logstash-input-beats 0.9.6 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1aed7cc18f6010a5e6e40d4dfa62c073dc3bd512
4
- data.tar.gz: ed5565a612454c4905fa48edc7c6482af02a5008
3
+ metadata.gz: df4aadb42d40ce79081da88eb7519460e82cd37f
4
+ data.tar.gz: 5239753151ef027acb05146920ba553c0101ce21
5
5
  SHA512:
6
- metadata.gz: fa7854d4255d44ca06916883f3cc24e78b0c778d6503d9d3b91c37d3b5e99e769474e12224ce5d226eaa4b56dca83083f1cd1d57a499557b3960b550a0d714e1
7
- data.tar.gz: c423e548a02defb63de98ea6f65a3e339a0f55423d19b08b92af71fb033ebe8da7e1c0fd3add8b31f84b6c1f73699a57893fda9b6205ab3177d47656ba599edb
6
+ metadata.gz: 350918e94dc2ba03d0b9c57abab07902fb8b7ca45354213932db23f0c7477b91f89813ba83d5dcbce1c8f01406976608b50b0a286a1c573ec0e6a28b55a8c45f
7
+ data.tar.gz: 6c3f2ad8a8fec46786012b8df87f9c89e4500b5d675fb21bd9cf917f87bb53e44ef6bf9b95694dfb236622beb57adfd4b354317a280e049e7c65d72f4bbab5cb
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ # 2.0.0
2
+ - Add support for stream identity, the ID will be generated from beat.id+resource_id or beat.name + beat.source if not present #22 #13
3
+ The identity allow the multiline codec to correctly merge string from multiples files.
1
4
  # 0.9.6
2
5
  - Fix an issue with rogue events created by buffered codecs #19
3
6
  # 0.9.5
@@ -2,9 +2,9 @@
2
2
  require "logstash/inputs/base"
3
3
  require "logstash/namespace"
4
4
  require "logstash/timestamp"
5
- require "logstash/compatibility_layer_api_v1"
6
5
  require "lumberjack/beats"
7
6
  require "lumberjack/beats/server"
7
+ require "logstash/codecs/identity_map_codec"
8
8
 
9
9
  # use Logstash provided json decoder
10
10
  Lumberjack::Beats::json = LogStash::Json
@@ -14,8 +14,6 @@ Lumberjack::Beats::json = LogStash::Json
14
14
  # https://github.com/elastic/filebeat[filebeat]
15
15
  #
16
16
  class LogStash::Inputs::Beats < LogStash::Inputs::Base
17
- include LogStash::CompatibilityLayerApiV1
18
-
19
17
  config_name "beats"
20
18
 
21
19
  default :codec, "plain"
@@ -50,7 +48,7 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
50
48
  # TODO(sissel): Add CA to authenticate clients with.
51
49
  BUFFERED_QUEUE_SIZE = 1
52
50
  RECONNECT_BACKOFF_SLEEP = 0.5
53
-
51
+
54
52
  def register
55
53
  require "concurrent"
56
54
  require "logstash/circuit_breaker"
@@ -69,7 +67,7 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
69
67
  :ssl_key_passphrase => @ssl_key_passphrase)
70
68
 
71
69
  # Create a reusable threadpool, we do not limit the number of connections
72
- # to the input, the circuit breaker with the timeout should take care
70
+ # to the input, the circuit breaker with the timeout should take care
73
71
  # of `blocked` threads and prevent logstash to go oom.
74
72
  @threadpool = Concurrent::CachedThreadPool.new(:idletime => 15)
75
73
 
@@ -80,6 +78,9 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
80
78
  @circuit_breaker = LogStash::CircuitBreaker.new("Beats input",
81
79
  :exceptions => [LogStash::SizedQueueTimeout::TimeoutError])
82
80
 
81
+ # wrap the configured codec to support identity stream
82
+ # from the producers
83
+ @codec = LogStash::Codecs::IdentityMapCodec.new(@codec)
83
84
  end # def register
84
85
 
85
86
  def ssl_configured?
@@ -99,7 +100,7 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
99
100
  connection = @lumberjack.accept # call that creates a new connection
100
101
  next if connection.nil? # if the connection is nil the connection was close.
101
102
 
102
- invoke(connection, @codec.clone) do |event|
103
+ invoke(connection) do |event|
103
104
  if stop?
104
105
  connection.close
105
106
  break
@@ -126,17 +127,17 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
126
127
  end
127
128
 
128
129
  public
129
- def create_event(codec, map)
130
+ def create_event(map, identity_stream)
130
131
  # Filebeats uses the `message` key and LSF `line`
131
132
  target_field = target_field_for_codec ? map.delete(target_field_for_codec) : nil
132
133
 
133
134
  if target_field.nil?
134
- event = LogStash::Event.new(map)
135
+ event = LogStash::Event.new(map)
135
136
  decorate(event)
136
137
  return event
137
138
  else
138
139
  # All codecs expects to work on string
139
- @codec.decode(target_field.to_s) do |decoded|
140
+ @codec.decode(target_field.to_s, identity_stream) do |decoded|
140
141
  ts = coerce_ts(map.delete("@timestamp"))
141
142
  decoded["@timestamp"] = ts unless ts.nil?
142
143
  map.each { |k, v| decoded[k] = v }
@@ -161,13 +162,13 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
161
162
  end
162
163
 
163
164
  private
164
- def invoke(connection, codec, &block)
165
+ def invoke(connection, &block)
165
166
  @threadpool.post do
166
167
  begin
167
168
  # If any errors occur in from the events the connection should be closed in the
168
169
  # library ensure block and the exception will be handled here
169
- connection.run do |map|
170
- event = create_event(codec, map)
170
+ connection.run do |map, identity_stream|
171
+ event = create_event(map, identity_stream)
171
172
  block.call(event) unless event.nil?
172
173
  end
173
174
 
@@ -150,17 +150,17 @@ module Lumberjack module Beats
150
150
  end # def transition
151
151
 
152
152
  # Feed data to this parser.
153
- #
153
+ #
154
154
  # Currently, it will return the raw payload of websocket messages.
155
155
  # Otherwise, it returns nil if no complete message has yet been consumed.
156
156
  #
157
- # @param [String] the string data to feed into the parser.
157
+ # @param [String] the string data to feed into the parser.
158
158
  # @return [String, nil] the websocket message payload, if any, nil otherwise.
159
159
  def feed(data, &block)
160
160
  @buffer << data
161
161
  #p :need => @need
162
162
  while have?(@need)
163
- send(@state, &block)
163
+ send(@state, &block)
164
164
  #case @state
165
165
  #when :header; header(&block)
166
166
  #when :window_size; window_size(&block)
@@ -310,6 +310,10 @@ module Lumberjack module Beats
310
310
  @ack_handler = nil
311
311
  end
312
312
 
313
+ def peer
314
+ "#{@fd.peeraddr[3]}:#{@fd.peeraddr[1]}"
315
+ end
316
+
313
317
  def run(&block)
314
318
  while !server.closed?
315
319
  read_socket(&block)
@@ -375,17 +379,34 @@ module Lumberjack module Beats
375
379
  end
376
380
 
377
381
  def data(map, &block)
378
- block.call(map) if block_given?
382
+ block.call(map, identity_stream(map)) if block_given?
379
383
  end
380
384
 
381
385
  def reset_next_ack(window_size)
382
- klass = (@version == Parser::PROTOCOL_VERSION_1) ? AckingProtocolV1 : AckingProtocolV2
386
+ klass = version_1? ? AckingProtocolV1 : AckingProtocolV2
383
387
  @ack_handler = klass.new(window_size)
384
388
  end
385
389
 
386
390
  def send_ack(sequence)
387
391
  @fd.syswrite(@ack_handler.ack_frame(sequence))
388
392
  end
393
+
394
+ def version_1?
395
+ @version == Parser::PROTOCOL_VERSION_1
396
+ end
397
+
398
+ def identity_stream(map)
399
+ id = map.fetch("beat", {})["id"]
400
+
401
+ if id && map["resource_id"]
402
+ identity_values = [id, map["resource_id"]]
403
+ else
404
+ identity_values = [map.fetch("beat", {})["name"],
405
+ map["source"]]
406
+ end
407
+
408
+ identity_values.compact.join("-")
409
+ end
389
410
  end # class Connection
390
411
 
391
412
  class AckingProtocolV1
@@ -395,7 +416,7 @@ module Lumberjack module Beats
395
416
  end
396
417
 
397
418
  def ack?(sequence)
398
- # The first encoded event will contain the sequence number
419
+ # The first encoded event will contain the sequence number
399
420
  # this is needed to know when we should ack.
400
421
  @next_ack = compute_next_ack(sequence) if @next_ack.nil?
401
422
  sequence == @next_ack
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "logstash-input-beats"
3
- s.version = "0.9.6"
3
+ s.version = "2.0.0"
4
4
  s.licenses = ["Apache License (2.0)"]
5
5
  s.summary = "Receive events using the lumberjack protocol."
6
6
  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"
@@ -19,10 +19,11 @@ Gem::Specification.new do |s|
19
19
  s.metadata = { "logstash_plugin" => "true", "logstash_group" => "input" }
20
20
 
21
21
  # Gem dependencies
22
- s.add_runtime_dependency "logstash-core", ">= 1.5.4", "< 3.0.0"
22
+ s.add_runtime_dependency "logstash-core", ">= 2.0.0", "< 3.0.0"
23
23
 
24
24
  s.add_runtime_dependency "logstash-codec-plain"
25
25
  s.add_runtime_dependency "concurrent-ruby", "0.9.1"
26
+ s.add_runtime_dependency "logstash-codec-multiline", "~> 2.0.3"
26
27
 
27
28
  s.add_development_dependency "flores", "~>0.0.6"
28
29
  s.add_development_dependency "rspec"
@@ -30,6 +31,5 @@ Gem::Specification.new do |s|
30
31
  s.add_development_dependency "pry"
31
32
  s.add_development_dependency "rspec-wait"
32
33
  s.add_development_dependency "logstash-devutils", "~> 0.0.18"
33
- s.add_development_dependency "logstash-codec-multiline"
34
34
  end
35
35
 
@@ -2,7 +2,6 @@
2
2
  require_relative "../spec_helper"
3
3
  require "stud/temporary"
4
4
  require "logstash/inputs/beats"
5
- require "logstash/compatibility_layer_api_v1"
6
5
  require "logstash/codecs/plain"
7
6
  require "logstash/codecs/multiline"
8
7
  require "logstash/event"
@@ -79,31 +78,19 @@ describe LogStash::Inputs::Beats do
79
78
  "type" => "example", "codec" => codec }
80
79
  end
81
80
 
82
-
83
- context "#codecs" do
84
- let(:lines) { {"line" => "one\ntwo\n two.2\nthree\n", "tags" => ["syslog"]} }
85
-
86
- before do
87
- allow(connection).to receive(:run).and_yield(lines)
88
- beats.register
89
- expect_any_instance_of(Lumberjack::Beats::Server).to receive(:accept).and_return(connection)
90
- end
91
-
92
- it "clone the codec per connection" do
93
- expect(beats.codec).to receive(:clone).once
94
- expect(beats).to receive(:invoke) { break }
95
- beats.run(queue)
96
- end
81
+ before do
82
+ beats.register
97
83
  end
98
84
 
99
85
  context "#create_event" do
100
86
  let(:config) { super.merge({ "add_field" => { "foo" => "bar", "[@metadata][hidden]" => "secret"}, "tags" => ["bonjour"]}) }
101
87
  let(:event_map) { { "hello" => "world" } }
102
88
  let(:codec) { LogStash::Codecs::Plain.new }
89
+ let(:identity_stream) { "custom-type-input_type-source" }
103
90
 
104
91
  context "without a `target_field` defined" do
105
92
  it "decorates the event" do
106
- event = beats.create_event(codec, event_map)
93
+ event = beats.create_event(event_map, identity_stream)
107
94
  expect(event["foo"]).to eq("bar")
108
95
  expect(event["[@metadata][hidden]"]).to eq("secret")
109
96
  expect(event["tags"]).to include("bonjour")
@@ -114,7 +101,7 @@ describe LogStash::Inputs::Beats do
114
101
  let(:event_map) { super.merge({"message" => "with a field"}) }
115
102
 
116
103
  it "decorates the event" do
117
- event = beats.create_event(beats.codec, event_map)
104
+ event = beats.create_event(event_map, identity_stream)
118
105
  expect(event["foo"]).to eq("bar")
119
106
  expect(event["[@metadata][hidden]"]).to eq("secret")
120
107
  expect(event["tags"]).to include("bonjour")
@@ -126,16 +113,14 @@ describe LogStash::Inputs::Beats do
126
113
  let(:event_map) { {"message" => "hello?", "tags" => ["syslog"]} }
127
114
 
128
115
  it "retuns nil" do
129
- event = beats.create_event(beats.codec, event_map)
116
+ event = beats.create_event(event_map, identity_stream)
130
117
  expect(event).to be_nil
131
118
  end
132
119
  end
133
120
  end
134
121
  end
135
122
 
136
- unless LogStash::CompatibilityLayerApiV1.is_v1?
137
- context "when interrupting the plugin" do
138
- it_behaves_like "an interruptible input plugin"
139
- end
123
+ context "when interrupting the plugin" do
124
+ it_behaves_like "an interruptible input plugin"
140
125
  end
141
126
  end
@@ -33,7 +33,7 @@ describe "A client" do
33
33
  tcp_server.accept do |socket|
34
34
  con = Lumberjack::Beats::Connection.new(socket, tcp_server)
35
35
  begin
36
- con.run { |data| queue << data }
36
+ con.run { |data, identity_stream| queue << [data, identity_stream] }
37
37
  rescue
38
38
  # Close connection on failure. For example SSL client will make
39
39
  # parser for TCP based server trip.
@@ -44,7 +44,7 @@ describe "A client" do
44
44
  end
45
45
 
46
46
  @ssl_server = Thread.new do
47
- ssl_server.run { |data| queue << data }
47
+ ssl_server.run { |data, identity_stream| queue << [data, identity_stream] }
48
48
  end
49
49
  end
50
50
 
@@ -55,6 +55,7 @@ describe "A client" do
55
55
  end
56
56
  sleep(0.5) # give time to the server to read the events
57
57
  expect(queue.size).to eq(random_number_of_events)
58
+ expect(queue.collect(&:last).uniq.size).to eq(1)
58
59
  end
59
60
 
60
61
  it "support sending multiple elements in one payload" do
@@ -62,7 +63,7 @@ describe "A client" do
62
63
  sleep(0.5)
63
64
 
64
65
  expect(queue.size).to eq(batch_size)
65
- expect(queue).to match_array(batch_payload)
66
+ expect(queue.collect(&:last).uniq.size).to eq(1)
66
67
  end
67
68
  end
68
69
 
@@ -106,7 +107,7 @@ describe "A client" do
106
107
  expect(client.write(batch_payload)).to eq(batch_size / 2)
107
108
  sleep(0.5)
108
109
  expect(queue.size).to eq(batch_size)
109
- expect(queue).to match_array(batch_payload)
110
+ expect(queue.collect(&:first)).to match_array(batch_payload)
110
111
  end
111
112
  end
112
113
  end
@@ -152,7 +153,7 @@ describe "A client" do
152
153
  context "using ssl encrypted connection" do
153
154
  context "with a valid certificate" do
154
155
  it "successfully connect to the server" do
155
- expect {
156
+ expect {
156
157
  Lumberjack::Beats::Client.new(:port => port,
157
158
  :host => host,
158
159
  :addresses => host,
@@ -161,7 +162,7 @@ describe "A client" do
161
162
  end
162
163
 
163
164
  it "should fail connecting to plain tcp server" do
164
- expect {
165
+ expect {
165
166
  Lumberjack::Beats::Client.new(:port => tcp_port,
166
167
  :host => host,
167
168
  :addresses => host,
@@ -4,6 +4,8 @@ require "spec_helper"
4
4
  require "flores/random"
5
5
 
6
6
  describe "Connnection" do
7
+ let(:ip) { "192.168.1.2" }
8
+ let(:port) { 4444 }
7
9
  let(:server) { double("server", :closed? => false) }
8
10
  let(:socket) { double("socket", :closed? => false) }
9
11
  let(:connection) { Lumberjack::Beats::Connection.new(socket, server) }
@@ -11,6 +13,55 @@ describe "Connnection" do
11
13
  let(:start_sequence) { Flores::Random.integer(0..2000) }
12
14
  let(:random_number_of_events) { Flores::Random.integer(2..200) }
13
15
 
16
+ subject { Lumberjack::Beats::Connection.new(socket, server) }
17
+
18
+ before do
19
+ allow(socket).to receive(:peeraddr).and_return(["AF_INET", port, "test.elastic.co", ip])
20
+ end
21
+
22
+ context "#peer" do
23
+ let(:socket) { double("socket", :closed? => false) }
24
+
25
+ it "return the ip and the port" do
26
+ expect(subject.peer).to eq("#{ip}:#{port}")
27
+ end
28
+ end
29
+
30
+ context "#identity_stream" do
31
+ let(:map) { { "message" => "Hello world" } }
32
+
33
+ context "with a map containing the beats.id and the file_id" do
34
+ let(:map) { super.merge({
35
+ "beat" => { "name" => "testing-host", "id" => "abc1234" },
36
+ "type" => "log",
37
+ "resource_id" => "123",
38
+ "input_type" => "propector",
39
+ "source" => "/var/log/message" }) }
40
+
41
+ it "generate a identity stream from an event start" do
42
+ expect(subject.identity_stream(map)).to eq("#{map["beat"]["id"]}-#{map["resource_id"]}")
43
+ end
44
+ end
45
+
46
+ context "with a map containing all the information" do
47
+ let(:map) { super.merge({
48
+ "beat" => { "name" => "testing-host" },
49
+ "type" => "log",
50
+ "input_type" => "propector",
51
+ "source" => "/var/log/message" }) }
52
+
53
+ it "generate a identity stream from an event start" do
54
+ expect(subject.identity_stream(map)).to eq("#{map["beat"]["name"]}-#{map["source"]}")
55
+ end
56
+ end
57
+
58
+ context "with a map containing no information" do
59
+ it "use the ip and the port" do
60
+ expect(subject.identity_stream(map)).to eq("")
61
+ end
62
+ end
63
+ end
64
+
14
65
  context "when the server is running" do
15
66
  before do
16
67
  allow(socket).to receive(:sysread).with(Lumberjack::Beats::Connection::READ_SIZE).and_return("")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-beats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.6
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-06 00:00:00.000000000 Z
11
+ date: 2015-11-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logstash-core
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - '>='
18
18
  - !ruby/object:Gem::Version
19
- version: 1.5.4
19
+ version: 2.0.0
20
20
  - - <
21
21
  - !ruby/object:Gem::Version
22
22
  version: 3.0.0
@@ -24,7 +24,7 @@ dependencies:
24
24
  requirements:
25
25
  - - '>='
26
26
  - !ruby/object:Gem::Version
27
- version: 1.5.4
27
+ version: 2.0.0
28
28
  - - <
29
29
  - !ruby/object:Gem::Version
30
30
  version: 3.0.0
@@ -58,6 +58,20 @@ dependencies:
58
58
  version: 0.9.1
59
59
  prerelease: false
60
60
  type: :runtime
61
+ - !ruby/object:Gem::Dependency
62
+ name: logstash-codec-multiline
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ version: 2.0.3
68
+ requirement: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ~>
71
+ - !ruby/object:Gem::Version
72
+ version: 2.0.3
73
+ prerelease: false
74
+ type: :runtime
61
75
  - !ruby/object:Gem::Dependency
62
76
  name: flores
63
77
  version_requirements: !ruby/object:Gem::Requirement
@@ -142,20 +156,6 @@ dependencies:
142
156
  version: 0.0.18
143
157
  prerelease: false
144
158
  type: :development
145
- - !ruby/object:Gem::Dependency
146
- name: logstash-codec-multiline
147
- version_requirements: !ruby/object:Gem::Requirement
148
- requirements:
149
- - - '>='
150
- - !ruby/object:Gem::Version
151
- version: '0'
152
- requirement: !ruby/object:Gem::Requirement
153
- requirements:
154
- - - '>='
155
- - !ruby/object:Gem::Version
156
- version: '0'
157
- prerelease: false
158
- type: :development
159
159
  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
160
160
  email: info@elastic.co
161
161
  executables: []
@@ -163,14 +163,13 @@ extensions: []
163
163
  extra_rdoc_files: []
164
164
  files:
165
165
  - lib/logstash/circuit_breaker.rb
166
- - lib/logstash/compatibility_layer_api_v1.rb
167
166
  - lib/logstash/sized_queue_timeout.rb
168
167
  - lib/logstash/inputs/beats.rb
169
168
  - lib/lumberjack/beats.rb
170
169
  - lib/lumberjack/beats/client.rb
171
170
  - lib/lumberjack/beats/server.rb
172
- - spec/integration_spec.rb
173
171
  - spec/spec_helper.rb
172
+ - spec/integration_spec.rb
174
173
  - spec/inputs/beats_spec.rb
175
174
  - spec/logstash/circuit_breaker_spec.rb
176
175
  - spec/logstash/size_queue_timeout_spec.rb
@@ -215,8 +214,8 @@ signing_key:
215
214
  specification_version: 4
216
215
  summary: Receive events using the lumberjack protocol.
217
216
  test_files:
218
- - spec/integration_spec.rb
219
217
  - spec/spec_helper.rb
218
+ - spec/integration_spec.rb
220
219
  - spec/inputs/beats_spec.rb
221
220
  - spec/logstash/circuit_breaker_spec.rb
222
221
  - spec/logstash/size_queue_timeout_spec.rb
@@ -1,28 +0,0 @@
1
- # encoding: utf-8
2
- require "logstash/version"
3
- require "gems"
4
-
5
- # This module allow this plugin to work with the v1 API.
6
- module LogStash::CompatibilityLayerApiV1
7
- LOGSTASH_CORE_VERSION = Gem::Version.new(LOGSTASH_VERSION)
8
- V2_VERSION = Gem::Version.new("2.0.0.beta2")
9
-
10
- def self.included(base)
11
- base.send(:include, InstanceMethods) if self.is_v1?
12
- end
13
-
14
- def self.is_v1?
15
- LOGSTASH_CORE_VERSION < V2_VERSION
16
- end
17
-
18
- # This allow this plugin to work both in V1 and v2 of logstash-core
19
- module InstanceMethods
20
- def stop?
21
- false
22
- end
23
-
24
- def teardown
25
- stop
26
- end
27
- end
28
- end