fluent-plugin-elasticsearch 4.0.7 → 4.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.
@@ -7,6 +7,13 @@ module Fluent::Plugin
7
7
  Fluent::Plugin.register_filter('elasticsearch_genid', self)
8
8
 
9
9
  config_param :hash_id_key, :string, :default => '_hash'
10
+ config_param :include_tag_in_seed, :bool, :default => false
11
+ config_param :include_time_in_seed, :bool, :default => false
12
+ config_param :use_record_as_seed, :bool, :default => false
13
+ config_param :use_entire_record, :bool, :default => false
14
+ config_param :record_keys, :array, :default => []
15
+ config_param :separator, :string, :default => '_'
16
+ config_param :hash_type, :enum, list: [:md5, :sha1, :sha256, :sha512], :default => :sha1
10
17
 
11
18
  def initialize
12
19
  super
@@ -14,12 +21,57 @@ module Fluent::Plugin
14
21
 
15
22
  def configure(conf)
16
23
  super
24
+
25
+ if !@use_entire_record
26
+ if @record_keys.empty? && @use_record_as_seed
27
+ raise Fluent::ConfigError, "When using record as hash seed, users must specify `record_keys`."
28
+ end
29
+ end
30
+
31
+ if @use_record_as_seed
32
+ class << self
33
+ alias_method :filter, :filter_seed_as_record
34
+ end
35
+ else
36
+ class << self
37
+ alias_method :filter, :filter_simple
38
+ end
39
+ end
17
40
  end
18
41
 
19
42
  def filter(tag, time, record)
43
+ # for safety.
44
+ end
45
+
46
+ def filter_simple(tag, time, record)
20
47
  record[@hash_id_key] = Base64.strict_encode64(SecureRandom.uuid)
21
48
  record
22
49
  end
23
50
 
51
+ def filter_seed_as_record(tag, time, record)
52
+ seed = ""
53
+ seed += tag + separator if @include_tag_in_seed
54
+ seed += time.to_s + separator if @include_time_in_seed
55
+ if @use_entire_record
56
+ record.each {|k,v| seed += "|#{k}|#{v}"}
57
+ else
58
+ seed += record_keys.map {|k| record[k]}.join(separator)
59
+ end
60
+ record[@hash_id_key] = Base64.strict_encode64(encode_hash(@hash_type, seed))
61
+ record
62
+ end
63
+
64
+ def encode_hash(type, seed)
65
+ case type
66
+ when :md5
67
+ Digest::MD5.digest(seed)
68
+ when :sha1
69
+ Digest::SHA1.digest(seed)
70
+ when :sha256
71
+ Digest::SHA256.digest(seed)
72
+ when :sha512
73
+ Digest::SHA512.digest(seed)
74
+ end
75
+ end
24
76
  end
25
77
  end
@@ -25,6 +25,7 @@ require_relative 'elasticsearch_error_handler'
25
25
  require_relative 'elasticsearch_index_template'
26
26
  require_relative 'elasticsearch_index_lifecycle_management'
27
27
  require_relative 'elasticsearch_tls'
28
+ require_relative 'elasticsearch_fallback_selector'
28
29
  begin
29
30
  require_relative 'oj_serializer'
30
31
  rescue LoadError
@@ -55,6 +56,7 @@ module Fluent::Plugin
55
56
  attr_reader :alias_indexes
56
57
  attr_reader :template_names
57
58
  attr_reader :ssl_version_options
59
+ attr_reader :compressable_connection
58
60
 
59
61
  helpers :event_emitter, :compat_parameters, :record_accessor, :timer
60
62
 
@@ -89,6 +91,7 @@ EOC
89
91
  config_param :logstash_dateformat, :string, :default => "%Y.%m.%d"
90
92
  config_param :utc_index, :bool, :default => true
91
93
  config_param :type_name, :string, :default => DEFAULT_TYPE_NAME
94
+ config_param :suppress_type_name, :bool, :default => false
92
95
  config_param :index_name, :string, :default => "fluentd"
93
96
  config_param :id_key, :string, :default => nil
94
97
  config_param :write_operation, :string, :default => "index"
