fluent-plugin-elasticsearch 2.0.0.rc.5 → 2.0.0.rc.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +5 -0
- data/README.md +20 -5
- data/fluent-plugin-elasticsearch.gemspec +1 -1
- data/lib/fluent/plugin/out_elasticsearch.rb +20 -1
- data/lib/fluent/plugin/out_elasticsearch_dynamic.rb +19 -11
- data/test/plugin/test_out_elasticsearch.rb +45 -0
- data/test/plugin/test_out_elasticsearch_dynamic.rb +59 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac60b728ec23b3ba9a24a87f1b58fcf3dbf2376f
|
4
|
+
data.tar.gz: fe9d1c7d1f5c07d84a69e27bc534c6c6db5512b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51b2bf67047821e1d028acdc507d9f1f235444cd6ffa2d0f6dded6284231ef86473aed7e2e448daa89af31ff3a6cbb2a05aa17a36be5a8717a9a1473ad05941d
|
7
|
+
data.tar.gz: b5a8317b90ddd6b52a8c6cdd2fddf7f609682422bcd24308332d1a760acf990851fb3bfde16d7c6ccd7c430aeb0f1b03186e163c21b35d4a00f4b1b87242ad57
|
data/History.md
CHANGED
@@ -4,6 +4,11 @@
|
|
4
4
|
- Log ES response errors (#230)
|
5
5
|
- Use latest elasticsearch-ruby (#240)
|
6
6
|
|
7
|
+
### 2.0.0.rc.6
|
8
|
+
- Improve documentation (#304)
|
9
|
+
- Handle dynamic_config misconfigurations (#305)
|
10
|
+
- Escape basic authentication user information placeholders (#306)
|
11
|
+
|
7
12
|
### 2.0.0.rc.5
|
8
13
|
- make configurable with `ssl_version` parameter (#299)
|
9
14
|
- add `logstash_prefix_separator` config parameter (#297)
|
data/README.md
CHANGED
@@ -109,6 +109,12 @@ You can specify multiple ElasticSearch hosts with separator ",".
|
|
109
109
|
|
110
110
|
If you specify multiple hosts, this plugin will load balance updates to ElasticSearch. This is an [elasticsearch-ruby](https://github.com/elasticsearch/elasticsearch-ruby) feature, the default strategy is round-robin.
|
111
111
|
|
112
|
+
And this plugin will escape required URL encoded characters within `%{}` placeholders.
|
113
|
+
|
114
|
+
```
|
115
|
+
hosts https://%{j+hn}:%{passw@rd}@host1:443/elastic/,http://host2
|
116
|
+
```
|
117
|
+
|
112
118
|
### user, password, path, scheme, ssl_verify
|
113
119
|
|
114
120
|
If you specify this option, host and port options are ignored.
|
@@ -122,6 +128,13 @@ scheme https
|
|
122
128
|
|
123
129
|
You can specify user and password for HTTP basic auth. If used in conjunction with a hosts list, then these options will be used by default i.e. if you do not provide any of these options within the hosts listed.
|
124
130
|
|
131
|
+
And this plugin will escape required URL encoded characters within `%{}` placeholders.
|
132
|
+
|
133
|
+
```
|
134
|
+
user %{demo+}
|
135
|
+
password %{@secret}
|
136
|
+
```
|
137
|
+
|
125
138
|
Specify `ssl_verify false` to skip ssl verification (defaults to true)
|
126
139
|
|
127
140
|
### logstash_format
|
@@ -132,6 +145,8 @@ logstash_format true # defaults to false
|
|
132
145
|
|
133
146
|
This is meant to make writing data into ElasticSearch indices compatible to what [Logstash](https://www.elastic.co/products/logstash) calls them. By doing this, one could take advantage of [Kibana](https://www.elastic.co/products/kibana). See logstash_prefix and logstash_dateformat to customize this index name pattern. The index name will be `#{logstash_prefix}-#{formated_date}`
|
134
147
|
|
148
|
+
:warning: Setting this option to `true` will ignore the `index_name` setting. The default index name prefix is `logstash-`.
|
149
|
+
|
135
150
|
### logstash_prefix
|
136
151
|
|
137
152
|
```
|
@@ -185,7 +200,7 @@ For example, should your input plugin not include a `time_key` in the record but
|
|
185
200
|
By default, when inserting records in [Logstash](https://www.elastic.co/products/logstash) format, `@timestamp` is dynamically created with the time at log ingestion. If you'd like to use a custom time, include an `@timestamp` with your record.
|
186
201
|
|
187
202
|
```
|
188
|
-
{"@timestamp":"2014-04-07T000:00:00-00:00"}
|
203
|
+
{"@timestamp": "2014-04-07T000:00:00-00:00"}
|
189
204
|
```
|
190
205
|
|
191
206
|
You can specify an option `time_key` (like the option described in [tail Input Plugin](http://docs.fluentd.org/articles/in_tail)) to replace `@timestamp` key.
|
@@ -348,7 +363,7 @@ This will add the Fluentd tag in the JSON record. For instance, if you have a co
|
|
348
363
|
The record inserted into ElasticSearch would be
|
349
364
|
|
350
365
|
```
|
351
|
-
{"_key":"my.logs", "name":"Johnny Doeie"}
|
366
|
+
{"_key": "my.logs", "name": "Johnny Doeie"}
|
352
367
|
```
|
353
368
|
|
354
369
|
### id_key
|
@@ -359,10 +374,10 @@ id_key request_id # use "request_id" field as a record id in ES
|
|
359
374
|
|
360
375
|
By default, all records inserted into ElasticSearch get a random _id. This option allows to use a field in the record as an identifier.
|
361
376
|
|
362
|
-
This following record `{"name":"Johnny","request_id":"87d89af7daffad6"}` will trigger the following ElasticSearch command
|
377
|
+
This following record `{"name": "Johnny", "request_id": "87d89af7daffad6"}` will trigger the following ElasticSearch command
|
363
378
|
|
364
379
|
```
|
365
|
-
{ "index" : { "_index"
|
380
|
+
{ "index" : { "_index": "logstash-2013.01.01", "_type": "fluentd", "_id": "87d89af7daffad6" } }
|
366
381
|
{ "name": "Johnny", "request_id": "87d89af7daffad6" }
|
367
382
|
```
|
368
383
|
|
@@ -380,7 +395,7 @@ If your input is
|
|
380
395
|
ElasticSearch command would be
|
381
396
|
|
382
397
|
```
|
383
|
-
{ "index" : { "_index"
|
398
|
+
{ "index" : { "_index": "****", "_type": "****", "_id": "****", "_parent": "my_parent" } }
|
384
399
|
{ "name": "Johnny", "a_parent": "my_parent" }
|
385
400
|
```
|
386
401
|
|
@@ -3,7 +3,7 @@ $:.push File.expand_path('../lib', __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = 'fluent-plugin-elasticsearch'
|
6
|
-
s.version = '2.0.0.rc.
|
6
|
+
s.version = '2.0.0.rc.6'
|
7
7
|
s.authors = ['diogo', 'pitr']
|
8
8
|
s.email = ['pitr.vern@gmail.com', 'me@diogoterror.com']
|
9
9
|
s.description = %q{ElasticSearch output plugin for Fluent event collector}
|
@@ -121,6 +121,13 @@ module Fluent::Plugin
|
|
121
121
|
rescue LoadError
|
122
122
|
@dump_proc = Yajl.method(:dump)
|
123
123
|
end
|
124
|
+
|
125
|
+
if @user && m = @user.match(/%{(?<user>.*)}/)
|
126
|
+
@user = URI.encode_www_form_component(m["user"])
|
127
|
+
end
|
128
|
+
if @password && m = @password.match(/%{(?<password>.*)}/)
|
129
|
+
@password = URI.encode_www_form_component(m["password"])
|
130
|
+
end
|
124
131
|
end
|
125
132
|
|
126
133
|
def create_meta_config_map
|
@@ -188,6 +195,18 @@ module Fluent::Plugin
|
|
188
195
|
end
|
189
196
|
end
|
190
197
|
|
198
|
+
def get_escaped_userinfo(host_str)
|
199
|
+
if m = host_str.match(/(?<scheme>.*)%{(?<user>.*)}:%{(?<password>.*)}(?<path>@.*)/)
|
200
|
+
m["scheme"] +
|
201
|
+
URI.encode_www_form_component(m["user"]) +
|
202
|
+
':' +
|
203
|
+
URI.encode_www_form_component(m["password"]) +
|
204
|
+
m["path"]
|
205
|
+
else
|
206
|
+
host_str
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
191
210
|
def get_connection_options
|
192
211
|
raise "`password` must be present if `user` is present" if @user && !@password
|
193
212
|
|
@@ -202,7 +221,7 @@ module Fluent::Plugin
|
|
202
221
|
}
|
203
222
|
else
|
204
223
|
# New hosts format expects URLs such as http://logs.foo.com,https://john:pass@logs2.foo.com/elastic
|
205
|
-
uri = URI(host_str)
|
224
|
+
uri = URI(get_escaped_userinfo(host_str))
|
206
225
|
%w(user password path).inject(host: uri.host, port: uri.port, scheme: uri.scheme) do |hash, key|
|
207
226
|
hash[key.to_sym] = uri.public_send(key) unless uri.public_send(key).nil? || uri.public_send(key) == ''
|
208
227
|
hash
|
@@ -6,6 +6,8 @@ module Fluent::Plugin
|
|
6
6
|
|
7
7
|
Fluent::Plugin.register_output('elasticsearch_dynamic', self)
|
8
8
|
|
9
|
+
helpers :event_emitter
|
10
|
+
|
9
11
|
config_param :delimiter, :string, :default => "."
|
10
12
|
|
11
13
|
DYNAMIC_PARAM_NAMES = %W[hosts host port logstash_format logstash_prefix logstash_dateformat time_key utc_index index_name tag_key type_name id_key parent_key routing_key write_operation]
|
@@ -81,7 +83,7 @@ module Fluent::Plugin
|
|
81
83
|
}
|
82
84
|
else
|
83
85
|
# New hosts format expects URLs such as http://logs.foo.com,https://john:pass@logs2.foo.com/elastic
|
84
|
-
uri = URI(host_str)
|
86
|
+
uri = URI(get_escaped_userinfo(host_str))
|
85
87
|
%w(user password path).inject(host: uri.host, port: uri.port, scheme: uri.scheme) do |hash, key|
|
86
88
|
hash[key.to_sym] = uri.public_send(key) unless uri.public_send(key).nil? || uri.public_send(key) == ''
|
87
89
|
hash
|
@@ -128,17 +130,23 @@ module Fluent::Plugin
|
|
128
130
|
chunk.msgpack_each do |time, record|
|
129
131
|
next unless record.is_a? Hash
|
130
132
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
133
|
+
begin
|
134
|
+
# evaluate all configurations here
|
135
|
+
DYNAMIC_PARAM_SYMBOLS.each_with_index { |var, i|
|
136
|
+
k = DYNAMIC_PARAM_NAMES[i]
|
137
|
+
v = self.instance_variable_get(var)
|
138
|
+
# check here to determine if we should evaluate
|
139
|
+
if dynamic_conf[k] != v
|
140
|
+
value = expand_param(v, tag, time, record)
|
141
|
+
dynamic_conf[k] = value
|
142
|
+
end
|
143
|
+
}
|
141
144
|
# end eval all configs
|
145
|
+
rescue => e
|
146
|
+
# handle dynamic parameters misconfigurations
|
147
|
+
router.emit_error_event(tag, time, record, e)
|
148
|
+
next
|
149
|
+
end
|
142
150
|
|
143
151
|
if eval_or_val(dynamic_conf['logstash_format'])
|
144
152
|
if record.has_key?("@timestamp")
|
@@ -335,6 +335,32 @@ class ElasticsearchOutput < Test::Unit::TestCase
|
|
335
335
|
assert_equal '/default_path', host2[:path]
|
336
336
|
end
|
337
337
|
|
338
|
+
def test_hosts_list_with_escape_placeholders
|
339
|
+
config = %{
|
340
|
+
hosts https://%{j+hn}:%{passw@rd}@host1:443/elastic/,http://host2
|
341
|
+
path /default_path
|
342
|
+
user default_user
|
343
|
+
password default_password
|
344
|
+
}
|
345
|
+
instance = driver(config).instance
|
346
|
+
|
347
|
+
assert_equal 2, instance.get_connection_options[:hosts].length
|
348
|
+
host1, host2 = instance.get_connection_options[:hosts]
|
349
|
+
|
350
|
+
assert_equal 'host1', host1[:host]
|
351
|
+
assert_equal 443, host1[:port]
|
352
|
+
assert_equal 'https', host1[:scheme]
|
353
|
+
assert_equal 'j%2Bhn', host1[:user]
|
354
|
+
assert_equal 'passw%40rd', host1[:password]
|
355
|
+
assert_equal '/elastic/', host1[:path]
|
356
|
+
|
357
|
+
assert_equal 'host2', host2[:host]
|
358
|
+
assert_equal 'http', host2[:scheme]
|
359
|
+
assert_equal 'default_user', host2[:user]
|
360
|
+
assert_equal 'default_password', host2[:password]
|
361
|
+
assert_equal '/default_path', host2[:path]
|
362
|
+
end
|
363
|
+
|
338
364
|
def test_single_host_params_and_defaults
|
339
365
|
config = %{
|
340
366
|
host logs.google.com
|
@@ -354,6 +380,25 @@ class ElasticsearchOutput < Test::Unit::TestCase
|
|
354
380
|
assert_equal nil, host1[:path]
|
355
381
|
end
|
356
382
|
|
383
|
+
def test_single_host_params_and_defaults_with_escape_placeholders
|
384
|
+
config = %{
|
385
|
+
host logs.google.com
|
386
|
+
user %{j+hn}
|
387
|
+
password %{d@e}
|
388
|
+
}
|
389
|
+
instance = driver(config).instance
|
390
|
+
|
391
|
+
assert_equal 1, instance.get_connection_options[:hosts].length
|
392
|
+
host1 = instance.get_connection_options[:hosts][0]
|
393
|
+
|
394
|
+
assert_equal 'logs.google.com', host1[:host]
|
395
|
+
assert_equal 9200, host1[:port]
|
396
|
+
assert_equal 'http', host1[:scheme]
|
397
|
+
assert_equal 'j%2Bhn', host1[:user]
|
398
|
+
assert_equal 'd%40e', host1[:password]
|
399
|
+
assert_equal nil, host1[:path]
|
400
|
+
end
|
401
|
+
|
357
402
|
def test_content_type_header
|
358
403
|
stub_request(:head, "http://localhost:9200/").
|
359
404
|
to_return(:status => 200, :body => "", :headers => {})
|
@@ -1,8 +1,11 @@
|
|
1
1
|
require 'helper'
|
2
2
|
require 'date'
|
3
3
|
require 'fluent/test/driver/output'
|
4
|
+
require 'flexmock/test_unit'
|
4
5
|
|
5
6
|
class ElasticsearchOutputDynamic < Test::Unit::TestCase
|
7
|
+
include FlexMock::TestCase
|
8
|
+
|
6
9
|
attr_accessor :index_cmds, :index_command_counts
|
7
10
|
|
8
11
|
def setup
|
@@ -140,6 +143,32 @@ class ElasticsearchOutputDynamic < Test::Unit::TestCase
|
|
140
143
|
assert_equal '/default_path', host2[:path]
|
141
144
|
end
|
142
145
|
|
146
|
+
def test_hosts_list_with_escape_placeholders
|
147
|
+
config = %{
|
148
|
+
hosts https://%{j+hn}:%{passw@rd}@host1:443/elastic/,http://host2
|
149
|
+
path /default_path
|
150
|
+
user default_user
|
151
|
+
password default_password
|
152
|
+
}
|
153
|
+
instance = driver(config).instance
|
154
|
+
|
155
|
+
assert_equal 2, instance.get_connection_options(nil)[:hosts].length
|
156
|
+
host1, host2 = instance.get_connection_options(nil)[:hosts]
|
157
|
+
|
158
|
+
assert_equal 'host1', host1[:host]
|
159
|
+
assert_equal 443, host1[:port]
|
160
|
+
assert_equal 'https', host1[:scheme]
|
161
|
+
assert_equal 'j%2Bhn', host1[:user]
|
162
|
+
assert_equal 'passw%40rd', host1[:password]
|
163
|
+
assert_equal '/elastic/', host1[:path]
|
164
|
+
|
165
|
+
assert_equal 'host2', host2[:host]
|
166
|
+
assert_equal 'http', host2[:scheme]
|
167
|
+
assert_equal 'default_user', host2[:user]
|
168
|
+
assert_equal 'default_password', host2[:password]
|
169
|
+
assert_equal '/default_path', host2[:path]
|
170
|
+
end
|
171
|
+
|
143
172
|
def test_single_host_params_and_defaults
|
144
173
|
config = %{
|
145
174
|
host logs.google.com
|
@@ -159,6 +188,25 @@ class ElasticsearchOutputDynamic < Test::Unit::TestCase
|
|
159
188
|
assert_equal nil, host1[:path]
|
160
189
|
end
|
161
190
|
|
191
|
+
def test_single_host_params_and_defaults_with_escape_placeholders
|
192
|
+
config = %{
|
193
|
+
host logs.google.com
|
194
|
+
user %{j+hn}
|
195
|
+
password %{d@e}
|
196
|
+
}
|
197
|
+
instance = driver(config).instance
|
198
|
+
|
199
|
+
assert_equal 1, instance.get_connection_options(nil)[:hosts].length
|
200
|
+
host1 = instance.get_connection_options(nil)[:hosts][0]
|
201
|
+
|
202
|
+
assert_equal 'logs.google.com', host1[:host]
|
203
|
+
assert_equal 9200, host1[:port]
|
204
|
+
assert_equal 'http', host1[:scheme]
|
205
|
+
assert_equal 'j%2Bhn', host1[:user]
|
206
|
+
assert_equal 'd%40e', host1[:password]
|
207
|
+
assert_equal nil, host1[:path]
|
208
|
+
end
|
209
|
+
|
162
210
|
def test_content_type_header
|
163
211
|
stub_request(:head, "http://localhost:9200/").
|
164
212
|
to_return(:status => 200, :body => "", :headers => {})
|
@@ -566,6 +614,17 @@ class ElasticsearchOutputDynamic < Test::Unit::TestCase
|
|
566
614
|
}
|
567
615
|
end
|
568
616
|
|
617
|
+
def test_tag_parts_index_error_event
|
618
|
+
stub_elastic_ping
|
619
|
+
stub_elastic
|
620
|
+
driver.configure("logstash_prefix ${tag_parts[1]}\n")
|
621
|
+
flexmock(driver.instance.router).should_receive(:emit_error_event)
|
622
|
+
.with('test', Fluent::EventTime, Hash, TypeError).once
|
623
|
+
driver.run(default_tag: 'test') do
|
624
|
+
driver.feed(sample_record)
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
569
628
|
def test_connection_failed_retry
|
570
629
|
connection_resets = 0
|
571
630
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-elasticsearch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.rc.
|
4
|
+
version: 2.0.0.rc.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- diogo
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-
|
12
|
+
date: 2017-10-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: fluentd
|
@@ -170,7 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
170
170
|
version: 1.3.1
|
171
171
|
requirements: []
|
172
172
|
rubyforge_project:
|
173
|
-
rubygems_version: 2.6.
|
173
|
+
rubygems_version: 2.6.13
|
174
174
|
signing_key:
|
175
175
|
specification_version: 4
|
176
176
|
summary: ElasticSearch output plugin for Fluent event collector
|