fluent-plugin-elasticsearch 2.0.1 → 2.1.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 +3 -0
- data/fluent-plugin-elasticsearch.gemspec +1 -1
- data/lib/fluent/plugin/elasticsearch_constants.rb +13 -0
- data/lib/fluent/plugin/elasticsearch_error_handler.rb +96 -0
- data/lib/fluent/plugin/out_elasticsearch.rb +7 -8
- data/test/plugin/test_out_elasticsearch.rb +231 -0
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 16f9f3e664e714be9d1e7fee4b3f8a1683ec9728
|
4
|
+
data.tar.gz: beb9fa49cb58739036a3f2c1cda6ee936fbd873d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8527ebae2dbdb312cc6ed32af668077f9560b74db612a4f5d381f758e4e9cb4aa6230a6859a4a72c3f2b944b1fbf07292a49804cd4648a81392259938f133bab
|
7
|
+
data.tar.gz: 887830c553b5ed0350bf64aba4f38d24c28c557fad5493c3ae5f350049269737645e93addfac89b0c252898d07cc127707a9d35f23470ea45cd35f5df48839e0
|
data/History.md
CHANGED
@@ -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
|
6
|
+
s.version = '2.1.0'
|
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}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Fluent
|
2
|
+
module Plugin
|
3
|
+
module ElasticsearchConstants
|
4
|
+
BODY_DELIMITER = "\n".freeze
|
5
|
+
UPDATE_OP = "update".freeze
|
6
|
+
UPSERT_OP = "upsert".freeze
|
7
|
+
CREATE_OP = "create".freeze
|
8
|
+
INDEX_OP = "index".freeze
|
9
|
+
ID_FIELD = "_id".freeze
|
10
|
+
TIMESTAMP_FIELD = "@timestamp".freeze
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require_relative 'elasticsearch_constants'
|
2
|
+
|
3
|
+
class Fluent::Plugin::ElasticsearchErrorHandler
|
4
|
+
include Fluent::Plugin::ElasticsearchConstants
|
5
|
+
|
6
|
+
attr_accessor :records, :bulk_message_count
|
7
|
+
class BulkIndexQueueFull < StandardError; end
|
8
|
+
class ElasticsearchOutOfMemory < StandardError; end
|
9
|
+
class ElasticsearchVersionMismatch < StandardError; end
|
10
|
+
class UnrecognizedElasticsearchError < StandardError; end
|
11
|
+
class ElasticsearchError < StandardError; end
|
12
|
+
def initialize(plugin, records = 0, bulk_message_count = 0)
|
13
|
+
@plugin = plugin
|
14
|
+
@records = records
|
15
|
+
@bulk_message_count = bulk_message_count
|
16
|
+
end
|
17
|
+
|
18
|
+
def handle_error(response)
|
19
|
+
errors = Hash.new(0)
|
20
|
+
errors_bad_resp = 0
|
21
|
+
errors_unrecognized = 0
|
22
|
+
successes = 0
|
23
|
+
duplicates = 0
|
24
|
+
bad_arguments = 0
|
25
|
+
response['items'].each do |item|
|
26
|
+
if item.has_key?(@plugin.write_operation)
|
27
|
+
write_operation = @plugin.write_operation
|
28
|
+
elsif INDEX_OP == @plugin.write_operation && item.has_key?(CREATE_OP)
|
29
|
+
write_operation = CREATE_OP
|
30
|
+
else
|
31
|
+
# When we don't have an expected ops field, something changed in the API
|
32
|
+
# expected return values (ES 2.x)
|
33
|
+
errors_bad_resp += 1
|
34
|
+
next
|
35
|
+
end
|
36
|
+
if item[write_operation].has_key?('status')
|
37
|
+
status = item[write_operation]['status']
|
38
|
+
else
|
39
|
+
# When we don't have a status field, something changed in the API
|
40
|
+
# expected return values (ES 2.x)
|
41
|
+
errors_bad_resp += 1
|
42
|
+
next
|
43
|
+
end
|
44
|
+
case
|
45
|
+
when CREATE_OP == write_operation && 409 == status
|
46
|
+
duplicates += 1
|
47
|
+
when 400 == status
|
48
|
+
bad_arguments += 1
|
49
|
+
@plugin.log.debug "Elasticsearch rejected document: #{item}"
|
50
|
+
when [429, 500].include?(status)
|
51
|
+
if item[write_operation].has_key?('error') && item[write_operation]['error'].has_key?('type')
|
52
|
+
type = item[write_operation]['error']['type']
|
53
|
+
else
|
54
|
+
# When we don't have a type field, something changed in the API
|
55
|
+
# expected return values (ES 2.x)
|
56
|
+
errors_bad_resp += 1
|
57
|
+
next
|
58
|
+
end
|
59
|
+
errors[type] += 1
|
60
|
+
when [200, 201].include?(status)
|
61
|
+
successes += 1
|
62
|
+
else
|
63
|
+
errors_unrecognized += 1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
if errors_bad_resp > 0
|
67
|
+
msg = "Unable to parse error response from Elasticsearch, likely an API version mismatch #{response}"
|
68
|
+
@plugin.log.error msg
|
69
|
+
raise ElasticsearchVersionMismatch, msg
|
70
|
+
end
|
71
|
+
if bad_arguments > 0
|
72
|
+
@plugin.log.warn "Elasticsearch rejected #{bad_arguments} documents due to invalid field arguments"
|
73
|
+
end
|
74
|
+
if duplicates > 0
|
75
|
+
@plugin.log.info "Encountered #{duplicates} duplicate(s) of #{successes} indexing chunk, ignoring"
|
76
|
+
end
|
77
|
+
msg = "Indexed (op = #{@plugin.write_operation}) #{successes} successfully, #{duplicates} duplicate(s), #{bad_arguments} bad argument(s), #{errors_unrecognized} unrecognized error(s)"
|
78
|
+
errors.each_key do |key|
|
79
|
+
msg << ", #{errors[key]} #{key} error(s)"
|
80
|
+
end
|
81
|
+
@plugin.log.debug msg
|
82
|
+
if errors_unrecognized > 0
|
83
|
+
raise UnrecognizedElasticsearchError, "Unrecognized elasticsearch errors returned, retrying #{response}"
|
84
|
+
end
|
85
|
+
errors.each_key do |key|
|
86
|
+
case key
|
87
|
+
when 'out_of_memory_error'
|
88
|
+
raise ElasticsearchOutOfMemory, "Elasticsearch has exhausted its heap, retrying"
|
89
|
+
when 'es_rejected_execution_exception'
|
90
|
+
raise BulkIndexQueueFull, "Bulk index queue is full, retrying"
|
91
|
+
else
|
92
|
+
raise ElasticsearchError, "Elasticsearch errors returned, retrying #{response}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -10,6 +10,8 @@ rescue LoadError
|
|
10
10
|
end
|
11
11
|
|
12
12
|
require 'fluent/plugin/output'
|
13
|
+
require_relative 'elasticsearch_constants'
|
14
|
+
require_relative 'elasticsearch_error_handler'
|
13
15
|
require_relative 'elasticsearch_index_template'
|
14
16
|
require_relative 'generate_hash_id_support'
|
15
17
|
|
@@ -81,6 +83,7 @@ module Fluent::Plugin
|
|
81
83
|
|
82
84
|
include Fluent::ElasticsearchIndexTemplate
|
83
85
|
include Fluent::Plugin::GenerateHashIdSupport
|
86
|
+
include Fluent::Plugin::ElasticsearchConstants
|
84
87
|
|
85
88
|
def initialize
|
86
89
|
super
|
@@ -251,14 +254,6 @@ module Fluent::Plugin
|
|
251
254
|
end.join(', ')
|
252
255
|
end
|
253
256
|
|
254
|
-
BODY_DELIMITER = "\n".freeze
|
255
|
-
UPDATE_OP = "update".freeze
|
256
|
-
UPSERT_OP = "upsert".freeze
|
257
|
-
CREATE_OP = "create".freeze
|
258
|
-
INDEX_OP = "index".freeze
|
259
|
-
ID_FIELD = "_id".freeze
|
260
|
-
TIMESTAMP_FIELD = "@timestamp".freeze
|
261
|
-
|
262
257
|
def append_record_to_messages(op, meta, header, record, msgs)
|
263
258
|
case op
|
264
259
|
when UPDATE_OP, UPSERT_OP
|
@@ -334,8 +329,10 @@ module Fluent::Plugin
|
|
334
329
|
|
335
330
|
tag = chunk.metadata.tag
|
336
331
|
logstash_prefix, index_name = expand_placeholders(chunk.metadata)
|
332
|
+
@error = Fluent::Plugin::ElasticsearchErrorHandler.new(self)
|
337
333
|
|
338
334
|
chunk.msgpack_each do |time, record|
|
335
|
+
@error.records += 1
|
339
336
|
next unless record.is_a? Hash
|
340
337
|
|
341
338
|
if @flatten_hashes
|
@@ -402,6 +399,7 @@ module Fluent::Plugin
|
|
402
399
|
end
|
403
400
|
|
404
401
|
append_record_to_messages(@write_operation, meta, header, record, bulk_message)
|
402
|
+
@error.bulk_message_count += 1
|
405
403
|
end
|
406
404
|
|
407
405
|
send_bulk(bulk_message) unless bulk_message.empty?
|
@@ -420,6 +418,7 @@ module Fluent::Plugin
|
|
420
418
|
begin
|
421
419
|
response = client.bulk body: data
|
422
420
|
if response['errors']
|
421
|
+
@error.handle_error(response)
|
423
422
|
log.error "Could not push log to Elasticsearch: #{response}"
|
424
423
|
end
|
425
424
|
rescue *client.transport.host_unreachable_exceptions => e
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'helper'
|
2
2
|
require 'date'
|
3
3
|
require 'fluent/test/helpers'
|
4
|
+
require 'json'
|
4
5
|
require 'fluent/test/driver/output'
|
5
6
|
require 'flexmock/test_unit'
|
6
7
|
|
@@ -59,6 +60,130 @@ class ElasticsearchOutput < Test::Unit::TestCase
|
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
63
|
+
def make_response_body(req, error_el = nil, error_status = nil, error = nil)
|
64
|
+
req_index_cmds = req.body.split("\n").map { |r| JSON.parse(r) }
|
65
|
+
items = []
|
66
|
+
count = 0
|
67
|
+
ids = 1
|
68
|
+
op = nil
|
69
|
+
index = nil
|
70
|
+
type = nil
|
71
|
+
id = nil
|
72
|
+
req_index_cmds.each do |cmd|
|
73
|
+
if count.even?
|
74
|
+
op = cmd.keys[0]
|
75
|
+
index = cmd[op]['_index']
|
76
|
+
type = cmd[op]['_type']
|
77
|
+
if cmd[op].has_key?('_id')
|
78
|
+
id = cmd[op]['_id']
|
79
|
+
else
|
80
|
+
# Note: this appears to be an undocumented feature of Elasticsearch
|
81
|
+
# https://www.elastic.co/guide/en/elasticsearch/reference/2.4/docs-bulk.html
|
82
|
+
# When you submit an "index" write_operation, with no "_id" field in the
|
83
|
+
# metadata header, Elasticsearch will turn this into a "create"
|
84
|
+
# operation in the response.
|
85
|
+
if "index" == op
|
86
|
+
op = "create"
|
87
|
+
end
|
88
|
+
id = ids
|
89
|
+
ids += 1
|
90
|
+
end
|
91
|
+
else
|
92
|
+
item = {
|
93
|
+
op => {
|
94
|
+
'_index' => index, '_type' => type, '_id' => id, '_version' => 1,
|
95
|
+
'_shards' => { 'total' => 1, 'successful' => 1, 'failed' => 0 },
|
96
|
+
'status' => op == 'create' ? 201 : 200
|
97
|
+
}
|
98
|
+
}
|
99
|
+
items.push(item)
|
100
|
+
end
|
101
|
+
count += 1
|
102
|
+
end
|
103
|
+
if !error_el.nil? && !error_status.nil? && !error.nil?
|
104
|
+
op = items[error_el].keys[0]
|
105
|
+
items[error_el][op].delete('_version')
|
106
|
+
items[error_el][op].delete('_shards')
|
107
|
+
items[error_el][op]['error'] = error
|
108
|
+
items[error_el][op]['status'] = error_status
|
109
|
+
errors = true
|
110
|
+
else
|
111
|
+
errors = false
|
112
|
+
end
|
113
|
+
@index_cmds = items
|
114
|
+
body = { 'took' => 6, 'errors' => errors, 'items' => items }
|
115
|
+
return body.to_json
|
116
|
+
end
|
117
|
+
|
118
|
+
def stub_elastic_bad_argument(url="http://localhost:9200/_bulk")
|
119
|
+
error = {
|
120
|
+
"type" => "mapper_parsing_exception",
|
121
|
+
"reason" => "failed to parse [...]",
|
122
|
+
"caused_by" => {
|
123
|
+
"type" => "illegal_argument_exception",
|
124
|
+
"reason" => "Invalid format: \"...\""
|
125
|
+
}
|
126
|
+
}
|
127
|
+
stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 400, error), :headers => { 'Content-Type' => 'json' } } })
|
128
|
+
end
|
129
|
+
|
130
|
+
def stub_elastic_bulk_error(url="http://localhost:9200/_bulk")
|
131
|
+
error = {
|
132
|
+
"type" => "some-unrecognized-error",
|
133
|
+
"reason" => "some message printed here ...",
|
134
|
+
}
|
135
|
+
stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 500, error), :headers => { 'Content-Type' => 'json' } } })
|
136
|
+
end
|
137
|
+
|
138
|
+
def stub_elastic_bulk_rejected(url="http://localhost:9200/_bulk")
|
139
|
+
error = {
|
140
|
+
"type" => "es_rejected_execution_exception",
|
141
|
+
"reason" => "rejected execution of org.elasticsearch.transport.TransportService$4@1a34d37a on EsThreadPoolExecutor[bulk, queue capacity = 50, org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor@312a2162[Running, pool size = 32, active threads = 32, queued tasks = 50, completed tasks = 327053]]"
|
142
|
+
}
|
143
|
+
stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 429, error), :headers => { 'Content-Type' => 'json' } } })
|
144
|
+
end
|
145
|
+
|
146
|
+
def stub_elastic_out_of_memory(url="http://localhost:9200/_bulk")
|
147
|
+
error = {
|
148
|
+
"type" => "out_of_memory_error",
|
149
|
+
"reason" => "Java heap space"
|
150
|
+
}
|
151
|
+
stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 500, error), :headers => { 'Content-Type' => 'json' } } })
|
152
|
+
end
|
153
|
+
|
154
|
+
def stub_elastic_unrecognized_error(url="http://localhost:9200/_bulk")
|
155
|
+
error = {
|
156
|
+
"type" => "some-other-type",
|
157
|
+
"reason" => "some-other-reason"
|
158
|
+
}
|
159
|
+
stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 504, error), :headers => { 'Content-Type' => 'json' } } })
|
160
|
+
end
|
161
|
+
|
162
|
+
def stub_elastic_version_mismatch(url="http://localhost:9200/_bulk")
|
163
|
+
error = {
|
164
|
+
"category" => "some-other-type",
|
165
|
+
"reason" => "some-other-reason"
|
166
|
+
}
|
167
|
+
stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 1, 500, error), :headers => { 'Content-Type' => 'json' } } })
|
168
|
+
end
|
169
|
+
|
170
|
+
def stub_elastic_index_to_create(url="http://localhost:9200/_bulk")
|
171
|
+
error = {
|
172
|
+
"category" => "some-other-type",
|
173
|
+
"reason" => "some-other-reason",
|
174
|
+
"type" => "some-other-type"
|
175
|
+
}
|
176
|
+
stub_request(:post, url).to_return(lambda { |req| { :status => 200, :body => make_response_body(req, 0, 500, error), :headers => { 'Content-Type' => 'json' } } })
|
177
|
+
end
|
178
|
+
|
179
|
+
def stub_elastic_unexpected_response_op(url="http://localhost:9200/_bulk")
|
180
|
+
error = {
|
181
|
+
"category" => "some-other-type",
|
182
|
+
"reason" => "some-other-reason"
|
183
|
+
}
|
184
|
+
stub_request(:post, url).to_return(lambda { |req| bodystr = make_response_body(req, 0, 500, error); body = JSON.parse(bodystr); body['items'][0]['unknown'] = body['items'][0].delete('create'); { :status => 200, :body => body.to_json, :headers => { 'Content-Type' => 'json' } } })
|
185
|
+
end
|
186
|
+
|
62
187
|
def test_configure
|
63
188
|
config = %{
|
64
189
|
host logs.google.com
|
@@ -1394,6 +1519,112 @@ class ElasticsearchOutput < Test::Unit::TestCase
|
|
1394
1519
|
assert_equal(connection_resets, 1)
|
1395
1520
|
end
|
1396
1521
|
|
1522
|
+
def test_bulk_bad_arguments
|
1523
|
+
driver = driver('@log_level debug')
|
1524
|
+
|
1525
|
+
stub_elastic_ping
|
1526
|
+
stub_elastic_bad_argument
|
1527
|
+
|
1528
|
+
driver.run(default_tag: 'test', shutdown: false) do
|
1529
|
+
driver.feed(sample_record)
|
1530
|
+
driver.feed(sample_record)
|
1531
|
+
driver.feed(sample_record)
|
1532
|
+
end
|
1533
|
+
|
1534
|
+
matches = driver.logs.grep /Elasticsearch rejected document:/
|
1535
|
+
assert_equal(1, matches.length, "Message 'Elasticsearch rejected document: ...' was not emitted")
|
1536
|
+
matches = driver.logs.grep /documents due to invalid field arguments/
|
1537
|
+
assert_equal(1, matches.length, "Message 'Elasticsearch rejected # documents due to invalid field arguments ...' was not emitted")
|
1538
|
+
end
|
1539
|
+
|
1540
|
+
def test_bulk_error
|
1541
|
+
stub_elastic_ping
|
1542
|
+
stub_elastic_bulk_error
|
1543
|
+
|
1544
|
+
assert_raise(Fluent::Plugin::ElasticsearchErrorHandler::ElasticsearchError) {
|
1545
|
+
driver.run(default_tag: 'test', shutdown: false) do
|
1546
|
+
driver.feed(sample_record)
|
1547
|
+
driver.feed(sample_record)
|
1548
|
+
driver.feed(sample_record)
|
1549
|
+
end
|
1550
|
+
}
|
1551
|
+
end
|
1552
|
+
|
1553
|
+
def test_bulk_error_version_mismatch
|
1554
|
+
stub_elastic_ping
|
1555
|
+
stub_elastic_version_mismatch
|
1556
|
+
|
1557
|
+
assert_raise(Fluent::Plugin::ElasticsearchErrorHandler::ElasticsearchVersionMismatch) {
|
1558
|
+
driver.run(default_tag: 'test', shutdown: false) do
|
1559
|
+
driver.feed(sample_record)
|
1560
|
+
driver.feed(sample_record)
|
1561
|
+
driver.feed(sample_record)
|
1562
|
+
end
|
1563
|
+
}
|
1564
|
+
end
|
1565
|
+
|
1566
|
+
def test_bulk_error_unrecognized_error
|
1567
|
+
stub_elastic_ping
|
1568
|
+
stub_elastic_unrecognized_error
|
1569
|
+
|
1570
|
+
assert_raise(Fluent::Plugin::ElasticsearchErrorHandler::UnrecognizedElasticsearchError) {
|
1571
|
+
driver.run(default_tag: 'test', shutdown: false) do
|
1572
|
+
driver.feed(sample_record)
|
1573
|
+
driver.feed(sample_record)
|
1574
|
+
driver.feed(sample_record)
|
1575
|
+
end
|
1576
|
+
}
|
1577
|
+
end
|
1578
|
+
|
1579
|
+
def test_bulk_error_out_of_memory
|
1580
|
+
stub_elastic_ping
|
1581
|
+
stub_elastic_out_of_memory
|
1582
|
+
|
1583
|
+
assert_raise(Fluent::Plugin::ElasticsearchErrorHandler::ElasticsearchOutOfMemory) {
|
1584
|
+
driver.run(default_tag: 'test', shutdown: false) do
|
1585
|
+
driver.feed(sample_record)
|
1586
|
+
driver.feed(sample_record)
|
1587
|
+
driver.feed(sample_record)
|
1588
|
+
end
|
1589
|
+
}
|
1590
|
+
end
|
1591
|
+
|
1592
|
+
def test_bulk_error_queue_full
|
1593
|
+
stub_elastic_ping
|
1594
|
+
stub_elastic_bulk_rejected
|
1595
|
+
|
1596
|
+
assert_raise(Fluent::Plugin::ElasticsearchErrorHandler::BulkIndexQueueFull) {
|
1597
|
+
driver.run(default_tag: 'test', shutdown: false) do
|
1598
|
+
driver.feed(sample_record)
|
1599
|
+
driver.feed(sample_record)
|
1600
|
+
driver.feed(sample_record)
|
1601
|
+
end
|
1602
|
+
}
|
1603
|
+
end
|
1604
|
+
|
1605
|
+
def test_bulk_index_into_a_create
|
1606
|
+
stub_elastic_ping
|
1607
|
+
stub_elastic_index_to_create
|
1608
|
+
|
1609
|
+
assert_raise(Fluent::Plugin::ElasticsearchErrorHandler::ElasticsearchError) {
|
1610
|
+
driver.run(default_tag: 'test', shutdown: false) do
|
1611
|
+
driver.feed(sample_record)
|
1612
|
+
end
|
1613
|
+
}
|
1614
|
+
assert(index_cmds[0].has_key?("create"))
|
1615
|
+
end
|
1616
|
+
|
1617
|
+
def test_bulk_unexpected_response_op
|
1618
|
+
stub_elastic_ping
|
1619
|
+
stub_elastic_unexpected_response_op
|
1620
|
+
|
1621
|
+
assert_raise(Fluent::Plugin::ElasticsearchErrorHandler::ElasticsearchVersionMismatch) {
|
1622
|
+
driver.run(default_tag: 'test', shutdown: false) do
|
1623
|
+
driver.feed(sample_record)
|
1624
|
+
end
|
1625
|
+
}
|
1626
|
+
end
|
1627
|
+
|
1397
1628
|
def test_update_should_not_write_if_theres_no_id
|
1398
1629
|
driver.configure("write_operation update\n")
|
1399
1630
|
stub_elastic_ping
|
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
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- diogo
|
@@ -143,6 +143,8 @@ files:
|
|
143
143
|
- README.md
|
144
144
|
- Rakefile
|
145
145
|
- fluent-plugin-elasticsearch.gemspec
|
146
|
+
- lib/fluent/plugin/elasticsearch_constants.rb
|
147
|
+
- lib/fluent/plugin/elasticsearch_error_handler.rb
|
146
148
|
- lib/fluent/plugin/elasticsearch_index_template.rb
|
147
149
|
- lib/fluent/plugin/generate_hash_id_support.rb
|
148
150
|
- lib/fluent/plugin/out_elasticsearch.rb
|