logstash-output-http 5.1.2 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
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