@@ -135,6 +138,7 @@ EOC
135
138
  config_param :with_transporter_log, :bool, :default => false
136
139
  config_param :emit_error_for_missing_id, :bool, :default => false
137
140
  config_param :sniffer_class_name, :string, :default => nil
141
+ config_param :selector_class_name, :string, :default => nil
138
142
  config_param :reload_after, :integer, :default => DEFAULT_RELOAD_AFTER
139
143
  config_param :content_type, :enum, list: [:"application/json", :"application/x-ndjson"], :default => :"application/json",
140
144
  :deprecated => <<EOC
@@ -159,6 +163,7 @@ EOC
159
163
  config_param :enable_ilm, :bool, :default => false
160
164
  config_param :ilm_policy_id, :string, :default => DEFAULT_POLICY_ID
161
165
  config_param :ilm_policy, :hash, :default => {}
166
+ config_param :ilm_policies, :hash, :default => {}
162
167
  config_param :ilm_policy_overwrite, :bool, :default => false
163
168
  config_param :truncate_caches_interval, :time, :default => nil
164
169
 
@@ -219,6 +224,11 @@ EOC
219
224
  log.info "host placeholder and template installation makes your Elasticsearch cluster a bit slow down(beta)."
220
225
  end
221
226
 
227
+ raise Fluent::ConfigError, "You can't specify ilm_policy and ilm_policies at the same time" unless @ilm_policy.empty? or @ilm_policies.empty?
228
+
229
+ unless @ilm_policy.empty?
230
+ @ilm_policies = { @ilm_policy_id => @ilm_policy }
231
+ end
222
232
  @alias_indexes = []
223
233
  @template_names = []
224
234
  if !dry_run?
@@ -227,14 +237,14 @@ EOC
227
237
  raise Fluent::ConfigError, "deflector_alias is prohibited to use with 'logstash_format at same time." if @logstash_format and @deflector_alias
228
238
  end
229
239
  if @ilm_policy.empty? && @ilm_policy_overwrite
230
- raise Fluent::ConfigError, "ilm_policy_overwrite can work with non empty ilm_policy. Specify non-empty ilm policy into ilm_policy. "
240
+ raise Fluent::ConfigError, "ilm_policy_overwrite requires a non empty ilm_policy."
231
241
  end
232
242
  if @logstash_format || placeholder_substitution_needed_for_template?
233
243
  class << self
234
244
  alias_method :template_installation, :template_installation_actual
235
245
  end
236
246
  else
237
- template_installation_actual(@deflector_alias ? @deflector_alias : @index_name, @template_name, @customize_template, @application_name, @index_name)
247
+ template_installation_actual(@deflector_alias ? @deflector_alias : @index_name, @template_name, @customize_template, @application_name, @index_name, @ilm_policy_id)
238
248
  end
239
249
  verify_ilm_working if @enable_ilm
240
250
  elsif @templates
@@ -292,21 +302,32 @@ EOC
292
302
  raise Fluent::ConfigError, "Could not load sniffer class #{@sniffer_class_name}: #{ex}"
293
303
  end
294
304
 
305
+ @selector_class = nil
306
+ begin
307
+ @selector_class = Object.const_get(@selector_class_name) if @selector_class_name
308
+ rescue Exception => ex
309
+ raise Fluent::ConfigError, "Could not load selector class #{@selector_class_name}: #{ex}"
310
+ end
311
+
295
312
  @last_seen_major_version = if major_version = handle_last_seen_es_major_version
296
313
  major_version
297
314
  else
298
315
  @default_elasticsearch_version
299
316
  end
300
- if @last_seen_major_version == 6 && @type_name != DEFAULT_TYPE_NAME_ES_7x
301
- log.info "Detected ES 6.x: ES 7.x will only accept `_doc` in type_name."
302
- end
303
- if @last_seen_major_version == 7 && @type_name != DEFAULT_TYPE_NAME_ES_7x
304
- log.warn "Detected ES 7.x: `_doc` will be used as the document `_type`."
305
- @type_name = '_doc'.freeze
306
- end
307
- if @last_seen_major_version >= 8 && @type_name != DEFAULT_TYPE_NAME_ES_7x
308
- log.info "Detected ES 8.x or above: This parameter has no effect."
317
+ if @suppress_type_name && @last_seen_major_version >= 7
309
318
  @type_name = nil
