logstash-output-tcp 6.0.2 → 6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f565b112e5a38f65bebf7c13061e0b7de65a460952afdc93bcf145998bd6ae30
4
- data.tar.gz: 97b30dfceff9b0d2187c408e3ac5fe4abc8def98e9fdd4e59580fa2f0d112ef9
3
+ metadata.gz: d30cf25cfbc7e1cc2e72d5e1eb8ab8179d42b31413a549801a012d5e1be7e640
4
+ data.tar.gz: 53632382865d4bade1e6d56e9872b1b5db690fd9b866cb4d0952dfa8d1156cee
5
5
  SHA512:
6
- metadata.gz: ffec33897e55cd02b237b385dc4e6ffe1d90f2bb87b2fc4b1dbd4bb050f1db9787038885b7fbd77539b9e60fab1a44e1f9567925f7dc8a36eaab4be0d158b2a3
7
- data.tar.gz: cb2c4871d74fd661ee2d947a94535483fd54854fee574d4615a05634df74b97fedb6e01d73f645e8cc70e41de80fbf1eeceaa466855ff757cc320f19130f8362
6
+ metadata.gz: e48f3511c25df04edb79dfd0425c6a84d18808bce5f8d4910cabe024955e6c16f8b856a8ac7fac01cb7721e7628934101e14e7180497a7c6cc1096169290963e
7
+ data.tar.gz: 94758e5adb6be529f4c70715aa0796d1a15bd8d17e91d35c63fdc1b29eccea58c0f36e4c29b0cf911a3691fade34c895922ca9b0384cd3df405f723a97ff642f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 6.1.0
2
+ - Feat: ssl_supported_protocols (TLSv1.3) [#47](https://github.com/logstash-plugins/logstash-output-tcp/pull/47)
3
+ - Fix: close server and client sockets on plugin close
4
+
1
5
  ## 6.0.2
2
6
  - Fix: unable to start with password protected key [#45](https://github.com/logstash-plugins/logstash-output-tcp/pull/45)
3
7
 
data/docs/index.asciidoc CHANGED
@@ -45,6 +45,7 @@ This plugin supports the following configuration options plus the <<plugins-{typ
45
45
  | <<plugins-{type}s-{plugin}-ssl_enable>> |<<boolean,boolean>>|No
46
46
  | <<plugins-{type}s-{plugin}-ssl_key>> |a valid filesystem path|No
47
47
  | <<plugins-{type}s-{plugin}-ssl_key_passphrase>> |<<password,password>>|No
48
+ | <<plugins-{type}s-{plugin}-ssl_supported_protocols>> |<<string,string>>|No
48
49
  | <<plugins-{type}s-{plugin}-ssl_verify>> |<<boolean,boolean>>|No
49
50
  |=======================================================================
50
51
 
@@ -130,6 +131,20 @@ SSL key path
130
131
 
131
132
  SSL key passphrase
132
133
 
134
+ [id="plugins-{type}s-{plugin}-ssl_supported_protocols"]
135
+ ===== `ssl_supported_protocols`
136
+
137
+ * Value type is <<string,string>>
138
+ * Allowed values are: `'TLSv1.1'`, `'TLSv1.2'`, `'TLSv1.3'`
139
+ * Default depends on the JDK being used. With up-to-date Logstash, the default is `['TLSv1.2', 'TLSv1.3']`.
140
+ `'TLSv1.1'` is not considered secure and is only provided for legacy applications.
141
+
142
+ List of allowed SSL/TLS versions to use when establishing a secure connection.
143
+
144
+ NOTE: If you configure the plugin to use `'TLSv1.1'` on any recent JVM, such as the one packaged with Logstash,
145
+ the protocol is disabled by default and needs to be enabled manually by changing `jdk.tls.disabledAlgorithms` in
146
+ the *$JDK_HOME/conf/security/java.security* configuration file. That is, `TLSv1.1` needs to be removed from the list.
147
+
133
148
  [id="plugins-{type}s-{plugin}-ssl_verify"]
134
149
  ===== `ssl_verify`
135
150
 
@@ -51,38 +51,43 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
51
51
  # SSL key passphrase
52
52
  config :ssl_key_passphrase, :validate => :password, :default => nil
53
53
 
54
+ # NOTE: the default setting [] uses SSL engine defaults
55
+ config :ssl_supported_protocols, :validate => ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], :default => [], :list => true
56
+
54
57
  class Client
55
- public
58
+
56
59
  def initialize(socket, logger)
57
60
  @socket = socket
58
61
  @logger = logger
59
62
  @queue = Queue.new
60
63
  end
61
64
 
62
- public
63
65
  def run
64
66
  loop do
65
67
  begin
66
68
  @socket.write(@queue.pop)
67
69
  rescue => e
68
- @logger.warn("tcp output exception", :socket => @socket,
69
- :exception => e)
70
+ log_warn 'socket write failed:', e, socket: (@socket ? @socket.to_s : nil)
70
71
  break
71
72
  end
72
73
  end
73
74
  end # def run
74
75
 
75
- public
76
76
  def write(msg)
77
77
  @queue.push(msg)
78
78
  end # def write
79
+
80
+ def close
81
+ @socket.close
82
+ rescue => e
83
+ log_warn 'socket close failed:', e, socket: (@socket ? @socket.to_s : nil)
84
+ end
79
85
  end # class Client
80
86
 
81
- private
82
87
  def setup_ssl
83
88
  require "openssl"
84
89
 
85
- @ssl_context = OpenSSL::SSL::SSLContext.new
90
+ @ssl_context = new_ssl_context
86
91
  if @ssl_cert
87
92
  @ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(@ssl_cert))
88
93
  if @ssl_key
@@ -104,50 +109,74 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
104
109
  @ssl_context.cert_store = @cert_store
105
110
  @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
106
111
  end
107
- end # def setup_ssl
108
112
 
109
- public
113
+ @ssl_context.min_version = :TLS1_1 # not strictly required - JVM should have disabled TLSv1
114
+ if ssl_supported_protocols.any?
115
+ disabled_protocols = ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'] - ssl_supported_protocols
116
+ unless OpenSSL::SSL.const_defined? :OP_NO_TLSv1_3 # work-around JRuby-OpenSSL bug - missing constant
117
+ @ssl_context.max_version = :TLS1_2 if disabled_protocols.delete('TLSv1.3')
118
+ end
119
+ # mapping 'TLSv1.2' -> OpenSSL::SSL::OP_NO_TLSv1_2
120
+ disabled_protocols.map! { |v| OpenSSL::SSL.const_get "OP_NO_#{v.sub('.', '_')}" }
121
+ @ssl_context.options = disabled_protocols.reduce(@ssl_context.options, :|)
122
+ end
123
+ @ssl_context
124
+ end
125
+ private :setup_ssl
126
+
127
+ # @note to be able to hook up into #ssl_context from tests
128
+ def new_ssl_context
129
+ OpenSSL::SSL::SSLContext.new
130
+ end
131
+ private :new_ssl_context
132
+
133
+ # @overload Base#register
110
134
  def register
111
135
  require "socket"
112
136
  require "stud/try"
113
- if @ssl_enable
114
- setup_ssl
115
- end # @ssl_enable
137
+ @closed = Concurrent::AtomicBoolean.new(false)
138
+ setup_ssl if @ssl_enable
116
139
 
117
140
  if server?
118
141
  @logger.info("Starting tcp output listener", :address => "#{@host}:#{@port}")
119
142
  begin
120
143
  @server_socket = TCPServer.new(@host, @port)
121
144
  rescue Errno::EADDRINUSE
122
- @logger.error("Could not start TCP server: Address in use",
123
- :host => @host, :port => @port)
145
+ @logger.error("Could not start tcp server: Address in use", host: @host, port: @port)
124
146
  raise
125
147
  end
126
148
  if @ssl_enable
127
149
  @server_socket = OpenSSL::SSL::SSLServer.new(@server_socket, @ssl_context)
128
150
  end # @ssl_enable
129
- @client_threads = []
151
+ @client_threads = Concurrent::Array.new
130
152
 
131
153
  @accept_thread = Thread.new(@server_socket) do |server_socket|
154
+ LogStash::Util.set_thread_name("[#{pipeline_id}]|output|tcp|server_accept")
132
155
  loop do
133
- Thread.start(server_socket.accept) do |client_socket|
156
+ break if @closed.value
157
+ client_socket = server_socket.accept_nonblock exception: false
158
+ if client_socket == :wait_readable
159
+ IO.select [ server_socket ]
160
+ next
161
+ end
162
+ Thread.start(client_socket) do |client_socket|
134
163
  # monkeypatch a 'peer' method onto the socket.
135
164
  client_socket.instance_eval { class << self; include ::LogStash::Util::SocketPeer end }
136
- @logger.debug("Accepted connection", :client => client_socket.peer,
137
- :server => "#{@host}:#{@port}")
165
+ @logger.debug("accepted connection", client: client_socket.peer, server: "#{@host}:#{@port}")
138
166
  client = Client.new(client_socket, @logger)
139
167
  Thread.current[:client] = client
168
+ LogStash::Util.set_thread_name("[#{pipeline_id}]|output|tcp|client_socket-#{@client_threads.size}")
140
169
  @client_threads << Thread.current
141
- client.run
170
+ client.run unless @closed.value
142
171
  end
143
172
  end
144
173
  end
145
174
 
146
175
  @codec.on_event do |event, payload|
176
+ @client_threads.select!(&:alive?)
147
177
  @client_threads.each do |client_thread|
148
178
  client_thread[:client].write(payload)
149
179
  end
150
- @client_threads.reject! {|t| !t.alive? }
151
180
  end
152
181
  else
153
182
  client_socket = nil
@@ -163,8 +192,7 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
163
192
  # Now send the payload
164
193
  client_socket.syswrite(payload) if w.any?
165
194
  rescue => e
166
- @logger.warn("tcp output exception", :host => @host, :port => @port,
167
- :exception => e, :backtrace => e.backtrace)
195
+ log_warn "client socket failed:", e, host: @host, port: @port, socket: (client_socket ? client_socket.to_s : nil)
168
196
  client_socket.close rescue nil
169
197
  client_socket = nil
170
198
  sleep @reconnect_interval
@@ -172,9 +200,27 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
172
200
  end
173
201
  end
174
202
  end
175
- end # def register
203
+ end
204
+
205
+ # @overload Base#receive
206
+ def receive(event)
207
+ @codec.encode(event)
208
+ end
209
+
210
+ # @overload Base#close
211
+ def close
212
+ @closed.make_true
213
+ @server_socket.close rescue nil if @server_socket
214
+
215
+ return unless @client_threads
216
+ @client_threads.each do |thread|
217
+ client = thread[:client]
218
+ client.close rescue nil if client
219
+ end
220
+ end
176
221
 
177
222
  private
223
+
178
224
  def connect
179
225
  begin
180
226
  client_socket = TCPSocket.new(@host, @port)
@@ -183,29 +229,40 @@ class LogStash::Outputs::Tcp < LogStash::Outputs::Base
183
229
  begin
184
230
  client_socket.connect
185
231
  rescue OpenSSL::SSL::SSLError => ssle
186
- @logger.error("SSL Error", :exception => ssle, :backtrace => ssle.backtrace)
232
+ log_error 'connect ssl failure:', ssle, backtrace: false
187
233
  # NOTE(mrichar1): Hack to prevent hammering peer
188
234
  sleep(5)
189
235
  raise
190
236
  end
191
237
  end
192
238
  client_socket.instance_eval { class << self; include ::LogStash::Util::SocketPeer end }
193
- @logger.debug("Opened connection", :client => "#{client_socket.peer}")
239
+ @logger.debug("opened connection", :client => client_socket.peer)
194
240
  return client_socket
195
- rescue StandardError => e
196
- @logger.error("Failed to connect: #{e.message}", :exception => e.class, :backtrace => e.backtrace)
241
+ rescue => e
242
+ log_error 'failed to connect:', e
197
243
  sleep @reconnect_interval
198
244
  retry
199
245
  end
200
246
  end # def connect
201
247
 
202
- private
203
248
  def server?
204
249
  @mode == "server"
205
250
  end # def server?
206
251
 
207
- public
208
- def receive(event)
209
- @codec.encode(event)
210
- end # def receive
252
+ def pipeline_id
253
+ execution_context.pipeline_id || 'main'
254
+ end
255
+
256
+ def log_warn(msg, e, backtrace: @logger.debug?, **details)
257
+ details = details.merge message: e.message, exception: e.class
258
+ details[:backtrace] = e.backtrace if backtrace
259
+ @logger.warn(msg, details)
260
+ end
261
+
262
+ def log_error(msg, e, backtrace: @logger.info?, **details)
263
+ details = details.merge message: e.message, exception: e.class
264
+ details[:backtrace] = e.backtrace if backtrace
265
+ @logger.error(msg, details)
266
+ end
267
+
211
268
  end # class LogStash::Outputs::Tcp
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-output-tcp'
4
- s.version = '6.0.2'
4
+ s.version = '6.1.0'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Writes events over a TCP socket"
7
7
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -21,11 +21,14 @@ Gem::Specification.new do |s|
21
21
 
22
22
  # Gem dependencies
23
23
  s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
24
-
24
+ s.add_runtime_dependency 'logstash-core', '>= 8.1.0'
25
25
  s.add_runtime_dependency 'logstash-codec-json'
26
26
  s.add_runtime_dependency 'stud'
27
27
 
28
+ s.add_runtime_dependency 'jruby-openssl', '>= 0.12.2' # 0.12 supports TLSv1.3
29
+
28
30
  s.add_development_dependency 'logstash-devutils'
31
+ s.add_development_dependency 'logstash-codec-plain'
29
32
  s.add_development_dependency 'flores'
30
33
  end
31
34
 
@@ -4,13 +4,112 @@ require "flores/pki"
4
4
 
5
5
  describe LogStash::Outputs::Tcp do
6
6
  subject { described_class.new(config) }
7
- let(:config) { {
8
- "host" => "localhost",
9
- "port" => 2000 + rand(3000),
10
- } }
7
+
8
+ let(:port) do
9
+ begin
10
+ # Start high to better avoid common services
11
+ port = rand(10000..65535)
12
+ s = TCPServer.new("127.0.0.1", port)
13
+ s.close
14
+
15
+ port
16
+ rescue Errno::EADDRINUSE
17
+ retry
18
+ end
19
+ end
20
+
21
+ let(:server) { TCPServer.new("127.0.0.1", port) }
22
+
23
+ let(:config) { { "host" => "localhost", "port" => port } }
24
+
25
+ let(:event) { LogStash::Event.new('message' => 'foo bar') }
26
+
27
+ context 'failing to connect' do
28
+
29
+ before { subject.register }
30
+
31
+ let(:config) { super().merge 'port' => 1000 }
32
+
33
+ it 'fails to connect' do
34
+ expect( subject ).to receive(:log_error).and_call_original
35
+ Thread.start { subject.receive(event) }
36
+ sleep 1.0
37
+ end
38
+
39
+ end
40
+
41
+ context 'server mode' do
42
+
43
+ before { subject.register }
44
+
45
+ let(:config) { super().merge 'mode' => 'server' }
46
+
47
+ let(:client) do
48
+ Stud::try(3.times) { TCPSocket.new("127.0.0.1", port) }
49
+ end
50
+
51
+ after { subject.close }
52
+
53
+ it 'receives serialized data' do; require 'json'
54
+ client # connect
55
+ Thread.start { sleep 0.5; subject.receive event }
56
+
57
+ read = client.recv(1000)
58
+ expect( read.size ).to be > 0
59
+ expect( JSON.parse(read)['message'] ).to eql 'foo bar'
60
+ end
61
+
62
+ end
63
+
64
+ context "with forced protocol" do
65
+ let(:config) do
66
+ super().merge 'ssl_supported_protocols' => [ 'TLSv1.1' ]
67
+ end
68
+
69
+ it "limits protocol selection" do
70
+ if OpenSSL::SSL.const_defined? :OP_NO_TLSv1_3
71
+ ssl_context = subject.send :setup_ssl
72
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_3).to_not eql 0
73
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_2).to_not eql 0
74
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_1).to eql 0
75
+ else
76
+ ssl_context = OpenSSL::SSL::SSLContext.new
77
+ allow(subject).to receive(:new_ssl_context).and_return(ssl_context)
78
+ expect(ssl_context).to receive(:max_version=).with(:'TLS1_2').and_call_original
79
+ ssl_context = subject.send :setup_ssl
80
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_2).to_not eql 0
81
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_1).to eql 0
82
+ end
83
+ end
84
+ end
85
+
86
+ context "with protocol range" do
87
+ let(:config) do
88
+ super().merge 'ssl_supported_protocols' => [ 'TLSv1.3', 'TLSv1.1', 'TLSv1.2' ]
89
+ end
90
+
91
+ it "does not limit protocol selection (except min_version)" do
92
+ ssl_context = OpenSSL::SSL::SSLContext.new
93
+ allow(subject).to receive(:new_ssl_context).and_return(ssl_context)
94
+ expect(ssl_context).to receive(:min_version=).with(:'TLS1_1').at_least(1).and_call_original
95
+
96
+ if OpenSSL::SSL.const_defined? :OP_NO_TLSv1_3
97
+ subject.send :setup_ssl
98
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_3).to eql 0
99
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_2).to eql 0
100
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_1).to eql 0
101
+ else
102
+ subject.send :setup_ssl
103
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_2).to eql 0
104
+ expect(ssl_context.options & OpenSSL::SSL::OP_NO_TLSv1_1).to eql 0
105
+ end
106
+
107
+ subject.send :setup_ssl
108
+ end
109
+ end
11
110
 
