logstash-output-elasticsearch 5.3.5-java → 5.4.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/lib/logstash/outputs/elasticsearch.rb +5 -0
- data/lib/logstash/outputs/elasticsearch/common_configs.rb +2 -2
- data/lib/logstash/outputs/elasticsearch/http_client.rb +11 -2
- data/lib/logstash/outputs/elasticsearch/http_client/manticore_adapter.rb +2 -1
- data/lib/logstash/outputs/elasticsearch/http_client/pool.rb +19 -12
- data/lib/logstash/outputs/elasticsearch/http_client_builder.rb +4 -0
- data/logstash-output-elasticsearch.gemspec +1 -1
- data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +7 -3
- data/spec/unit/outputs/elasticsearch_proxy_spec.rb +2 -1
- data/spec/unit/outputs/elasticsearch_spec.rb +89 -1
- data/spec/unit/outputs/elasticsearch_ssl_spec.rb +7 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4c13cc28976cdad32da8417651ef69ad5695333
|
4
|
+
data.tar.gz: 145160a4c63cd206a3d869f044b434a169fe5f43
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0256fab32a89ddafee3dbe18023c4d1bc415d32d1aab8bd30e11bac473760bc6c5fa9beab01b058ebee9e14ec4163f9ef1882527e5a5205592add85b63007b25
|
7
|
+
data.tar.gz: 297813ad0a6c5103c04fc9cd5085829bb8982bb56be9c15526561fc361d5094f2dbecbde3d5c9dd93b5d4fc1513c1be51dd6ddf8d71c73c15026e802c2411d46
|
data/CHANGELOG.md
CHANGED
@@ -102,6 +102,11 @@ class LogStash::Outputs::ElasticSearch < LogStash::Outputs::Base
|
|
102
102
|
# not also set this field. That will raise an error at startup
|
103
103
|
config :path, :validate => :string
|
104
104
|
|
105
|
+
# Pass a set of key value pairs as the URL query string. This query string is added
|
106
|
+
# to every host listed in the 'hosts' configuration. If the 'hosts' list contains
|
107
|
+
# urls that already have query strings, the one specified here will be appended.
|
108
|
+
config :parameters, :validate => :hash
|
109
|
+
|
105
110
|
# Enable SSL/TLS secured communication to Elasticsearch cluster. Leaving this unspecified will use whatever scheme
|
106
111
|
# is specified in the URLs listed in 'hosts'. If no explicit protocol is specified plain HTTP will be used.
|
107
112
|
# If SSL is explicitly disabled here the plugin will refuse to start if an HTTPS URL is given in 'hosts'
|
@@ -79,8 +79,8 @@ module LogStash; module Outputs; class ElasticSearch
|
|
79
79
|
mod.config :hosts, :validate => :array, :default => ["127.0.0.1"]
|
80
80
|
|
81
81
|
# This plugin uses the bulk index API for improved indexing performance.
|
82
|
-
# This setting defines the maximum sized bulk request Logstash will make
|
83
|
-
# You
|
82
|
+
# This setting defines the maximum sized bulk request Logstash will make.
|
83
|
+
# You may want to increase this to be in line with your pipeline's batch size.
|
84
84
|
# If you specify a number larger than the batch size of your pipeline it will have no effect,
|
85
85
|
# save for the case where a filter increases the size of an inflight batch by outputting
|
86
86
|
# events.
|
@@ -131,7 +131,7 @@ module LogStash; module Outputs; class ElasticSearch;
|
|
131
131
|
timeout = options[:timeout] || 0
|
132
132
|
|
133
133
|
host_ssl_opt = client_settings[:ssl].nil? ? nil : client_settings[:ssl][:enabled]
|
134
|
-
urls = hosts.map {|host| host_to_url(host, host_ssl_opt, client_settings[:path])}
|
134
|
+
urls = hosts.map {|host| host_to_url(host, host_ssl_opt, client_settings[:path], client_settings[:parameters])}
|
135
135
|
|
136
136
|
adapter_options = {
|
137
137
|
:socket_timeout => timeout,
|
@@ -186,7 +186,7 @@ module LogStash; module Outputs; class ElasticSearch;
|
|
186
186
|
HOSTNAME_PORT_REGEX=/\A(?<hostname>([A-Za-z0-9\.\-]+)|\[[0-9A-Fa-f\:]+\])(:(?<port>\d+))?\Z/
|
187
187
|
URL_REGEX=/\A#{URI::regexp(['http', 'https'])}\z/
|
188
188
|
# Parse a configuration host to a normalized URL
|
189
|
-
def host_to_url(host, ssl=nil, path=nil)
|
189
|
+
def host_to_url(host, ssl=nil, path=nil, parameters=nil)
|
190
190
|
explicit_scheme = case ssl
|
191
191
|
when true
|
192
192
|
"https"
|
@@ -233,6 +233,15 @@ module LogStash; module Outputs; class ElasticSearch;
|
|
233
233
|
url.path = path # The URI library cannot stringify if it holds a nil
|
234
234
|
end
|
235
235
|
|
236
|
+
if parameters
|
237
|
+
query_string = parameters.map { |k,v| "#{k}=#{v}" }.join("&")
|
238
|
+
if query = url.query
|
239
|
+
url.query = "#{query}&#{query_string}"
|
240
|
+
else
|
241
|
+
url.query = query_string
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
236
245
|
if url.password || url.user
|
237
246
|
raise LogStash::ConfigurationError, "We do not support setting the user password in the URL directly as " +
|
238
247
|
"this may be logged to disk thus leaking credentials. Use the 'user' and 'password' options respectively"
|
@@ -31,7 +31,8 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
31
31
|
def perform_request(url, method, path, params={}, body=nil)
|
32
32
|
params = (params || {}).merge @request_options
|
33
33
|
params[:body] = body if body
|
34
|
-
|
34
|
+
# Convert URI object to string
|
35
|
+
url_and_path = path ? (url + path).to_s : url.to_s
|
35
36
|
|
36
37
|
resp = @manticore.send(method.downcase, url_and_path, params)
|
37
38
|
|
@@ -62,8 +62,9 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
62
62
|
# Holds metadata about all URLs
|
63
63
|
@url_info = {}
|
64
64
|
@stopping = false
|
65
|
-
|
65
|
+
|
66
66
|
update_urls(initial_urls)
|
67
|
+
|
67
68
|
start_resurrectionist
|
68
69
|
start_sniffer if @sniffing
|
69
70
|
end
|
@@ -96,7 +97,7 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
96
97
|
end
|
97
98
|
|
98
99
|
def alive_urls_count
|
99
|
-
@state_mutex.synchronize { @url_info.values.select {|v| !v[:
|
100
|
+
@state_mutex.synchronize { @url_info.values.select {|v| !v[:state] == :alive }.count }
|
100
101
|
end
|
101
102
|
|
102
103
|
def url_info
|
@@ -186,24 +187,24 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
186
187
|
def start_resurrectionist
|
187
188
|
@resurrectionist = Thread.new do
|
188
189
|
until_stopped("resurrection", @resurrect_delay) do
|
189
|
-
|
190
|
+
healthcheck!
|
190
191
|
end
|
191
192
|
end
|
192
193
|
end
|
193
194
|
|
194
|
-
def
|
195
|
+
def healthcheck!
|
195
196
|
# Try to keep locking granularity low such that we don't affect IO...
|
196
|
-
@state_mutex.synchronize { @url_info.select {|url,meta| meta[:
|
197
|
+
@state_mutex.synchronize { @url_info.select {|url,meta| meta[:state] != :alive } }.each do |url,meta|
|
197
198
|
safe_url = ::LogStash::Outputs::ElasticSearch::SafeURL.without_credentials(url)
|
198
199
|
begin
|
199
200
|
logger.info("Running health check to see if an Elasticsearch connection is working",
|
200
201
|
url: safe_url, healthcheck_path: @healthcheck_path)
|
201
|
-
perform_request_to_url(url, "HEAD", @healthcheck_path)
|
202
|
+
response = perform_request_to_url(url, "HEAD", @healthcheck_path)
|
202
203
|
# If no exception was raised it must have succeeded!
|
203
204
|
logger.warn("Restored connection to ES instance", :url => safe_url)
|
204
|
-
@state_mutex.synchronize { meta[:
|
205
|
-
rescue HostUnreachableError => e
|
206
|
-
logger.
|
205
|
+
@state_mutex.synchronize { meta[:state] = :alive }
|
206
|
+
rescue HostUnreachableError, BadResponseCodeError => e
|
207
|
+
logger.warn("Attempted to resurrect connection to dead ES instance, but got an error.", url: safe_url, error_type: e.class, error: e.message)
|
207
208
|
end
|
208
209
|
end
|
209
210
|
end
|
@@ -280,6 +281,12 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
280
281
|
logger.info("Elasticsearch pool URLs updated", :changes => safe_state_changes(state_changes))
|
281
282
|
end
|
282
283
|
end
|
284
|
+
|
285
|
+
# Run an inline healthcheck anytime URLs are updated
|
286
|
+
# This guarantees that during startup / post-startup
|
287
|
+
# sniffing we don't have idle periods waiting for the
|
288
|
+
# periodic sniffer to allow new hosts to come online
|
289
|
+
healthcheck!
|
283
290
|
end
|
284
291
|
|
285
292
|
def safe_state_changes(state_changes)
|
@@ -305,7 +312,7 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
305
312
|
def empty_url_meta
|
306
313
|
{
|
307
314
|
:in_use => 0,
|
308
|
-
:
|
315
|
+
:state => :unknown
|
309
316
|
}
|
310
317
|
end
|
311
318
|
|
@@ -340,7 +347,7 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
340
347
|
safe_url = ::LogStash::Outputs::ElasticSearch::SafeURL.without_credentials(url)
|
341
348
|
logger.warn("Marking url as dead.", :reason => error.message, :url => safe_url,
|
342
349
|
:error_message => error.message, :error_class => error.class.name)
|
343
|
-
meta[:
|
350
|
+
meta[:state] = :dead
|
344
351
|
meta[:last_error] = error
|
345
352
|
meta[:last_errored_at] = Time.now
|
346
353
|
end
|
@@ -361,7 +368,7 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
361
368
|
lowest_value_seen = nil
|
362
369
|
@url_info.each do |url,meta|
|
363
370
|
meta_in_use = meta[:in_use]
|
364
|
-
next if meta[:dead
|
371
|
+
next if meta[:state] == :dead
|
365
372
|
|
366
373
|
if lowest_value_seen.nil? || meta_in_use < lowest_value_seen
|
367
374
|
lowest_value_seen = meta_in_use
|
@@ -24,6 +24,10 @@ module LogStash; module Outputs; class ElasticSearch;
|
|
24
24
|
client_settings[:path] = "/#{params["path"]}/".gsub(/\/+/, "/") # Normalize slashes
|
25
25
|
end
|
26
26
|
|
27
|
+
if params["parameters"]
|
28
|
+
client_settings[:parameters] = params["parameters"]
|
29
|
+
end
|
30
|
+
|
27
31
|
logger.debug? && logger.debug("Normalizing http path", :path => params["path"], :normalized => client_settings[:path])
|
28
32
|
|
29
33
|
client_settings.merge! setup_ssl(logger, params)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
|
3
3
|
s.name = 'logstash-output-elasticsearch'
|
4
|
-
s.version = '5.
|
4
|
+
s.version = '5.4.0'
|
5
5
|
s.licenses = ['apache-2.0']
|
6
6
|
s.summary = "Logstash Output to Elasticsearch"
|
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"
|
@@ -9,6 +9,10 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
|
|
9
9
|
let(:options) { {:resurrect_delay => 2} } # Shorten the delay a bit to speed up tests
|
10
10
|
|
11
11
|
subject { described_class.new(logger, adapter, initial_urls, options) }
|
12
|
+
|
13
|
+
before do
|
14
|
+
allow(adapter).to receive(:perform_request).with(anything, 'HEAD', subject.healthcheck_path, {}, nil)
|
15
|
+
end
|
12
16
|
|
13
17
|
describe "initialization" do
|
14
18
|
it "should be successful" do
|
@@ -22,7 +26,7 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
|
|
22
26
|
end
|
23
27
|
|
24
28
|
it "should attempt to resurrect connections after the ressurrect delay" do
|
25
|
-
expect(subject).to receive(:
|
29
|
+
expect(subject).to receive(:healthcheck!).once
|
26
30
|
sleep(subject.resurrect_delay + 1)
|
27
31
|
end
|
28
32
|
end
|
@@ -148,9 +152,9 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do
|
|
148
152
|
subject.return_connection(u)
|
149
153
|
subject.mark_dead(u, Exception.new)
|
150
154
|
|
151
|
-
expect(subject.url_meta(u)[:
|
155
|
+
expect(subject.url_meta(u)[:state]).to eql(:dead)
|
152
156
|
sleep subject.resurrect_delay + 1
|
153
|
-
expect(subject.url_meta(u)[:
|
157
|
+
expect(subject.url_meta(u)[:state]).to eql(:alive)
|
154
158
|
end
|
155
159
|
end
|
156
160
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require_relative "../../../spec/es_spec_helper"
|
2
2
|
require 'stud/temporary'
|
3
3
|
require "logstash/outputs/elasticsearch"
|
4
|
+
require 'manticore/client'
|
4
5
|
|
5
6
|
describe "Proxy option" do
|
6
7
|
let(:settings) {
|
@@ -14,7 +15,7 @@ describe "Proxy option" do
|
|
14
15
|
}
|
15
16
|
|
16
17
|
before do
|
17
|
-
allow(::Manticore::Client).to receive(:new).with(any_args)
|
18
|
+
allow(::Manticore::Client).to receive(:new).with(any_args).and_call_original
|
18
19
|
end
|
19
20
|
|
20
21
|
describe "valid configs" do
|
@@ -128,6 +128,7 @@ describe "outputs/elasticsearch" do
|
|
128
128
|
end
|
129
129
|
end
|
130
130
|
end
|
131
|
+
|
131
132
|
describe "without a port specified" do
|
132
133
|
it "should properly set the default port (9200) on the HTTP client" do
|
133
134
|
expect(manticore_url.port).to eql(9200)
|
@@ -285,7 +286,7 @@ describe "outputs/elasticsearch" do
|
|
285
286
|
subject(:eso) { LogStash::Outputs::ElasticSearch.new("validate_after_inactivity" => validate_after_inactivity) }
|
286
287
|
|
287
288
|
before do
|
288
|
-
allow(::Manticore::Client).to receive(:new).with(any_args)
|
289
|
+
allow(::Manticore::Client).to receive(:new).with(any_args).and_call_original
|
289
290
|
subject.register
|
290
291
|
end
|
291
292
|
|
@@ -295,4 +296,91 @@ describe "outputs/elasticsearch" do
|
|
295
296
|
end
|
296
297
|
end
|
297
298
|
end
|
299
|
+
|
300
|
+
describe "custom parameters" do
|
301
|
+
|
302
|
+
let(:eso) {LogStash::Outputs::ElasticSearch.new(options)}
|
303
|
+
|
304
|
+
let(:manticore_urls) { eso.client.pool.urls }
|
305
|
+
let(:manticore_url) { manticore_urls.first }
|
306
|
+
|
307
|
+
let(:custom_parameters_hash) { { "id" => 1, "name" => "logstash" } }
|
308
|
+
let(:custom_parameters_query) { custom_parameters_hash.map {|k,v| "#{k}=#{v}" }.join("&") }
|
309
|
+
|
310
|
+
before(:each) do
|
311
|
+
eso.register
|
312
|
+
end
|
313
|
+
|
314
|
+
after(:each) do
|
315
|
+
eso.close rescue nil
|
316
|
+
end
|
317
|
+
|
318
|
+
context "using non-url hosts" do
|
319
|
+
|
320
|
+
let(:options) {
|
321
|
+
{
|
322
|
+
"index" => "my-index",
|
323
|
+
"hosts" => ["localhost:9202"],
|
324
|
+
"path" => "some-path",
|
325
|
+
"parameters" => custom_parameters_hash
|
326
|
+
}
|
327
|
+
}
|
328
|
+
|
329
|
+
it "creates a URI with the added parameters" do
|
330
|
+
expect(eso.parameters).to eql(custom_parameters_hash)
|
331
|
+
end
|
332
|
+
|
333
|
+
it "sets the query string on the HTTP client" do
|
334
|
+
expect(manticore_url.query).to eql(custom_parameters_query)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
context "using url hosts" do
|
339
|
+
|
340
|
+
context "with embedded query parameters" do
|
341
|
+
let(:options) {
|
342
|
+
{ "hosts" => ["http://localhost:9202/path?#{custom_parameters_query}"] }
|
343
|
+
}
|
344
|
+
|
345
|
+
it "sets the query string on the HTTP client" do
|
346
|
+
expect(manticore_url.query).to eql(custom_parameters_query)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
context "with explicity query parameters" do
|
351
|
+
let(:options) {
|
352
|
+
{
|
353
|
+
"hosts" => ["http://localhost:9202/path"],
|
354
|
+
"parameters" => custom_parameters_hash
|
355
|
+
}
|
356
|
+
}
|
357
|
+
|
358
|
+
it "sets the query string on the HTTP client" do
|
359
|
+
expect(manticore_url.query).to eql(custom_parameters_query)
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
context "with explicity query parameters and existing url parameters" do
|
364
|
+
let(:existing_query_string) { "existing=param" }
|
365
|
+
let(:options) {
|
366
|
+
{
|
367
|
+
"hosts" => ["http://localhost:9202/path?#{existing_query_string}"],
|
368
|
+
"parameters" => custom_parameters_hash
|
369
|
+
}
|
370
|
+
}
|
371
|
+
|
372
|
+
it "keeps the existing query string" do
|
373
|
+
expect(manticore_url.query).to include(existing_query_string)
|
374
|
+
end
|
375
|
+
|
376
|
+
it "includes the new query string" do
|
377
|
+
expect(manticore_url.query).to include(custom_parameters_query)
|
378
|
+
end
|
379
|
+
|
380
|
+
it "appends the new query string to the existing one" do
|
381
|
+
expect(manticore_url.query).to eql("#{existing_query_string}&#{custom_parameters_query}")
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
298
386
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require_relative "../../../spec/es_spec_helper"
|
2
2
|
require 'stud/temporary'
|
3
|
+
require "logstash/outputs/elasticsearch"
|
3
4
|
|
4
5
|
describe "SSL option" do
|
5
6
|
context "when using ssl without cert verification" do
|
@@ -16,7 +17,7 @@ describe "SSL option" do
|
|
16
17
|
end
|
17
18
|
|
18
19
|
it "should pass the flag to the ES client" do
|
19
|
-
expect(::Manticore::Client).to receive(:new) do |args|
|
20
|
+
expect(::Manticore::Client).to receive(:new).and_call_original do |args|
|
20
21
|
expect(args[:ssl]).to eq(:enabled => true, :verify => false)
|
21
22
|
end
|
22
23
|
subject.register
|
@@ -32,6 +33,9 @@ describe "SSL option" do
|
|
32
33
|
|
33
34
|
context "when using ssl with client certificates" do
|
34
35
|
let(:keystore_path) { Stud::Temporary.file.path }
|
36
|
+
before do
|
37
|
+
`openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout lumberjack.key -out #{keystore_path}.pem`
|
38
|
+
end
|
35
39
|
|
36
40
|
after :each do
|
37
41
|
File.delete(keystore_path)
|
@@ -42,8 +46,7 @@ describe "SSL option" do
|
|
42
46
|
settings = {
|
43
47
|
"hosts" => "node01",
|
44
48
|
"ssl" => true,
|
45
|
-
"
|
46
|
-
"keystore_password" => "test"
|
49
|
+
"cacert" => keystore_path,
|
47
50
|
}
|
48
51
|
next LogStash::Outputs::ElasticSearch.new(settings)
|
49
52
|
end
|
@@ -51,7 +54,7 @@ describe "SSL option" do
|
|
51
54
|
it "should pass the keystore parameters to the ES client" do
|
52
55
|
expect(::Manticore::Client).to receive(:new) do |args|
|
53
56
|
expect(args[:ssl]).to include(:keystore => keystore_path, :keystore_password => "test")
|
54
|
-
end
|
57
|
+
end.and_call_original
|
55
58
|
subject.register
|
56
59
|
end
|
57
60
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-output-elasticsearch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.4.0
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Elastic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-11-
|
11
|
+
date: 2016-11-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|