logstash-output-elasticsearch 5.3.5-java → 5.4.0-java
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 +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
|