12
111
  context "when enabling SSL" do
13
- let(:config) { super().merge("ssl_enable" => true) }
112
+ let(:config) { super().merge("ssl_enable" => true, 'codec' => 'plain') }
14
113
  context "and not providing a certificate/key pair" do
15
114
  it "registers without error" do
16
115
  expect { subject.register }.to_not raise_error
@@ -51,6 +150,62 @@ describe LogStash::Outputs::Tcp do
51
150
  end
52
151
 
53
152
  end
153
+
154
+ let(:secure_server) do
155
+ ssl_context = OpenSSL::SSL::SSLContext.new
156
+ ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
157
+ ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(crt_file))
158
+ ssl_context.key = OpenSSL::PKey::RSA.new(File.read(key_file), nil)
159
+ ssl_context.ssl_version = server_ssl_version if server_ssl_version
160
+ ssl_context.min_version = server_min_version if server_min_version
161
+ ssl_context.max_version = server_max_version if server_max_version
162
+ OpenSSL::SSL::SSLServer.new(server, ssl_context)
163
+ end
164
+
165
+ let(:server_min_version) { nil }
166
+ let(:server_max_version) { nil }
167
+ let(:server_ssl_version) { nil }
168
+
169
+ context 'with supported protocol' do
170
+
171
+ let(:config) { super().merge("ssl_supported_protocols" => ['TLSv1.2']) }
172
+
173
+ let(:server_min_version) { 'TLS1_2' }
174
+
175
+ before { subject.register }
176
+ after { secure_server.close }
177
+
178
+ it 'reads plain data' do
179
+ Thread.start { sleep 0.25; subject.receive event }
180
+ socket = secure_server.accept
181
+ read = socket.sysread(100)
182
+ expect( read.size ).to be > 0
183
+ expect( read ).to end_with 'foo bar'
184
+ end
185
+
186
+ end
187
+
188
+ context 'with unsupported protocol (on server)' do
189
+
190
+ let(:config) { super().merge("ssl_supported_protocols" => ['TLSv1.1']) }
191
+
192
+ let(:server_min_version) { 'TLS1_2' }
193
+
194
+ before { subject.register }
195
+ after { secure_server.close }
196
+
197
+ it 'fails (and loops retrying)' do
198
+ expect(subject.logger).to receive(:error).with(/connect ssl failure/i, hash_including(message: /No appropriate protocol/i)).and_call_original
199
+ expect(subject.logger).to receive(:error).with(/failed to connect/i, hash_including(exception: OpenSSL::SSL::SSLError)).and_call_original
200
+ expect(subject).to receive(:sleep).once.and_call_original
201
+ expect(subject).to receive(:sleep).once.and_throw :TEST_DONE # to be able to abort the retry loop
202
+
203
+ Thread.start { secure_server.accept rescue nil }
204
+ expect { subject.receive event }.to throw_symbol(:TEST_DONE)
205
+ end
206
+
207
+ end if LOGSTASH_VERSION > '7.0'
208
+
54
209
  end
