logstash-output-http 5.1.2 → 5.2.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: 0def77507d3ea7b9d83ede431e0853c8f16e77f6249b11b62328e40cd3a0c403
4
- data.tar.gz: 85108445896c3ea59793b997e2b73f28dbe7d4fa05cb028af09b3727a8df6e9e
3
+ metadata.gz: 74a3adbb0ae7188d50a8c025f7fd256cfb4adb36e4e17862c6d64ae6b6b6c8ee
4
+ data.tar.gz: 72e55c3f943a81d2e5e89e4a52ba54a39495e7f9ea3e684273822d7dc59d7ec4
5
5
  SHA512:
6
- metadata.gz: ee4d0f5fc3152eed88be134bc5c72d99d6f2dccce346fd5bf45bcb3d4ebef72fba9b8b57c2f34dd5053f6b6550fd6a458837edc2bfd0c17d8ff3881f736a4be9
7
- data.tar.gz: eaec797c6f1d257c0c4b8ad99d4e8fc329508ec088536f5a204dd169cbeb84efbf2649a753a8684924211be6b51eedd26acef822d8c38dea077dc9399bd634c1
6
+ metadata.gz: d7d0109785a2fe03f9936e6a7d56cf6406d3c81c1f66f7afdae0d45fe9b998dfdd95fe1e5970ccec15b69f6f884e9104aacfc7ca063a83d7feae3b35e278bb8a
7
+ data.tar.gz: 074e51a776ec7c1b12f3fbb33ccc9da9d6bd1188b32884bd6403506709c09083c930420550f9ee5ce2d8c5a4aff32dcc709972df71a53bb69c6a05044b1580fe
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 5.2.0
2
+ - Added json_batch format
3
+ - Make 429 responses log at debug, not error level. They are really just flow control
4
+
1
5
  ## 5.1.2
2
6
  - Add check to avoid hanging pipeline if an empty event array is passed in. #80
3
7
 