319
+ else
320
+ if @last_seen_major_version == 6 && @type_name != DEFAULT_TYPE_NAME_ES_7x
321
+ log.info "Detected ES 6.x: ES 7.x will only accept `_doc` in type_name."
322
+ end
323
+ if @last_seen_major_version == 7 && @type_name != DEFAULT_TYPE_NAME_ES_7x
324
+ log.warn "Detected ES 7.x: `_doc` will be used as the document `_type`."
325
+ @type_name = '_doc'.freeze
326
+ end
327
+ if @last_seen_major_version >= 8 && @type_name != DEFAULT_TYPE_NAME_ES_7x
328
+ log.info "Detected ES 8.x or above: This parameter has no effect."
329
+ @type_name = nil
330
+ end
310
331
  end
311
332
 
312
333
  if @validate_client_version && !dry_run?
@@ -343,6 +364,7 @@ EOC
343
364
  @routing_key_name = configure_routing_key_name
344
365
  @meta_config_map = create_meta_config_map
345
366
  @current_config = nil
367
+ @compressable_connection = false
346
368
 
347
369
  @ignore_exception_classes = @ignore_exceptions.map do |exception|
348
370
  unless Object.const_defined?(exception)
@@ -511,13 +533,15 @@ EOC
511
533
  return Time.at(event_time).to_datetime
512
534
  end
513
535
 
514
- def client(host = nil)
536
+ def client(host = nil, compress_connection = false)
515
537
  # check here to see if we already have a client connection for the given host
516
538
  connection_options = get_connection_options(host)
517
539
 
518
540
  @_es = nil unless is_existing_connection(connection_options[:hosts])
541
+ @_es = nil unless @compressable_connection == compress_connection
519
542
 
520
543
  @_es ||= begin
544
+ @compressable_connection = compress_connection
521
545
  @current_config = connection_options[:hosts].clone
522
546
  adapter_conf = lambda {|f| f.adapter @http_backend, @backend_options }
523
547
  local_reload_connections = @reload_connections
@@ -525,7 +549,7 @@ EOC
525
549
  local_reload_connections = @reload_after
526
550
  end
527
551
 
528
- gzip_headers = if compression
552
+ gzip_headers = if compress_connection
529
553
  {'Content-Encoding' => 'gzip'}
530
554
  else
531
555
  {}
@@ -551,7 +575,8 @@ EOC
551
575
  },
552
576
  sniffer_class: @sniffer_class,
553
577
  serializer_class: @serializer_class,
554
- compression: compression,
578
+ selector_class: @selector_class,
579
+ compression: compress_connection,
555
580
  }), &adapter_conf)
556
581
  Elasticsearch::Client.new transport: transport
557
582
  end
@@ -715,7 +740,12 @@ EOC
715
740
  else
716
741
  pipeline = nil
717
742
  end
718
- return logstash_prefix, logstash_dateformat, index_name, type_name, template_name, customize_template, deflector_alias, application_name, pipeline
743
+ if @ilm_policy_id
744
+ ilm_policy_id = extract_placeholders(@ilm_policy_id, chunk)
745
+ else
746
+ ilm_policy_id = nil
747
+ end
748
+ return logstash_prefix, logstash_dateformat, index_name, type_name, template_name, customize_template, deflector_alias, application_name, pipeline, ilm_policy_id
719
749
  end
720
750
 
721
751
  def multi_workers_ready?
@@ -741,9 +771,9 @@ EOC
741
771
  begin
742
772
  meta, header, record = process_message(tag, meta, header, time, record, extracted_values)
743
773
  info = if @include_index_in_url
744
- RequestInfo.new(host, meta.delete("_index".freeze), meta["_index".freeze])
774
+ RequestInfo.new(host, meta.delete("_index".freeze), meta.delete("_alias".freeze))
745
775
  else