55
210
 
56
211
  context "encrypted key using PKCS#1" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-output-tcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.2
4
+ version: 6.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-21 00:00:00.000000000 Z
11
+ date: 2022-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -30,6 +30,20 @@ dependencies:
30
30
  - - "<="
31
31
  - !ruby/object:Gem::Version
32
32
  version: '2.99'
33
+ - !ruby/object:Gem::Dependency
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: 8.1.0
39
+ name: logstash-core
40
+ prerelease: false
41
+ type: :runtime
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 8.1.0
33
47
  - !ruby/object:Gem::Dependency
34
48
  requirement: !ruby/object:Gem::Requirement
35
49
  requirements:
@@ -58,6 +72,20 @@ dependencies:
58
72
  - - ">="
59
73
  - !ruby/object:Gem::Version
60
74
  version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 0.12.2
81
+ name: jruby-openssl
82
+ prerelease: false
83
+ type: :runtime
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 0.12.2
61
89
  - !ruby/object:Gem::Dependency
62
90
  requirement: !ruby/object:Gem::Requirement
63
91
  requirements:
@@ -72,6 +100,20 @@ dependencies:
72
100
  - - ">="
73
101
  - !ruby/object:Gem::Version
74
102
  version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ name: logstash-codec-plain
110
+ prerelease: false
111
+ type: :development
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
75
117
  - !ruby/object:Gem::Dependency
76
118
  requirement: !ruby/object:Gem::Requirement
77
119
  requirements: