fluent-plugin-elasticsearch 0.4.0 → 0.5.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 +4 -4
- data/History.md +8 -2
- data/README.md +22 -2
- data/fluent-plugin-elasticsearch.gemspec +3 -3
- data/lib/fluent/plugin/out_elasticsearch.rb +83 -20
- data/test/plugin/test_out_elasticsearch.rb +103 -3
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52e6ea7f1fcf3e37be32d06f5d81d5364702cbe5
|
4
|
+
data.tar.gz: 66ce94029829ccf119262929ca50e9e21c4d73ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9922d58ba0999c3c03671144b1f4446da3154731e9e5b9dc4bc7dc921d154f382259b2b8b972a21bbcafc35f12265921dadbcba4f592d4fc0f1ebba16566ff5
|
7
|
+
data.tar.gz: d0ec86f473deb9e1a70cca98f3d8194a72f82d08f389246840ae1e25c0dcad9a2c7832b3d54acb6e774652e6c26f50858a9945bca069760f8e972ee01b666515
|
data/History.md
CHANGED
@@ -3,9 +3,16 @@
|
|
3
3
|
### Future
|
4
4
|
|
5
5
|
|
6
|
+
### 0.5.0
|
7
|
+
|
8
|
+
- add full connection URI support (#65)
|
9
|
+
- use `@timestamp` for index (#41)
|
10
|
+
- add support for elasticsearch gem version 1 (#71)
|
11
|
+
- fix connection reset & retry when connection is lost (#67)
|
12
|
+
|
6
13
|
### 0.4.0
|
7
14
|
|
8
|
-
- add `request_timeout` config
|
15
|
+
- add `request_timeout` config (#59)
|
9
16
|
- fix lockup when non-hash values are sent (#52)
|
10
17
|
|
11
18
|
### 0.3.1
|
@@ -40,7 +47,6 @@
|
|
40
47
|
|
41
48
|
- fix timezone in logstash key
|
42
49
|
|
43
|
-
|
44
50
|
### 0.1.0
|
45
51
|
|
46
52
|
- Initial gem release.
|
data/README.md
CHANGED
@@ -25,18 +25,38 @@ index_name fluentd
|
|
25
25
|
type_name fluentd
|
26
26
|
```
|
27
27
|
|
28
|
+
**Index templates**
|
29
|
+
|
30
|
+
This plugin creates ElasticSearch indices by merely writing to them. Consider using [Index Templates](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-templates.html) to gain control of what get indexed and how. See [this example](https://github.com/uken/fluent-plugin-elasticsearch/issues/33#issuecomment-38693282) for a good starting point.
|
31
|
+
|
28
32
|
**More options:**
|
29
33
|
|
30
34
|
```
|
31
35
|
hosts host1:port1,host2:port2,host3:port3
|
32
36
|
```
|
33
37
|
|
38
|
+
or
|
39
|
+
|
40
|
+
```
|
41
|
+
hosts https://customhost.com:443/path,https://username:password@host-failover.com:443
|
42
|
+
```
|
43
|
+
|
34
44
|
You can specify multiple elasticsearch hosts with separator ",".
|
35
45
|
|
36
|
-
If you specify multiple hosts, plugin
|
46
|
+
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.
|
37
47
|
|
38
48
|
If you specify this option, host and port options are ignored.
|
39
49
|
|
50
|
+
```
|
51
|
+
user demo
|
52
|
+
password secret
|
53
|
+
path /elastic_search/
|
54
|
+
scheme https
|
55
|
+
```
|
56
|
+
|
57
|
+
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.
|
58
|
+
|
59
|
+
|
40
60
|
```
|
41
61
|
logstash_format true # defaults to false
|
42
62
|
```
|
@@ -53,7 +73,7 @@ By default, the records inserted into index `logstash-YYMMDD`. This option allow
|
|
53
73
|
logstash_dateformat %Y.%m. # defaults to "%Y.%m.%d"
|
54
74
|
```
|
55
75
|
|
56
|
-
By default, when inserting records in 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.
|
76
|
+
By default, when inserting records in 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.
|
57
77
|
|
58
78
|
```
|
59
79
|
{"@timestamp":"2014-04-07T000:00:00-00:00"}
|
@@ -3,9 +3,9 @@ $:.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 = '0.
|
6
|
+
s.version = '0.5.0'
|
7
7
|
s.authors = ['diogo', 'pitr']
|
8
|
-
s.email = ['
|
8
|
+
s.email = ['pitr@uken.com', 'diogo@uken.com']
|
9
9
|
s.description = %q{ElasticSearch output plugin for Fluent event collector}
|
10
10
|
s.summary = s.description
|
11
11
|
s.homepage = 'https://github.com/uken/fluent-plugin-elasticsearch'
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
|
19
19
|
s.add_runtime_dependency 'fluentd', '~> 0'
|
20
20
|
s.add_runtime_dependency 'patron', '~> 0'
|
21
|
-
s.add_runtime_dependency 'elasticsearch', '
|
21
|
+
s.add_runtime_dependency 'elasticsearch', '>= 0'
|
22
22
|
|
23
23
|
s.add_development_dependency 'rake', '~> 0'
|
24
24
|
s.add_development_dependency 'webmock', '~> 1'
|
@@ -2,12 +2,20 @@
|
|
2
2
|
require 'date'
|
3
3
|
require 'patron'
|
4
4
|
require 'elasticsearch'
|
5
|
+
require 'uri'
|
5
6
|
|
6
7
|
class Fluent::ElasticsearchOutput < Fluent::BufferedOutput
|
8
|
+
class ConnectionFailure < StandardError; end
|
9
|
+
|
7
10
|
Fluent::Plugin.register_output('elasticsearch', self)
|
8
11
|
|
9
12
|
config_param :host, :string, :default => 'localhost'
|
10
13
|
config_param :port, :integer, :default => 9200
|
14
|
+
config_param :user, :string, :default => nil
|
15
|
+
config_param :password, :string, :default => nil
|
16
|
+
config_param :path, :string, :default => nil
|
17
|
+
config_param :scheme, :string, :default => 'http'
|
18
|
+
config_param :hosts, :string, :default => nil
|
11
19
|
config_param :logstash_format, :bool, :default => false
|
12
20
|
config_param :logstash_prefix, :string, :default => "logstash"
|
13
21
|
config_param :logstash_dateformat, :string, :default => "%Y.%m.%d"
|
@@ -16,7 +24,6 @@ class Fluent::ElasticsearchOutput < Fluent::BufferedOutput
|
|
16
24
|
config_param :index_name, :string, :default => "fluentd"
|
17
25
|
config_param :id_key, :string, :default => nil
|
18
26
|
config_param :parent_key, :string, :default => nil
|
19
|
-
config_param :hosts, :string, :default => nil
|
20
27
|
config_param :request_timeout, :time, :default => 5
|
21
28
|
|
22
29
|
include Fluent::SetTagKeyMixin
|
@@ -37,26 +44,66 @@ class Fluent::ElasticsearchOutput < Fluent::BufferedOutput
|
|
37
44
|
def client
|
38
45
|
@_es ||= begin
|
39
46
|
adapter_conf = lambda {|f| f.adapter :patron }
|
40
|
-
transport = Elasticsearch::Transport::Transport::HTTP::Faraday.new(
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
}
|
48
|
-
Elasticsearch::Client.new transport: transport
|
47
|
+
transport = Elasticsearch::Transport::Transport::HTTP::Faraday.new(get_connection_options.merge(
|
48
|
+
options: {
|
49
|
+
reload_connections: true,
|
50
|
+
retry_on_failure: 5,
|
51
|
+
transport_options: {
|
52
|
+
request: { timeout: @request_timeout }
|
53
|
+
}
|
54
|
+
}), &adapter_conf)
|
55
|
+
es = Elasticsearch::Client.new transport: transport
|
56
|
+
|
57
|
+
begin
|
58
|
+
raise ConnectionFailure, "Can not reach Elasticsearch cluster (#{connection_options_description})!" unless es.ping
|
59
|
+
rescue Faraday::ConnectionFailed => e
|
60
|
+
raise ConnectionFailure, "Can not reach Elasticsearch cluster (#{connection_options_description})! #{e.message}"
|
61
|
+
end
|
62
|
+
|
63
|
+
log.info "Connection opened to Elasticsearch cluster => #{connection_options_description}"
|
64
|
+
es
|
49
65
|
end
|
50
|
-
raise "Can not reach Elasticsearch cluster (#{@host}:#{@port})!" unless @_es.ping
|
51
|
-
@_es
|
52
66
|
end
|
53
67
|
|
54
|
-
def
|
55
|
-
if @
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
68
|
+
def get_connection_options
|
69
|
+
raise "`password` must be present if `user` is present" if @user && !@password
|
70
|
+
|
71
|
+
hosts = if @hosts
|
72
|
+
@hosts.split(',').map do |host_str|
|
73
|
+
# Support legacy hosts format host:port,host:port,host:port...
|
74
|
+
if host_str.match(%r{^[^:]+\:\d+$})
|
75
|
+
{
|
76
|
+
host: host_str.split(':')[0],
|
77
|
+
port: (host_str.split(':')[1] || @port).to_i,
|
78
|
+
scheme: @scheme
|
79
|
+
}
|
80
|
+
else
|
81
|
+
# New hosts format expects URLs such as http://logs.foo.com,https://john:pass@logs2.foo.com/elastic
|
82
|
+
uri = URI(host_str)
|
83
|
+
%w(user password path).inject(host: uri.host, port: uri.port, scheme: uri.scheme) do |hash, key|
|
84
|
+
hash[key.to_sym] = uri.public_send(key) unless uri.public_send(key).nil? || uri.public_send(key) == ''
|
85
|
+
hash
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end.compact
|
89
|
+
else
|
90
|
+
[{host: @host, port: @port, scheme: @scheme}]
|
91
|
+
end.each do |host|
|
92
|
+
host.merge!(user: @user, password: @password) if !host[:user] && @user
|
93
|
+
host.merge!(path: @path) if !host[:path] && @path
|
94
|
+
end
|
95
|
+
|
96
|
+
{
|
97
|
+
hosts: hosts
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def connection_options_description
|
102
|
+
get_connection_options[:hosts].map do |host_info|
|
103
|
+
attributes = host_info.dup
|
104
|
+
attributes[:password] = 'obfuscated' if attributes.has_key?(:password)
|
105
|
+
attributes.inspect
|
106
|
+
end.join(', ')
|
60
107
|
end
|
61
108
|
|
62
109
|
def format(tag, time, record)
|
@@ -73,7 +120,11 @@ class Fluent::ElasticsearchOutput < Fluent::BufferedOutput
|
|
73
120
|
chunk.msgpack_each do |tag, time, record|
|
74
121
|
next unless record.is_a? Hash
|
75
122
|
if @logstash_format
|
76
|
-
|
123
|
+
if record.has_key?("@timestamp")
|
124
|
+
time = Time.parse record["@timestamp"]
|
125
|
+
else
|
126
|
+
record.merge!({"@timestamp" => Time.at(time).to_datetime.to_s})
|
127
|
+
end
|
77
128
|
if @utc_index
|
78
129
|
target_index = "#{@logstash_prefix}-#{Time.at(time).getutc.strftime("#{@logstash_dateformat}")}"
|
79
130
|
else
|
@@ -105,6 +156,18 @@ class Fluent::ElasticsearchOutput < Fluent::BufferedOutput
|
|
105
156
|
end
|
106
157
|
|
107
158
|
def send(data)
|
108
|
-
|
159
|
+
retries = 0
|
160
|
+
begin
|
161
|
+
client.bulk body: data
|
162
|
+
rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
|
163
|
+
if retries < 2
|
164
|
+
retries += 1
|
165
|
+
@_es = nil
|
166
|
+
log.warn "Could not push logs to Elasticsearch, resetting connection and trying again. #{e.message}"
|
167
|
+
sleep 2**retries
|
168
|
+
retry
|
169
|
+
end
|
170
|
+
raise ConnectionFailure, "Could not push logs to Elasticsearch after #{retries} retries. #{e.message}"
|
171
|
+
end
|
109
172
|
end
|
110
173
|
end
|
@@ -6,11 +6,11 @@ require 'fluent/plugin/out_elasticsearch'
|
|
6
6
|
require 'webmock/test_unit'
|
7
7
|
require 'date'
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
$:.push File.expand_path("../lib", __FILE__)
|
9
|
+
$:.push File.expand_path("../..", __FILE__)
|
12
10
|
$:.push File.dirname(__FILE__)
|
13
11
|
|
12
|
+
require 'helper'
|
13
|
+
|
14
14
|
WebMock.disable_net_connect!
|
15
15
|
|
16
16
|
class ElasticsearchOutput < Test::Unit::TestCase
|
@@ -55,6 +55,87 @@ class ElasticsearchOutput < Test::Unit::TestCase
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
def test_configure
|
59
|
+
config = %{
|
60
|
+
host logs.google.com
|
61
|
+
port 777
|
62
|
+
scheme https
|
63
|
+
path /es/
|
64
|
+
user john
|
65
|
+
password doe
|
66
|
+
}
|
67
|
+
instance = driver('test', config).instance
|
68
|
+
|
69
|
+
assert_equal 'logs.google.com', instance.host
|
70
|
+
assert_equal 777, instance.port
|
71
|
+
assert_equal 'https', instance.scheme
|
72
|
+
assert_equal '/es/', instance.path
|
73
|
+
assert_equal 'john', instance.user
|
74
|
+
assert_equal 'doe', instance.password
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_legacy_hosts_list
|
78
|
+
config = %{
|
79
|
+
hosts host1:50,host2:100
|
80
|
+
scheme https
|
81
|
+
path /es/
|
82
|
+
}
|
83
|
+
instance = driver('test', config).instance
|
84
|
+
|
85
|
+
assert_equal 2, instance.get_connection_options[:hosts].length
|
86
|
+
host1, host2 = instance.get_connection_options[:hosts]
|
87
|
+
|
88
|
+
assert_equal 'host1', host1[:host]
|
89
|
+
assert_equal 50, host1[:port]
|
90
|
+
assert_equal 'https', host1[:scheme]
|
91
|
+
assert_equal '/es/', host2[:path]
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_hosts_list
|
95
|
+
config = %{
|
96
|
+
hosts https://john:password@host1:443/elastic/,http://host2
|
97
|
+
path /default_path
|
98
|
+
user default_user
|
99
|
+
password default_password
|
100
|
+
}
|
101
|
+
instance = driver('test', config).instance
|
102
|
+
|
103
|
+
assert_equal 2, instance.get_connection_options[:hosts].length
|
104
|
+
host1, host2 = instance.get_connection_options[:hosts]
|
105
|
+
|
106
|
+
assert_equal 'host1', host1[:host]
|
107
|
+
assert_equal 443, host1[:port]
|
108
|
+
assert_equal 'https', host1[:scheme]
|
109
|
+
assert_equal 'john', host1[:user]
|
110
|
+
assert_equal 'password', host1[:password]
|
111
|
+
assert_equal '/elastic/', host1[:path]
|
112
|
+
|
113
|
+
assert_equal 'host2', host2[:host]
|
114
|
+
assert_equal 'http', host2[:scheme]
|
115
|
+
assert_equal 'default_user', host2[:user]
|
116
|
+
assert_equal 'default_password', host2[:password]
|
117
|
+
assert_equal '/default_path', host2[:path]
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_single_host_params_and_defaults
|
121
|
+
config = %{
|
122
|
+
host logs.google.com
|
123
|
+
user john
|
124
|
+
password doe
|
125
|
+
}
|
126
|
+
instance = driver('test', config).instance
|
127
|
+
|
128
|
+
assert_equal 1, instance.get_connection_options[:hosts].length
|
129
|
+
host1 = instance.get_connection_options[:hosts][0]
|
130
|
+
|
131
|
+
assert_equal 'logs.google.com', host1[:host]
|
132
|
+
assert_equal 9200, host1[:port]
|
133
|
+
assert_equal 'http', host1[:scheme]
|
134
|
+
assert_equal 'john', host1[:user]
|
135
|
+
assert_equal 'doe', host1[:password]
|
136
|
+
assert_equal nil, host1[:path]
|
137
|
+
end
|
138
|
+
|
58
139
|
def test_writes_to_default_index
|
59
140
|
stub_elastic_ping
|
60
141
|
stub_elastic
|
@@ -332,4 +413,23 @@ class ElasticsearchOutput < Test::Unit::TestCase
|
|
332
413
|
driver.emit("some garbage string")
|
333
414
|
driver.run
|
334
415
|
end
|
416
|
+
|
417
|
+
def test_connection_failed_retry
|
418
|
+
connection_resets = 0
|
419
|
+
|
420
|
+
stub_elastic_ping(url="http://localhost:9200").with do |req|
|
421
|
+
connection_resets += 1
|
422
|
+
end
|
423
|
+
|
424
|
+
stub_request(:post, "http://localhost:9200/_bulk").with do |req|
|
425
|
+
raise Faraday::ConnectionFailed, "Test message"
|
426
|
+
end
|
427
|
+
|
428
|
+
driver.emit(sample_record)
|
429
|
+
|
430
|
+
assert_raise(Fluent::ElasticsearchOutput::ConnectionFailure) {
|
431
|
+
driver.run
|
432
|
+
}
|
433
|
+
assert_equal(connection_resets, 3)
|
434
|
+
end
|
335
435
|
end
|
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: 0.
|
4
|
+
version: 0.5.0
|
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: 2014-
|
12
|
+
date: 2014-10-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: fluentd
|
@@ -43,14 +43,14 @@ dependencies:
|
|
43
43
|
name: elasticsearch
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- - "
|
46
|
+
- - ">="
|
47
47
|
- !ruby/object:Gem::Version
|
48
48
|
version: '0'
|
49
49
|
type: :runtime
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- - "
|
53
|
+
- - ">="
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '0'
|
56
56
|
- !ruby/object:Gem::Dependency
|
@@ -83,7 +83,8 @@ dependencies:
|
|
83
83
|
version: '1'
|
84
84
|
description: ElasticSearch output plugin for Fluent event collector
|
85
85
|
email:
|
86
|
-
-
|
86
|
+
- pitr@uken.com
|
87
|
+
- diogo@uken.com
|
87
88
|
executables: []
|
88
89
|
extensions: []
|
89
90
|
extra_rdoc_files: []
|
@@ -119,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
120
|
version: '0'
|
120
121
|
requirements: []
|
121
122
|
rubyforge_project:
|
122
|
-
rubygems_version: 2.2.
|
123
|
+
rubygems_version: 2.2.2
|
123
124
|
signing_key:
|
124
125
|
specification_version: 4
|
125
126
|
summary: ElasticSearch output plugin for Fluent event collector
|