746
- RequestInfo.new(host, nil, meta["_index".freeze])
776
+ RequestInfo.new(host, nil, meta.delete("_alias".freeze))
747
777
  end
748
778
 
749
779
  if split_request?(bulk_message, info)
@@ -789,7 +819,7 @@ EOC
789
819
  end
790
820
 
791
821
  def process_message(tag, meta, header, time, record, extracted_values)
792
- logstash_prefix, logstash_dateformat, index_name, type_name, _template_name, _customize_template, _deflector_alias, _application_name, pipeline = extracted_values
822
+ logstash_prefix, logstash_dateformat, index_name, type_name, _template_name, _customize_template, _deflector_alias, application_name, pipeline, _ilm_policy_id = extracted_values
793
823
 
794
824
  if @flatten_hashes
795
825
  record = flatten_record(record)
@@ -812,17 +842,19 @@ EOC
812
842
 
813
843
  target_index_parent, target_index_child_key = @target_index_key ? get_parent_of(record, @target_index_key) : nil
814
844
  if target_index_parent && target_index_parent[target_index_child_key]
815
- target_index = target_index_parent.delete(target_index_child_key)
845
+ target_index_alias = target_index = target_index_parent.delete(target_index_child_key)
816
846
  elsif @logstash_format
817
847
  dt = dt.new_offset(0) if @utc_index
818
848
  target_index = "#{logstash_prefix}#{@logstash_prefix_separator}#{dt.strftime(logstash_dateformat)}"
849
+ target_index_alias = "#{logstash_prefix}#{@logstash_prefix_separator}#{application_name}#{@logstash_prefix_separator}#{dt.strftime(logstash_dateformat)}"
819
850
  else
820
- target_index = index_name
851
+ target_index_alias = target_index = index_name
821
852
  end
822
853
 
823
854
  # Change target_index to lower-case since Elasticsearch doesn't
824
855
  # allow upper-case characters in index names.
825
856
  target_index = target_index.downcase
857
+ target_index_alias = target_index_alias.downcase
826
858
  if @include_tag_key
827
859
  record[@tag_key] = tag
828
860
  end
@@ -841,7 +873,9 @@ EOC
841
873
  target_type = nil
842
874
  end
843
875
  else
844
- if @last_seen_major_version == 7 && @type_name != DEFAULT_TYPE_NAME_ES_7x
876
+ if @suppress_type_name && @last_seen_major_version >= 7
877
+ target_type = nil
878
+ elsif @last_seen_major_version == 7 && @type_name != DEFAULT_TYPE_NAME_ES_7x
845
879
  log.warn "Detected ES 7.x: `_doc` will be used as the document `_type`."
846
880
  target_type = '_doc'.freeze
847
881
  elsif @last_seen_major_version >= 8
@@ -855,6 +889,7 @@ EOC
855
889
  meta.clear
856
890
  meta["_index".freeze] = target_index
857
891
  meta["_type".freeze] = target_type unless @last_seen_major_version >= 8
892
+ meta["_alias".freeze] = target_index_alias
858
893
 
859
894
  if @pipeline
860
895
  meta["pipeline".freeze] = pipeline
@@ -897,16 +932,17 @@ EOC
897
932
  placeholder?(:logstash_prefix, @logstash_prefix.to_s) ||
898
933
  placeholder?(:logstash_dateformat, @logstash_dateformat.to_s) ||
899
934
  placeholder?(:deflector_alias, @deflector_alias.to_s) ||
900
- placeholder?(:application_name, @application_name.to_s)
935
+ placeholder?(:application_name, @application_name.to_s) ||
936
+ placeholder?(:ilm_policy_id, @ilm_policy_id.to_s)
901
937
  log.debug("Need substitution: #{need_substitution}")
902
938
  need_substitution
903
939
  end
904
940
 
905
- def template_installation(deflector_alias, template_name, customize_template, application_name, target_index, host)
941
+ def template_installation(deflector_alias, template_name, customize_template, application_name, ilm_policy_id, target_index, host)
906
942
  # for safety.
907
943
  end
908
944
 