data/docs/index.asciidoc CHANGED
@@ -46,7 +46,7 @@ This plugin supports the following configuration options plus the <<plugins-{typ
46
46
  | <<plugins-{type}s-{plugin}-content_type>> |<<string,string>>|No
47
47
  | <<plugins-{type}s-{plugin}-cookies>> |<<boolean,boolean>>|No
48
48
  | <<plugins-{type}s-{plugin}-follow_redirects>> |<<boolean,boolean>>|No
49
- | <<plugins-{type}s-{plugin}-format>> |<<string,string>>, one of `["json", "form", "message"]`|No
49
+ | <<plugins-{type}s-{plugin}-format>> |<<string,string>>, one of `["json", "json_batch", "form", "message"]`|No
50
50
  | <<plugins-{type}s-{plugin}-headers>> |<<hash,hash>>|No
51
51
  | <<plugins-{type}s-{plugin}-http_compression>> |<<boolean,boolean>>|No
52
52
  | <<plugins-{type}s-{plugin}-http_method>> |<<string,string>>, one of `["put", "post", "patch", "delete", "get", "head"]`|Yes
@@ -130,6 +130,7 @@ Content type
130
130
  If not specified, this defaults to the following:
131
131
 
132
132
  * if format is "json", "application/json"
133
+ * if format is "json_batch", "application/json". Each Logstash batch of events will be concatenated into a single array and sent in one request.
133
134
  * if format is "form", "application/x-www-form-urlencoded"
134
135
 
135
136
  [id="plugins-{type}s-{plugin}-cookies"]
@@ -152,11 +153,15 @@ Should redirects be followed? Defaults to `true`
152
153
  [id="plugins-{type}s-{plugin}-format"]
153
154
  ===== `format`
154
155
 
155
- * Value can be any of: `json`, `form`, `message`
156
+ * Value can be any of: `json`, `json_batch`, `form`, `message`
156
157
  * Default value is `"json"`
157
158
 
158
159
  Set the format of the http body.
159
160
 
161
+ If json_batch, each batch of events received by this output will be placed
162
+ into a single JSON array and sent in one request. This is particularly useful
163
+ for high throughput scenarios such as sending data between Logstash instaces.
164
+
160
165
  If form, then the body will be the mapping (or whole event) converted
161
166
  into a query parameter string, e.g. `foo=bar&baz=fizz...`
162
167
 
@@ -42,7 +42,7 @@ class LogStash::Outputs::Http < LogStash::Outputs::Base
42
42
 
43
43
  # Custom headers to use
44
44
  # format is `headers => ["X-My-Header", "%{host}"]`
45
- config :headers, :validate => :hash
45
+ config :headers, :validate => :hash, :default => {}
46
46
 
47
47
  # Content type
48
48
  #
@@ -79,7 +79,7 @@ class LogStash::Outputs::Http < LogStash::Outputs::Base
79
79
  # If message, then the body will be the result of formatting the event according to message
80
80
  #
81
81
  # Otherwise, the event is sent as json.
82
- config :format, :validate => ["json", "form", "message"], :default => "json"
82
+ config :format, :validate => ["json", "json_batch", "form", "message"], :default => "json"
83
83
 
84
84
  # Set this to true if you want to enable gzip compression for your http requests
85
85
  config :http_compression, :validate => :boolean, :default => false
@@ -102,10 +102,14 @@ class LogStash::Outputs::Http < LogStash::Outputs::Base
102
102
  case @format
103
103
  when "form" ; @content_type = "application/x-www-form-urlencoded"
104
104
  when "json" ; @content_type = "application/json"
105
+ when "json_batch" ; @content_type = "application/json"
105
106
  when "message" ; @content_type = "text/plain"
106
107
  end
107
108
  end
108
109
 
110
+
111
+ @headers["Content-Type"] = @content_type
112
+
109
113
  validate_format!
110
114
 
111
115
  # Run named Timer as daemon thread
@@ -113,7 +117,12 @@ class LogStash::Outputs::Http < LogStash::Outputs::Base
113
117
  end # def register
114
118
 
115
119
  def multi_receive(events)
116
- send_events(events) unless events.empty?
120
+ return if events.empty?
121
+ if @format == "json_batch"
122
+ send_json_batch(events)
123
+ else
124
+ send_events(events)
125
+ end
117
126
  end
118
127
 
119
128
  class RetryTimerTask < java.util.TimerTask
@@ -128,6 +137,46 @@ class LogStash::Outputs::Http < LogStash::Outputs::Base
128
137
  @pending << [@event, @attempt]
129
138
  end
130
139
  end
140
+
141
+ def send_json_batch(events)
142
+ attempt = 1
143
+ body = LogStash::Json.dump(events.map {|e| map_event(e) })
144
+ begin
145
+ while true
146
+ request = client.send(@http_method, @url, :body => body, :headers => @headers)
147
+ response = request.call
148
+ break if response_success?(response)
149
+ if retryable_response?(response)
150
+ log_retryable_response(response)
151
+ sleep_for_attempt attempt
152
+ attempt += 1
153
+ else
154
+ log_error_response(response, url, events)
155
+ end
156
+ end
157
+ rescue *RETRYABLE_MANTICORE_EXCEPTIONS => e
158
+ logger.warn("Encountered exception during http output send, will retry after delay", :message => e.message, :class => e.class.name)
159
+ sleep_for_attempt attempt
160
+ retry
161
+ end
162
+ end
163
+
164
+ def log_retryable_response(response)
165
+ if (response.code == 429)
166
+ @logger.debug? && @logger.debug("Encountered a 429 response, will retry. This is not serious, just flow control via HTTP")
167
+ else
168
+ @logger.warn("Encountered a retryable HTTP request in HTTP output, will retry", :code => response.code, :body => response.body)
169
+ end
170
+ end
171
+
172
+ def log_error_response(response, url, event)
173
+ log_failure(
174
+ "Encountered non-2xx HTTP code #{response.code}",
175
+ :response_code => response.code,
176
+ :url => url,
177
+ :event => event
178
+ )
179
+ end
131
180
 
132
181
  def send_events(events)
133
182
  successes = java.util.concurrent.atomic.AtomicInteger.new(0)
@@ -212,18 +261,11 @@ class LogStash::Outputs::Http < LogStash::Outputs::Base
212
261
  request.on_success do |response|
213
262
  begin
214
263
  if !response_success?(response)
215
- will_retry = retryable_response?(response)
216
- log_failure(
217
- "Encountered non-2xx HTTP code #{response.code}",
218
- :response_code => response.code,
219
- :url => url,
220
- :event => event,
221
- :will_retry => will_retry
222
- )
223
-
224
- if will_retry
264
+ if retryable_response?(response)
265
+ log_retryable_response(response)
225
266
  yield :retry, event, attempt
226
267
  else
268
+ log_error_response(response, url, event)
227
269
  yield :failure, event, attempt
228
270
  end
229
271
  else
@@ -343,9 +385,7 @@ class LogStash::Outputs::Http < LogStash::Outputs::Base
343
385
  end
344
386
 
345
387
  def event_headers(event)
346
- headers = custom_headers(event) || {}
347
- headers["Content-Type"] = @content_type
348
- headers
388
+ custom_headers(event) || {}
349
389
  end
350
390
 
351
391
  def custom_headers(event)
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-output-http'
3
- s.version = '5.1.2'
3
+ s.version = '5.2.0'
4
4
  s.licenses = ['Apache License (2.0)']
5
5
  s.summary = "Sends events to a generic HTTP or HTTPS endpoint"
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/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -47,7 +47,7 @@ class TestApp < Sinatra::Base
47
47
  end
48
48
 
49
49
  def self.retry_fail_count()
50
- @retry_fail_count
50
+ @retry_fail_count || 2
51
51
  end
52
52
 
53
53
  multiroute(%w(get post put patch delete), "/good") do
@@ -126,6 +126,7 @@ describe LogStash::Outputs::Http do
126
126
  with(expected_method, url, anything).
127
127
  and_call_original
128
128
  allow(subject).to receive(:log_failure).with(any_args)
129
+ allow(subject).to receive(:log_retryable_response).with(any_args)
129
130
  end
130
131
 
131
132
  context 'sending no events' do
@@ -190,8 +191,8 @@ describe LogStash::Outputs::Http do
190
191
  subject.multi_receive([event])
191
192
  end
192
193
 
193
- it "should log a failure 2 times" do
194
- expect(subject).to have_received(:log_failure).with(any_args).twice
194
+ it "should log a retryable response 2 times" do
195
+ expect(subject).to have_received(:log_retryable_response).with(any_args).twice
195
196
  end
196
197
 
197
198
  it "should make three total requests" do
@@ -213,24 +214,43 @@ describe LogStash::Outputs::Http do
213
214
  TestApp.last_request = nil
214
215
  end
215
216
 
216
- before do
217
- subject.multi_receive([event])
218
- end
217
+ let(:events) { [event] }
219
218
 
220
- let(:last_request) { TestApp.last_request }
221
- let(:body) { last_request.body.read }
222
- let(:content_type) { last_request.env["CONTENT_TYPE"] }
219
+ describe "with a good code" do
220
+ before do
221
+ subject.multi_receive(events)
222
+ end
223
223
 
224
- it "should receive the request" do
225
- expect(last_request).to be_truthy
226
- end
224
+ let(:last_request) { TestApp.last_request }
225
+ let(:body) { last_request.body.read }
226
+ let(:content_type) { last_request.env["CONTENT_TYPE"] }
227
+
228
+ it "should receive the request" do
229
+ expect(last_request).to be_truthy
230
+ end
231
+
232
+ it "should receive the event as a hash" do
233
+ expect(body).to eql(expected_body)
234
+ end
227
235
 
228
- it "should receive the event as a hash" do
229
- expect(body).to eql(expected_body)
236
+ it "should have the correct content type" do
237
+ expect(content_type).to eql(expected_content_type)
238
+ end
230
239
  end
231
240
 
232
- it "should have the correct content type" do
233
- expect(content_type).to eql(expected_content_type)
241
+ describe "a retryable code" do
242
+ let(:url) { "http://localhost:#{port}/retry" }
243
+
244
+ before do
245
+ TestApp.retry_fail_count=2
246
+ allow(subject).to receive(:send_event).and_call_original
247
+ allow(subject).to receive(:log_retryable_response)
248
+ subject.multi_receive(events)
249
+ end
250
+
251
+ it "should retry" do
252
+ expect(subject).to have_received(:log_retryable_response).with(any_args).twice
253
+ end
234
254
  end
235
255
  end
236
256
 
@@ -257,6 +277,19 @@ describe LogStash::Outputs::Http do
257
277
  include_examples("a received event")
258
278
  end
259
279
 
280
+ describe "sending the batch as JSON" do
281
+ let(:config) do
282
+ base_config.merge({"url" => url, "http_method" => "post", "format" => "json_batch"})
283
+ end
284
+
285
+ let(:expected_body) { ::LogStash::Json.dump events }
286
+ let(:events) { [::LogStash::Event.new("a" => 1), ::LogStash::Event.new("b" => 2)]}
287
+ let(:expected_content_type) { "application/json" }
288
+
289
+ include_examples("a received event")
290
+
291
+ end
292
+
260
293
  describe "sending the event as a form" do
261
294
  let(:config) {
262
295
  base_config.merge({"url" => url, "http_method" => "post", "pool_max" => 1, "format" => "form"})
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-output-http
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.2
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-09 00:00:00.000000000 Z
11
+ date: 2018-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement