fluent-plugin-elasticsearch 2.10.3 → 2.10.4
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/.coveralls.yml +2 -2
- data/.editorconfig +9 -9
- data/.gitignore +18 -18
- data/.travis.yml +14 -14
- data/Gemfile +8 -8
- data/History.md +243 -239
- data/ISSUE_TEMPLATE.md +25 -25
- data/LICENSE.txt +201 -201
- data/PULL_REQUEST_TEMPLATE.md +10 -10
- data/README.md +807 -807
- data/Rakefile +11 -11
- data/appveyor.yml +35 -35
- data/fluent-plugin-elasticsearch.gemspec +31 -31
- data/lib/fluent/plugin/elasticsearch_constants.rb +13 -13
- data/lib/fluent/plugin/elasticsearch_error_handler.rb +89 -87
- data/lib/fluent/plugin/elasticsearch_index_template.rb +60 -60
- data/lib/fluent/plugin/filter_elasticsearch_genid.rb +25 -25
- data/lib/fluent/plugin/out_elasticsearch.rb +540 -538
- data/lib/fluent/plugin/out_elasticsearch_dynamic.rb +294 -294
- data/test/helper.rb +24 -24
- data/test/plugin/test_elasticsearch_error_handler.rb +195 -195
- data/test/plugin/test_filter_elasticsearch_genid.rb +44 -44
- data/test/plugin/test_out_elasticsearch.rb +2055 -2046
- data/test/plugin/test_out_elasticsearch_dynamic.rb +992 -992
- data/test/plugin/test_template.json +23 -23
- metadata +3 -3
data/Rakefile
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
require 'bundler/gem_tasks'
|
2
|
-
require 'rake/testtask'
|
3
|
-
|
4
|
-
Rake::TestTask.new(:test) do |test|
|
5
|
-
test.libs << 'test'
|
6
|
-
test.pattern = 'test/**/test_*.rb'
|
7
|
-
test.verbose = true
|
8
|
-
test.warning = false
|
9
|
-
end
|
10
|
-
|
11
|
-
task :default => :test
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
Rake::TestTask.new(:test) do |test|
|
5
|
+
test.libs << 'test'
|
6
|
+
test.pattern = 'test/**/test_*.rb'
|
7
|
+
test.verbose = true
|
8
|
+
test.warning = false
|
9
|
+
end
|
10
|
+
|
11
|
+
task :default => :test
|
data/appveyor.yml
CHANGED
@@ -1,35 +1,35 @@
|
|
1
|
-
version: '{build}'
|
2
|
-
install:
|
3
|
-
- SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
|
4
|
-
- "%devkit%\\devkitvars.bat"
|
5
|
-
- IF EXIST "%devkit%\\bin\\ridk.cmd" ridk.cmd enable
|
6
|
-
- ruby --version
|
7
|
-
- gem --version
|
8
|
-
- bundle install
|
9
|
-
build: off
|
10
|
-
test_script:
|
11
|
-
- bundle exec rake test
|
12
|
-
|
13
|
-
# https://www.appveyor.com/docs/installed-software/#ruby
|
14
|
-
environment:
|
15
|
-
matrix:
|
16
|
-
- ruby_version: "25-x64"
|
17
|
-
devkit: C:\Ruby23-x64\DevKit
|
18
|
-
- ruby_version: "25"
|
19
|
-
devkit: C:\Ruby23\DevKit
|
20
|
-
- ruby_version: "24-x64"
|
21
|
-
devkit: C:\Ruby23-x64\DevKit
|
22
|
-
- ruby_version: "24"
|
23
|
-
devkit: C:\Ruby23\DevKit
|
24
|
-
- ruby_version: "23-x64"
|
25
|
-
devkit: C:\Ruby23-x64\DevKit
|
26
|
-
- ruby_version: "22-x64"
|
27
|
-
devkit: C:\Ruby23-x64\DevKit
|
28
|
-
- ruby_version: "21-x64"
|
29
|
-
devkit: C:\Ruby23-x64\DevKit
|
30
|
-
- ruby_version: "21"
|
31
|
-
devkit: C:\Ruby23\DevKit
|
32
|
-
WIN_RAPID: true
|
33
|
-
matrix:
|
34
|
-
allow_failures:
|
35
|
-
- ruby_version: "21"
|
1
|
+
version: '{build}'
|
2
|
+
install:
|
3
|
+
- SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
|
4
|
+
- "%devkit%\\devkitvars.bat"
|
5
|
+
- IF EXIST "%devkit%\\bin\\ridk.cmd" ridk.cmd enable
|
6
|
+
- ruby --version
|
7
|
+
- gem --version
|
8
|
+
- bundle install
|
9
|
+
build: off
|
10
|
+
test_script:
|
11
|
+
- bundle exec rake test
|
12
|
+
|
13
|
+
# https://www.appveyor.com/docs/installed-software/#ruby
|
14
|
+
environment:
|
15
|
+
matrix:
|
16
|
+
- ruby_version: "25-x64"
|
17
|
+
devkit: C:\Ruby23-x64\DevKit
|
18
|
+
- ruby_version: "25"
|
19
|
+
devkit: C:\Ruby23\DevKit
|
20
|
+
- ruby_version: "24-x64"
|
21
|
+
devkit: C:\Ruby23-x64\DevKit
|
22
|
+
- ruby_version: "24"
|
23
|
+
devkit: C:\Ruby23\DevKit
|
24
|
+
- ruby_version: "23-x64"
|
25
|
+
devkit: C:\Ruby23-x64\DevKit
|
26
|
+
- ruby_version: "22-x64"
|
27
|
+
devkit: C:\Ruby23-x64\DevKit
|
28
|
+
- ruby_version: "21-x64"
|
29
|
+
devkit: C:\Ruby23-x64\DevKit
|
30
|
+
- ruby_version: "21"
|
31
|
+
devkit: C:\Ruby23\DevKit
|
32
|
+
WIN_RAPID: true
|
33
|
+
matrix:
|
34
|
+
allow_failures:
|
35
|
+
- ruby_version: "21"
|
@@ -1,31 +1,31 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
$:.push File.expand_path('../lib', __FILE__)
|
3
|
-
|
4
|
-
Gem::Specification.new do |s|
|
5
|
-
s.name = 'fluent-plugin-elasticsearch'
|
6
|
-
s.version = '2.10.
|
7
|
-
s.authors = ['diogo', 'pitr']
|
8
|
-
s.email = ['pitr.vern@gmail.com', 'me@diogoterror.com']
|
9
|
-
s.description = %q{Elasticsearch output plugin for Fluent event collector}
|
10
|
-
s.summary = s.description
|
11
|
-
s.homepage = 'https://github.com/uken/fluent-plugin-elasticsearch'
|
12
|
-
s.license = 'Apache-2.0'
|
13
|
-
|
14
|
-
s.files = `git ls-files`.split($/)
|
15
|
-
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
-
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
17
|
-
s.require_paths = ['lib']
|
18
|
-
|
19
|
-
s.required_ruby_version = Gem::Requirement.new(">= 2.0".freeze)
|
20
|
-
|
21
|
-
s.add_runtime_dependency 'fluentd', '>= 0.14.20'
|
22
|
-
s.add_runtime_dependency 'excon', '>= 0'
|
23
|
-
s.add_runtime_dependency 'elasticsearch'
|
24
|
-
|
25
|
-
|
26
|
-
s.add_development_dependency 'rake', '>= 0'
|
27
|
-
s.add_development_dependency 'webmock', '~> 1'
|
28
|
-
s.add_development_dependency 'test-unit', '~> 3.1.0'
|
29
|
-
s.add_development_dependency 'minitest', '~> 5.8'
|
30
|
-
s.add_development_dependency 'flexmock', '~> 2.0'
|
31
|
-
end
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'fluent-plugin-elasticsearch'
|
6
|
+
s.version = '2.10.4'
|
7
|
+
s.authors = ['diogo', 'pitr']
|
8
|
+
s.email = ['pitr.vern@gmail.com', 'me@diogoterror.com']
|
9
|
+
s.description = %q{Elasticsearch output plugin for Fluent event collector}
|
10
|
+
s.summary = s.description
|
11
|
+
s.homepage = 'https://github.com/uken/fluent-plugin-elasticsearch'
|
12
|
+
s.license = 'Apache-2.0'
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split($/)
|
15
|
+
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
17
|
+
s.require_paths = ['lib']
|
18
|
+
|
19
|
+
s.required_ruby_version = Gem::Requirement.new(">= 2.0".freeze)
|
20
|
+
|
21
|
+
s.add_runtime_dependency 'fluentd', '>= 0.14.20'
|
22
|
+
s.add_runtime_dependency 'excon', '>= 0'
|
23
|
+
s.add_runtime_dependency 'elasticsearch'
|
24
|
+
|
25
|
+
|
26
|
+
s.add_development_dependency 'rake', '>= 0'
|
27
|
+
s.add_development_dependency 'webmock', '~> 1'
|
28
|
+
s.add_development_dependency 'test-unit', '~> 3.1.0'
|
29
|
+
s.add_development_dependency 'minitest', '~> 5.8'
|
30
|
+
s.add_development_dependency 'flexmock', '~> 2.0'
|
31
|
+
end
|
@@ -1,13 +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
|
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
|
@@ -1,87 +1,89 @@
|
|
1
|
-
require 'fluent/event'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
class
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
write_operation =
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
when
|
61
|
-
stats[:
|
62
|
-
when
|
63
|
-
stats[:
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
@plugin.
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
1
|
+
require 'fluent/event'
|
2
|
+
require 'fluent/error'
|
3
|
+
require_relative 'elasticsearch_constants'
|
4
|
+
|
5
|
+
class Fluent::Plugin::ElasticsearchErrorHandler
|
6
|
+
include Fluent::Plugin::ElasticsearchConstants
|
7
|
+
|
8
|
+
attr_accessor :bulk_message_count
|
9
|
+
class ElasticsearchVersionMismatch < Fluent::UnrecoverableError; end
|
10
|
+
class ElasticsearchSubmitMismatch < Fluent::UnrecoverableError; end
|
11
|
+
class ElasticsearchError < StandardError; end
|
12
|
+
|
13
|
+
def initialize(plugin)
|
14
|
+
@plugin = plugin
|
15
|
+
end
|
16
|
+
|
17
|
+
def handle_error(response, tag, chunk, bulk_message_count, extracted_values)
|
18
|
+
items = response['items']
|
19
|
+
if items.nil? || !items.is_a?(Array)
|
20
|
+
raise ElasticsearchVersionMismatch, "The response format was unrecognized: #{response}"
|
21
|
+
end
|
22
|
+
if bulk_message_count != items.length
|
23
|
+
raise ElasticsearchSubmitMismatch, "The number of records submitted #{bulk_message_count} do not match the number returned #{items.length}. Unable to process bulk response."
|
24
|
+
end
|
25
|
+
retry_stream = Fluent::MultiEventStream.new
|
26
|
+
stats = Hash.new(0)
|
27
|
+
meta = {}
|
28
|
+
header = {}
|
29
|
+
chunk.msgpack_each do |time, rawrecord|
|
30
|
+
bulk_message = ''
|
31
|
+
next unless rawrecord.is_a? Hash
|
32
|
+
begin
|
33
|
+
# we need a deep copy for process_message to alter
|
34
|
+
processrecord = Marshal.load(Marshal.dump(rawrecord))
|
35
|
+
@plugin.process_message(tag, meta, header, time, processrecord, bulk_message, extracted_values)
|
36
|
+
rescue => e
|
37
|
+
stats[:bad_chunk_record] += 1
|
38
|
+
next
|
39
|
+
end
|
40
|
+
item = items.shift
|
41
|
+
if item.has_key?(@plugin.write_operation)
|
42
|
+
write_operation = @plugin.write_operation
|
43
|
+
elsif INDEX_OP == @plugin.write_operation && item.has_key?(CREATE_OP)
|
44
|
+
write_operation = CREATE_OP
|
45
|
+
else
|
46
|
+
# When we don't have an expected ops field, something changed in the API
|
47
|
+
# expected return values (ES 2.x)
|
48
|
+
stats[:errors_bad_resp] += 1
|
49
|
+
next
|
50
|
+
end
|
51
|
+
if item[write_operation].has_key?('status')
|
52
|
+
status = item[write_operation]['status']
|
53
|
+
else
|
54
|
+
# When we don't have a status field, something changed in the API
|
55
|
+
# expected return values (ES 2.x)
|
56
|
+
stats[:errors_bad_resp] += 1
|
57
|
+
next
|
58
|
+
end
|
59
|
+
case
|
60
|
+
when [200, 201].include?(status)
|
61
|
+
stats[:successes] += 1
|
62
|
+
when CREATE_OP == write_operation && 409 == status
|
63
|
+
stats[:duplicates] += 1
|
64
|
+
when 400 == status
|
65
|
+
stats[:bad_argument] += 1
|
66
|
+
@plugin.router.emit_error_event(tag, time, rawrecord, ElasticsearchError.new('400 - Rejected by Elasticsearch'))
|
67
|
+
else
|
68
|
+
if item[write_operation].has_key?('error') && item[write_operation]['error'].has_key?('type')
|
69
|
+
type = item[write_operation]['error']['type']
|
70
|
+
stats[type] += 1
|
71
|
+
retry_stream.add(time, rawrecord)
|
72
|
+
else
|
73
|
+
# When we don't have a type field, something changed in the API
|
74
|
+
# expected return values (ES 2.x)
|
75
|
+
stats[:errors_bad_resp] += 1
|
76
|
+
@plugin.router.emit_error_event(tag, time, rawrecord, ElasticsearchError.new("#{status} - No error type provided in the response"))
|
77
|
+
next
|
78
|
+
end
|
79
|
+
stats[type] += 1
|
80
|
+
end
|
81
|
+
end
|
82
|
+
@plugin.log.on_debug do
|
83
|
+
msg = ["Indexed (op = #{@plugin.write_operation})"]
|
84
|
+
stats.each_pair { |key, value| msg << "#{value} #{key}" }
|
85
|
+
@plugin.log.debug msg.join(', ')
|
86
|
+
end
|
87
|
+
raise Fluent::Plugin::ElasticsearchOutput::RetryStreamError.new(retry_stream) unless retry_stream.empty?
|
88
|
+
end
|
89
|
+
end
|
@@ -1,60 +1,60 @@
|
|
1
|
-
module Fluent::ElasticsearchIndexTemplate
|
2
|
-
|
3
|
-
def get_template(template_file)
|
4
|
-
if !File.exists?(template_file)
|
5
|
-
raise "If you specify a template_name you must specify a valid template file (checked '#{template_file}')!"
|
6
|
-
end
|
7
|
-
file_contents = IO.read(template_file).gsub(/\n/,'')
|
8
|
-
JSON.parse(file_contents)
|
9
|
-
end
|
10
|
-
|
11
|
-
def template_exists?(name)
|
12
|
-
client.indices.get_template(:name => name)
|
13
|
-
return true
|
14
|
-
rescue Elasticsearch::Transport::Transport::Errors::NotFound
|
15
|
-
return false
|
16
|
-
end
|
17
|
-
|
18
|
-
def retry_install(max_retries)
|
19
|
-
return unless block_given?
|
20
|
-
retries = 0
|
21
|
-
begin
|
22
|
-
yield
|
23
|
-
rescue Fluent::Plugin::ElasticsearchOutput::ConnectionFailure, Timeout::Error => e
|
24
|
-
@_es = nil
|
25
|
-
@_es_info = nil
|
26
|
-
if retries < max_retries
|
27
|
-
retries += 1
|
28
|
-
sleep 2**retries
|
29
|
-
log.warn "Could not push template(s) to Elasticsearch, resetting connection and trying again. #{e.message}"
|
30
|
-
retry
|
31
|
-
end
|
32
|
-
raise Fluent::Plugin::ElasticsearchOutput::ConnectionFailure, "Could not push template(s) to Elasticsearch after #{retries} retries. #{e.message}"
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def template_put(name, template)
|
37
|
-
client.indices.put_template(:name => name, :body => template)
|
38
|
-
end
|
39
|
-
|
40
|
-
def template_install(name, template_file, overwrite)
|
41
|
-
if overwrite
|
42
|
-
template_put(name, get_template(template_file))
|
43
|
-
log.info("Template '#{name}' overwritten with #{template_file}.")
|
44
|
-
return
|
45
|
-
end
|
46
|
-
if !template_exists?(name)
|
47
|
-
template_put(name, get_template(template_file))
|
48
|
-
log.info("Template configured, but no template installed. Installed '#{name}' from #{template_file}.")
|
49
|
-
else
|
50
|
-
log.info("Template configured and already installed.")
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def templates_hash_install(templates, overwrite)
|
55
|
-
templates.each do |key, value|
|
56
|
-
template_install(key, value, overwrite)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
1
|
+
module Fluent::ElasticsearchIndexTemplate
|
2
|
+
|
3
|
+
def get_template(template_file)
|
4
|
+
if !File.exists?(template_file)
|
5
|
+
raise "If you specify a template_name you must specify a valid template file (checked '#{template_file}')!"
|
6
|
+
end
|
7
|
+
file_contents = IO.read(template_file).gsub(/\n/,'')
|
8
|
+
JSON.parse(file_contents)
|
9
|
+
end
|
10
|
+
|
11
|
+
def template_exists?(name)
|
12
|
+
client.indices.get_template(:name => name)
|
13
|
+
return true
|
14
|
+
rescue Elasticsearch::Transport::Transport::Errors::NotFound
|
15
|
+
return false
|
16
|
+
end
|
17
|
+
|
18
|
+
def retry_install(max_retries)
|
19
|
+
return unless block_given?
|
20
|
+
retries = 0
|
21
|
+
begin
|
22
|
+
yield
|
23
|
+
rescue Fluent::Plugin::ElasticsearchOutput::ConnectionFailure, Timeout::Error => e
|
24
|
+
@_es = nil
|
25
|
+
@_es_info = nil
|
26
|
+
if retries < max_retries
|
27
|
+
retries += 1
|
28
|
+
sleep 2**retries
|
29
|
+
log.warn "Could not push template(s) to Elasticsearch, resetting connection and trying again. #{e.message}"
|
30
|
+
retry
|
31
|
+
end
|
32
|
+
raise Fluent::Plugin::ElasticsearchOutput::ConnectionFailure, "Could not push template(s) to Elasticsearch after #{retries} retries. #{e.message}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def template_put(name, template)
|
37
|
+
client.indices.put_template(:name => name, :body => template)
|
38
|
+
end
|
39
|
+
|
40
|
+
def template_install(name, template_file, overwrite)
|
41
|
+
if overwrite
|
42
|
+
template_put(name, get_template(template_file))
|
43
|
+
log.info("Template '#{name}' overwritten with #{template_file}.")
|
44
|
+
return
|
45
|
+
end
|
46
|
+
if !template_exists?(name)
|
47
|
+
template_put(name, get_template(template_file))
|
48
|
+
log.info("Template configured, but no template installed. Installed '#{name}' from #{template_file}.")
|
49
|
+
else
|
50
|
+
log.info("Template configured and already installed.")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def templates_hash_install(templates, overwrite)
|
55
|
+
templates.each do |key, value|
|
56
|
+
template_install(key, value, overwrite)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|