909
- def template_installation_actual(deflector_alias, template_name, customize_template, application_name, target_index, host=nil)
945
+ def template_installation_actual(deflector_alias, template_name, customize_template, application_name, target_index, ilm_policy_id, host=nil)
910
946
  if template_name && @template_file
911
947
  if @alias_indexes.include? deflector_alias
912
948
  log.debug("Index alias #{deflector_alias} already exists (cached)")
@@ -915,11 +951,12 @@ EOC
915
951
  else
916
952
  retry_operate(@max_retry_putting_template, @fail_on_putting_template_retry_exceed) do
917
953
  if customize_template
918
- template_custom_install(template_name, @template_file, @template_overwrite, customize_template, @enable_ilm, deflector_alias, @ilm_policy_id, host)
954
+ template_custom_install(template_name, @template_file, @template_overwrite, customize_template, @enable_ilm, deflector_alias, ilm_policy_id, host)
919
955
  else
920
- template_install(template_name, @template_file, @template_overwrite, @enable_ilm, deflector_alias, @ilm_policy_id, host)
956
+ template_install(template_name, @template_file, @template_overwrite, @enable_ilm, deflector_alias, ilm_policy_id, host)
921
957
  end
922
- create_rollover_alias(target_index, @rollover_index, deflector_alias, application_name, @index_date_pattern, @index_separator, @enable_ilm, @ilm_policy_id, @ilm_policy, @ilm_policy_overwrite, host)
958
+ ilm_policy = @ilm_policies[ilm_policy_id] || {}
959
+ create_rollover_alias(target_index, @rollover_index, deflector_alias, application_name, @index_date_pattern, @index_separator, @enable_ilm, ilm_policy_id, ilm_policy, @ilm_policy_overwrite, host)
923
960
  end
924
961
  @alias_indexes << deflector_alias unless deflector_alias.nil?
925
962
  @template_names << template_name unless template_name.nil?
@@ -930,11 +967,11 @@ EOC
930
967
  # send_bulk given a specific bulk request, the original tag,
931
968
  # chunk, and bulk_message_count
932
969
  def send_bulk(data, tag, chunk, bulk_message_count, extracted_values, info)
933
- logstash_prefix, _logstash_dateformat, index_name, _type_name, template_name, customize_template, deflector_alias, application_name, _pipeline = extracted_values
970
+ logstash_prefix, _logstash_dateformat, index_name, _type_name, template_name, customize_template, deflector_alias, application_name, _pipeline, ilm_policy_id = extracted_values
934
971
  if deflector_alias
935
- template_installation(deflector_alias, template_name, customize_template, application_name, index_name, info.host)
972
+ template_installation(deflector_alias, template_name, customize_template, application_name, index_name, ilm_policy_id, info.host)
936
973
  else
937
- template_installation(info.ilm_index, template_name, customize_template, application_name, @logstash_format ? logstash_prefix : index_name, info.host)
974
+ template_installation(info.ilm_index, template_name, customize_template, application_name, @logstash_format ? logstash_prefix : index_name, ilm_policy_id, info.host)
938
975
  end
939
976
 
940
977
  begin
@@ -947,7 +984,7 @@ EOC
947
984
  data
948
985
  end
949
986
 
950
- response = client(info.host).bulk body: prepared_data, index: info.index
987
+ response = client(info.host, compression).bulk body: prepared_data, index: info.index
951
988
  log.on_trace { log.trace "bulk response: #{response}" }
952
989
 
953
990
  if response['errors']
@@ -35,16 +35,18 @@ module Fluent::Plugin
35
35
  end
36
36
 
37
37
 
38
- def client(host = nil)
38
+ def client(host = nil, compress_connection = false)
39
39
  # check here to see if we already have a client connection for the given host
40
40
  connection_options = get_connection_options(host)
41
41
 
42
42
  @_es = nil unless is_existing_connection(connection_options[:hosts])
43
+ @_es = nil unless @compressable_connection == compress_connection
43
44
 
44
45
  @_es ||= begin
46
+ @compressable_connection = compress_connection
45
47
  @current_config = connection_options[:hosts].clone
46
48
  adapter_conf = lambda {|f| f.adapter @http_backend, @backend_options }
47
- gzip_headers = if compression
49
+ gzip_headers = if compress_connection
48
50
  {'Content-Encoding' => 'gzip'}
49
51
  else
50
52
  {}
@@ -67,7 +69,7 @@ module Fluent::Plugin
67
69
  password: @password,
68
70
  scheme: @scheme
69
71
  },
70
- compression: compression,
72
+ compression: compress_connection,
71
73
  }), &adapter_conf)
72
74
  Elasticsearch::Client.new transport: transport
73
75
  end
@@ -228,7 +230,7 @@ module Fluent::Plugin
228
230
  else
229
231
  data
230
232
  end
231
- response = client(host).bulk body: prepared_data, index: index
233
+ response = client(host, compression).bulk body: prepared_data, index: index
232
234
  if response['errors']
233
235
  log.error "Could not push log to Elasticsearch: #{response}"
234
236
  end
@@ -0,0 +1,73 @@
1
+ require_relative '../helper'
2
+ require 'fluent/test/driver/output'
3
+ require 'fluent/plugin/out_elasticsearch'
4
+
5
+ class ElasticsearchFallbackSelectorTest < Test::Unit::TestCase
6
+ attr_accessor :index_cmds
7
+
8
+ def setup
9
+ Fluent::Test.setup
10
+ @driver = nil
11
+ log = Fluent::Engine.log
12
+ log.out.logs.slice!(0, log.out.logs.length)
13
+ end
14
+
15
+ def stub_elastic(url="http://localhost:9200/_bulk")
16
+ stub_request(:post, url).with do |req|
17
+ @index_cmds = req.body.split("\n").map {|r| JSON.parse(r) }
18
+ end
19
+ end
20
+
21
+ def stub_elastic_info(url="http://localhost:9200/", version="6.4.2")
22
+ body ="{\"version\":{\"number\":\"#{version}\"}}"
23
+ stub_request(:get, url).to_return({:status => 200, :body => body, :headers => { 'Content-Type' => 'json' } })
24
+ end
25
+
26
+ def stub_elastic_info_not_found(url="http://localhost:9200/", version="6.4.2")
27
+ stub_request(:get, url).to_return(:status => [404, "Not Found"])
28
+ end
29
+
30
+ def stub_elastic_info_unavailable(url="http://localhost:9200/", version="6.4.2")
31
+ stub_request(:get, url).to_return(:status => [503, "Service Unavailable"])
32
+ end
33
+
34
+ def sample_record(content={})
35
+ {'age' => 26, 'request_id' => '42', 'parent_id' => 'parent', 'routing_id' => 'routing'}.merge(content)
36
+ end
37
+
38
+ def driver(conf='')
39
+ @driver ||= Fluent::Test::Driver::Output.new(Fluent::Plugin::ElasticsearchOutput) {
40
+ # v0.12's test driver assume format definition. This simulates ObjectBufferedOutput format
41
+ if !defined?(Fluent::Plugin::Output)
42
+ def format(tag, time, record)
43
+ [time, record].to_msgpack
44
+ end
45
+ end
46
+ }.configure(conf)
47
+ end
48
+
49
+ def test_fallback_on_info
50
+ stub_elastic_info_not_found("http://localhost:9202/")
51
+ stub_elastic_info_unavailable("http://localhost:9201/")
52
+ stub_elastic_info
53
+ stub_elastic
54
+ config = %[
55
+ hosts localhost:9202,localhost:9201,localhost:9200
56
+ selector_class_name Fluent::Plugin::ElasticseatchFallbackSelector
57
+ @log_level debug
58
+ with_transporter_log true
59
+ reload_connections true
60
+ reload_after 10
61
+ ]
62
+ assert_raise(Elasticsearch::Transport::Transport::Errors::NotFound) do
63
+ driver(config)
64
+ end
65
+ driver.run(default_tag: 'test') do
66
+ driver.feed(sample_record)
67
+ end
68
+ assert_equal(2, index_cmds.length)
69
+ assert_equal("fluentd", index_cmds.first['index']['_index'])
70
+ end
71
+
72
+ # TODO: on feed phase test case
73
+ end