fluent-plugin-elasticsearch2 3.5.5
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 +7 -0
 - data/.coveralls.yml +2 -0
 - data/.editorconfig +9 -0
 - data/.gitignore +18 -0
 - data/.travis.yml +18 -0
 - data/CONTRIBUTING.md +24 -0
 - data/Gemfile +9 -0
 - data/History.md +402 -0
 - data/ISSUE_TEMPLATE.md +30 -0
 - data/LICENSE.txt +201 -0
 - data/PULL_REQUEST_TEMPLATE.md +10 -0
 - data/README.md +1558 -0
 - data/Rakefile +11 -0
 - data/appveyor.yml +30 -0
 - data/fluent-plugin-elasticsearch.gemspec +31 -0
 - data/lib/fluent/log-ext.rb +38 -0
 - data/lib/fluent/plugin/elasticsearch_constants.rb +13 -0
 - data/lib/fluent/plugin/elasticsearch_error.rb +5 -0
 - data/lib/fluent/plugin/elasticsearch_error_handler.rb +127 -0
 - data/lib/fluent/plugin/elasticsearch_index_template.rb +112 -0
 - data/lib/fluent/plugin/elasticsearch_simple_sniffer.rb +10 -0
 - data/lib/fluent/plugin/filter_elasticsearch_genid.rb +25 -0
 - data/lib/fluent/plugin/oj_serializer.rb +22 -0
 - data/lib/fluent/plugin/out_elasticsearch.rb +782 -0
 - data/lib/fluent/plugin/out_elasticsearch_dynamic.rb +262 -0
 - data/test/helper.rb +24 -0
 - data/test/plugin/test_alias_template.json +9 -0
 - data/test/plugin/test_elasticsearch_error_handler.rb +525 -0
 - data/test/plugin/test_filter_elasticsearch_genid.rb +44 -0
 - data/test/plugin/test_out_elasticsearch.rb +2811 -0
 - data/test/plugin/test_out_elasticsearch_dynamic.rb +1001 -0
 - data/test/plugin/test_template.json +23 -0
 - data/test/test_log-ext.rb +35 -0
 - metadata +198 -0
 
| 
         @@ -0,0 +1,262 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # encoding: UTF-8
         
     | 
| 
      
 2 
     | 
    
         
            +
            require_relative 'out_elasticsearch'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Fluent::Plugin
         
     | 
| 
      
 5 
     | 
    
         
            +
              class ElasticsearchOutputDynamic < ElasticsearchOutput
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                Fluent::Plugin.register_output('elasticsearch_dynamic', self)
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                helpers :event_emitter
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                config_param :delimiter, :string, :default => "."
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                DYNAMIC_PARAM_NAMES = %W[hosts host port include_timestamp logstash_format logstash_prefix logstash_dateformat time_key utc_index index_name tag_key type_name id_key parent_key routing_key write_operation]
         
     | 
| 
      
 14 
     | 
    
         
            +
                DYNAMIC_PARAM_SYMBOLS = DYNAMIC_PARAM_NAMES.map { |n| "@#{n}".to_sym }
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                RequestInfo = Struct.new(:host, :index)
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                attr_reader :dynamic_config
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def configure(conf)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  super
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  # evaluate all configurations here
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @dynamic_config = {}
         
     | 
| 
      
 25 
     | 
    
         
            +
                  DYNAMIC_PARAM_SYMBOLS.each_with_index { |var, i|
         
     | 
| 
      
 26 
     | 
    
         
            +
                    value = expand_param(self.instance_variable_get(var), nil, nil, nil)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    key = DYNAMIC_PARAM_NAMES[i]
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @dynamic_config[key] = value.to_s
         
     | 
| 
      
 29 
     | 
    
         
            +
                  }
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # end eval all configs
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def create_meta_config_map
         
     | 
| 
      
 34 
     | 
    
         
            +
                  {'id_key' => '_id', 'parent_key' => '_parent', 'routing_key' => @routing_key_name}
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def client(host = nil)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # check here to see if we already have a client connection for the given host
         
     | 
| 
      
 40 
     | 
    
         
            +
                  connection_options = get_connection_options(host)
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  @_es = nil unless is_existing_connection(connection_options[:hosts])
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  @_es ||= begin
         
     | 
| 
      
 45 
     | 
    
         
            +
                    @current_config = connection_options[:hosts].clone
         
     | 
| 
      
 46 
     | 
    
         
            +
                    adapter_conf = lambda {|f| f.adapter @http_backend, @backend_options }
         
     | 
| 
      
 47 
     | 
    
         
            +
                    transport = Elasticsearch::Transport::Transport::HTTP::Faraday.new(connection_options.merge(
         
     | 
| 
      
 48 
     | 
    
         
            +
                                                                                        options: {
         
     | 
| 
      
 49 
     | 
    
         
            +
                                                                                          reload_connections: @reload_connections,
         
     | 
| 
      
 50 
     | 
    
         
            +
                                                                                          reload_on_failure: @reload_on_failure,
         
     | 
| 
      
 51 
     | 
    
         
            +
                                                                                          resurrect_after: @resurrect_after,
         
     | 
| 
      
 52 
     | 
    
         
            +
                                                                                          logger: @transport_logger,
         
     | 
| 
      
 53 
     | 
    
         
            +
                                                                                          transport_options: {
         
     | 
| 
      
 54 
     | 
    
         
            +
                                                                                            headers: { 'Content-Type' => @content_type.to_s },
         
     | 
| 
      
 55 
     | 
    
         
            +
                                                                                            request: { timeout: @request_timeout },
         
     | 
| 
      
 56 
     | 
    
         
            +
                                                                                            ssl: { verify: @ssl_verify, ca_file: @ca_file, version: @ssl_version }
         
     | 
| 
      
 57 
     | 
    
         
            +
                                                                                          },
         
     | 
| 
      
 58 
     | 
    
         
            +
                                                                                          http: {
         
     | 
| 
      
 59 
     | 
    
         
            +
                                                                                            user: @user,
         
     | 
| 
      
 60 
     | 
    
         
            +
                                                                                            password: @password
         
     | 
| 
      
 61 
     | 
    
         
            +
                                                                                          }
         
     | 
| 
      
 62 
     | 
    
         
            +
                                                                                        }), &adapter_conf)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    Elasticsearch::Client.new transport: transport
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                def get_connection_options(con_host)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  raise "`password` must be present if `user` is present" if @user && !@password
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                  hosts = if con_host || @hosts
         
     | 
| 
      
 71 
     | 
    
         
            +
                    (con_host || @hosts).split(',').map do |host_str|
         
     | 
| 
      
 72 
     | 
    
         
            +
                      # Support legacy hosts format host:port,host:port,host:port...
         
     | 
| 
      
 73 
     | 
    
         
            +
                      if host_str.match(%r{^[^:]+(\:\d+)?$})
         
     | 
| 
      
 74 
     | 
    
         
            +
                        {
         
     | 
| 
      
 75 
     | 
    
         
            +
                          host:   host_str.split(':')[0],
         
     | 
| 
      
 76 
     | 
    
         
            +
                          port:   (host_str.split(':')[1] || @port).to_i,
         
     | 
| 
      
 77 
     | 
    
         
            +
                          scheme: @scheme.to_s
         
     | 
| 
      
 78 
     | 
    
         
            +
                        }
         
     | 
| 
      
 79 
     | 
    
         
            +
                      else
         
     | 
| 
      
 80 
     | 
    
         
            +
                        # New hosts format expects URLs such as http://logs.foo.com,https://john:pass@logs2.foo.com/elastic
         
     | 
| 
      
 81 
     | 
    
         
            +
                        uri = URI(get_escaped_userinfo(host_str))
         
     | 
| 
      
 82 
     | 
    
         
            +
                        %w(user password path).inject(host: uri.host, port: uri.port, scheme: uri.scheme) do |hash, key|
         
     | 
| 
      
 83 
     | 
    
         
            +
                          hash[key.to_sym] = uri.public_send(key) unless uri.public_send(key).nil? || uri.public_send(key) == ''
         
     | 
| 
      
 84 
     | 
    
         
            +
                          hash
         
     | 
| 
      
 85 
     | 
    
         
            +
                        end
         
     | 
| 
      
 86 
     | 
    
         
            +
                      end
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end.compact
         
     | 
| 
      
 88 
     | 
    
         
            +
                  else
         
     | 
| 
      
 89 
     | 
    
         
            +
                    [{host: @host, port: @port.to_i, scheme: @scheme.to_s}]
         
     | 
| 
      
 90 
     | 
    
         
            +
                  end.each do |host|
         
     | 
| 
      
 91 
     | 
    
         
            +
                    host.merge!(user: @user, password: @password) if !host[:user] && @user
         
     | 
| 
      
 92 
     | 
    
         
            +
                    host.merge!(path: @path) if !host[:path] && @path
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                  {
         
     | 
| 
      
 96 
     | 
    
         
            +
                    hosts: hosts
         
     | 
| 
      
 97 
     | 
    
         
            +
                  }
         
     | 
| 
      
 98 
     | 
    
         
            +
                end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                def connection_options_description(host)
         
     | 
| 
      
 101 
     | 
    
         
            +
                  get_connection_options(host)[:hosts].map do |host_info|
         
     | 
| 
      
 102 
     | 
    
         
            +
                    attributes = host_info.dup
         
     | 
| 
      
 103 
     | 
    
         
            +
                    attributes[:password] = 'obfuscated' if attributes.has_key?(:password)
         
     | 
| 
      
 104 
     | 
    
         
            +
                    attributes.inspect
         
     | 
| 
      
 105 
     | 
    
         
            +
                  end.join(', ')
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                def multi_workers_ready?
         
     | 
| 
      
 109 
     | 
    
         
            +
                  true
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                def write(chunk)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  bulk_message = Hash.new { |h,k| h[k] = '' }
         
     | 
| 
      
 114 
     | 
    
         
            +
                  dynamic_conf = @dynamic_config.clone
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  headers = {
         
     | 
| 
      
 117 
     | 
    
         
            +
                    UPDATE_OP => {},
         
     | 
| 
      
 118 
     | 
    
         
            +
                    UPSERT_OP => {},
         
     | 
| 
      
 119 
     | 
    
         
            +
                    CREATE_OP => {},
         
     | 
| 
      
 120 
     | 
    
         
            +
                    INDEX_OP => {}
         
     | 
| 
      
 121 
     | 
    
         
            +
                  }
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                  tag = chunk.metadata.tag
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                  chunk.msgpack_each do |time, record|
         
     | 
| 
      
 126 
     | 
    
         
            +
                    next unless record.is_a? Hash
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 129 
     | 
    
         
            +
                      # evaluate all configurations here
         
     | 
| 
      
 130 
     | 
    
         
            +
                      DYNAMIC_PARAM_SYMBOLS.each_with_index { |var, i|
         
     | 
| 
      
 131 
     | 
    
         
            +
                        k = DYNAMIC_PARAM_NAMES[i]
         
     | 
| 
      
 132 
     | 
    
         
            +
                        v = self.instance_variable_get(var)
         
     | 
| 
      
 133 
     | 
    
         
            +
                        # check here to determine if we should evaluate
         
     | 
| 
      
 134 
     | 
    
         
            +
                        if dynamic_conf[k] != v
         
     | 
| 
      
 135 
     | 
    
         
            +
                          value = expand_param(v, tag, time, record)
         
     | 
| 
      
 136 
     | 
    
         
            +
                          dynamic_conf[k] = value
         
     | 
| 
      
 137 
     | 
    
         
            +
                        end
         
     | 
| 
      
 138 
     | 
    
         
            +
                      }
         
     | 
| 
      
 139 
     | 
    
         
            +
                    # end eval all configs
         
     | 
| 
      
 140 
     | 
    
         
            +
                    rescue => e
         
     | 
| 
      
 141 
     | 
    
         
            +
                      # handle dynamic parameters misconfigurations
         
     | 
| 
      
 142 
     | 
    
         
            +
                      router.emit_error_event(tag, time, record, e)
         
     | 
| 
      
 143 
     | 
    
         
            +
                      next
         
     | 
| 
      
 144 
     | 
    
         
            +
                    end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                    if eval_or_val(dynamic_conf['logstash_format']) || eval_or_val(dynamic_conf['include_timestamp'])
         
     | 
| 
      
 147 
     | 
    
         
            +
                      if record.has_key?("@timestamp")
         
     | 
| 
      
 148 
     | 
    
         
            +
                        time = Time.parse record["@timestamp"]
         
     | 
| 
      
 149 
     | 
    
         
            +
                      elsif record.has_key?(dynamic_conf['time_key'])
         
     | 
| 
      
 150 
     | 
    
         
            +
                        time = Time.parse record[dynamic_conf['time_key']]
         
     | 
| 
      
 151 
     | 
    
         
            +
                        record['@timestamp'] = record[dynamic_conf['time_key']] unless time_key_exclude_timestamp
         
     | 
| 
      
 152 
     | 
    
         
            +
                      else
         
     | 
| 
      
 153 
     | 
    
         
            +
                        record.merge!({"@timestamp" => Time.at(time).iso8601(@time_precision)})
         
     | 
| 
      
 154 
     | 
    
         
            +
                      end
         
     | 
| 
      
 155 
     | 
    
         
            +
                    end
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                    if eval_or_val(dynamic_conf['logstash_format'])
         
     | 
| 
      
 158 
     | 
    
         
            +
                      if eval_or_val(dynamic_conf['utc_index'])
         
     | 
| 
      
 159 
     | 
    
         
            +
                        target_index = "#{dynamic_conf['logstash_prefix']}#{@logstash_prefix_separator}#{Time.at(time).getutc.strftime("#{dynamic_conf['logstash_dateformat']}")}"
         
     | 
| 
      
 160 
     | 
    
         
            +
                      else
         
     | 
| 
      
 161 
     | 
    
         
            +
                        target_index = "#{dynamic_conf['logstash_prefix']}#{@logstash_prefix_separator}#{Time.at(time).strftime("#{dynamic_conf['logstash_dateformat']}")}"
         
     | 
| 
      
 162 
     | 
    
         
            +
                      end
         
     | 
| 
      
 163 
     | 
    
         
            +
                    else
         
     | 
| 
      
 164 
     | 
    
         
            +
                      target_index = dynamic_conf['index_name']
         
     | 
| 
      
 165 
     | 
    
         
            +
                    end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                    # Change target_index to lower-case since Elasticsearch doesn't
         
     | 
| 
      
 168 
     | 
    
         
            +
                    # allow upper-case characters in index names.
         
     | 
| 
      
 169 
     | 
    
         
            +
                    target_index = target_index.downcase
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                    if @include_tag_key
         
     | 
| 
      
 172 
     | 
    
         
            +
                      record.merge!(dynamic_conf['tag_key'] => tag)
         
     | 
| 
      
 173 
     | 
    
         
            +
                    end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                    if dynamic_conf['hosts']
         
     | 
| 
      
 176 
     | 
    
         
            +
                      host = dynamic_conf['hosts']
         
     | 
| 
      
 177 
     | 
    
         
            +
                    else
         
     | 
| 
      
 178 
     | 
    
         
            +
                      host = "#{dynamic_conf['host']}:#{dynamic_conf['port']}"
         
     | 
| 
      
 179 
     | 
    
         
            +
                    end
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                    if @include_index_in_url
         
     | 
| 
      
 182 
     | 
    
         
            +
                      key = RequestInfo.new(host, target_index)
         
     | 
| 
      
 183 
     | 
    
         
            +
                      meta = {"_type" => dynamic_conf['type_name']}
         
     | 
| 
      
 184 
     | 
    
         
            +
                    else
         
     | 
| 
      
 185 
     | 
    
         
            +
                      key = RequestInfo.new(host, nil)
         
     | 
| 
      
 186 
     | 
    
         
            +
                      meta = {"_index" => target_index, "_type" => dynamic_conf['type_name']}
         
     | 
| 
      
 187 
     | 
    
         
            +
                    end
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
                    @meta_config_map.each_pair do |config_name, meta_key|
         
     | 
| 
      
 190 
     | 
    
         
            +
                      if dynamic_conf[config_name] && accessor = record_accessor_create(dynamic_conf[config_name])
         
     | 
| 
      
 191 
     | 
    
         
            +
                        if raw_value = accessor.call(record)
         
     | 
| 
      
 192 
     | 
    
         
            +
                          meta[meta_key] = raw_value
         
     | 
| 
      
 193 
     | 
    
         
            +
                        end
         
     | 
| 
      
 194 
     | 
    
         
            +
                      end
         
     | 
| 
      
 195 
     | 
    
         
            +
                    end
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                    if @remove_keys
         
     | 
| 
      
 198 
     | 
    
         
            +
                      @remove_keys.each { |key| record.delete(key) }
         
     | 
| 
      
 199 
     | 
    
         
            +
                    end
         
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
      
 201 
     | 
    
         
            +
                    write_op = dynamic_conf["write_operation"]
         
     | 
| 
      
 202 
     | 
    
         
            +
                    append_record_to_messages(write_op, meta, headers[write_op], record, bulk_message[key])
         
     | 
| 
      
 203 
     | 
    
         
            +
                  end
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                  bulk_message.each do |info, msgs|
         
     | 
| 
      
 206 
     | 
    
         
            +
                    send_bulk(msgs, info.host, info.index) unless msgs.empty?
         
     | 
| 
      
 207 
     | 
    
         
            +
                    msgs.clear
         
     | 
| 
      
 208 
     | 
    
         
            +
                  end
         
     | 
| 
      
 209 
     | 
    
         
            +
                end
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
                def send_bulk(data, host, index)
         
     | 
| 
      
 212 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 213 
     | 
    
         
            +
                    response = client(host).bulk body: data, index: index
         
     | 
| 
      
 214 
     | 
    
         
            +
                    if response['errors']
         
     | 
| 
      
 215 
     | 
    
         
            +
                      log.error "Could not push log to Elasticsearch: #{response}"
         
     | 
| 
      
 216 
     | 
    
         
            +
                    end
         
     | 
| 
      
 217 
     | 
    
         
            +
                  rescue => e
         
     | 
| 
      
 218 
     | 
    
         
            +
                    @_es = nil if @reconnect_on_error
         
     | 
| 
      
 219 
     | 
    
         
            +
                    # FIXME: identify unrecoverable errors and raise UnrecoverableRequestFailure instead
         
     | 
| 
      
 220 
     | 
    
         
            +
                    raise RecoverableRequestFailure, "could not push logs to Elasticsearch cluster (#{connection_options_description(host)}): #{e.message}"
         
     | 
| 
      
 221 
     | 
    
         
            +
                  end
         
     | 
| 
      
 222 
     | 
    
         
            +
                end
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
      
 224 
     | 
    
         
            +
                def eval_or_val(var)
         
     | 
| 
      
 225 
     | 
    
         
            +
                  return var unless var.is_a?(String)
         
     | 
| 
      
 226 
     | 
    
         
            +
                  eval(var)
         
     | 
| 
      
 227 
     | 
    
         
            +
                end
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
                def expand_param(param, tag, time, record)
         
     | 
| 
      
 230 
     | 
    
         
            +
                  # check for '${ ... }'
         
     | 
| 
      
 231 
     | 
    
         
            +
                  #   yes => `eval`
         
     | 
| 
      
 232 
     | 
    
         
            +
                  #   no  => return param
         
     | 
| 
      
 233 
     | 
    
         
            +
                  return param if (param =~ /\${.+}/).nil?
         
     | 
| 
      
 234 
     | 
    
         
            +
             
     | 
| 
      
 235 
     | 
    
         
            +
                  # check for 'tag_parts[]'
         
     | 
| 
      
 236 
     | 
    
         
            +
                    # separated by a delimiter (default '.')
         
     | 
| 
      
 237 
     | 
    
         
            +
                  tag_parts = tag.split(@delimiter) unless (param =~ /tag_parts\[.+\]/).nil? || tag.nil?
         
     | 
| 
      
 238 
     | 
    
         
            +
             
     | 
| 
      
 239 
     | 
    
         
            +
                  # pull out section between ${} then eval
         
     | 
| 
      
 240 
     | 
    
         
            +
                  inner = param.clone
         
     | 
| 
      
 241 
     | 
    
         
            +
                  while inner.match(/\${.+}/)
         
     | 
| 
      
 242 
     | 
    
         
            +
                    to_eval = inner.match(/\${(.+?)}/){$1}
         
     | 
| 
      
 243 
     | 
    
         
            +
             
     | 
| 
      
 244 
     | 
    
         
            +
                    if !(to_eval =~ /record\[.+\]/).nil? && record.nil?
         
     | 
| 
      
 245 
     | 
    
         
            +
                      return to_eval
         
     | 
| 
      
 246 
     | 
    
         
            +
                    elsif !(to_eval =~/tag_parts\[.+\]/).nil? && tag_parts.nil?
         
     | 
| 
      
 247 
     | 
    
         
            +
                      return to_eval
         
     | 
| 
      
 248 
     | 
    
         
            +
                    elsif !(to_eval =~/time/).nil? && time.nil?
         
     | 
| 
      
 249 
     | 
    
         
            +
                      return to_eval
         
     | 
| 
      
 250 
     | 
    
         
            +
                    else
         
     | 
| 
      
 251 
     | 
    
         
            +
                      inner.sub!(/\${.+?}/, eval( to_eval ))
         
     | 
| 
      
 252 
     | 
    
         
            +
                    end
         
     | 
| 
      
 253 
     | 
    
         
            +
                  end
         
     | 
| 
      
 254 
     | 
    
         
            +
                  inner
         
     | 
| 
      
 255 
     | 
    
         
            +
                end
         
     | 
| 
      
 256 
     | 
    
         
            +
             
     | 
| 
      
 257 
     | 
    
         
            +
                def is_valid_expand_param_type(param)
         
     | 
| 
      
 258 
     | 
    
         
            +
                  return false if [:@buffer_type].include?(param)
         
     | 
| 
      
 259 
     | 
    
         
            +
                  return self.instance_variable_get(param).is_a?(String)
         
     | 
| 
      
 260 
     | 
    
         
            +
                end
         
     | 
| 
      
 261 
     | 
    
         
            +
              end
         
     | 
| 
      
 262 
     | 
    
         
            +
            end
         
     | 
    
        data/test/helper.rb
    ADDED
    
    | 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'simplecov'
         
     | 
| 
      
 2 
     | 
    
         
            +
            SimpleCov.start do
         
     | 
| 
      
 3 
     | 
    
         
            +
              add_filter do |src|
         
     | 
| 
      
 4 
     | 
    
         
            +
                !(src.filename =~ /^#{SimpleCov.root}\/lib/)
         
     | 
| 
      
 5 
     | 
    
         
            +
              end
         
     | 
| 
      
 6 
     | 
    
         
            +
            end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            require 'coveralls'
         
     | 
| 
      
 9 
     | 
    
         
            +
            Coveralls.wear!
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            # needs to be after simplecov but before test/unit, because fluentd sets default
         
     | 
| 
      
 12 
     | 
    
         
            +
            # encoding to ASCII-8BIT, but coverall might load git data which could contain a
         
     | 
| 
      
 13 
     | 
    
         
            +
            # UTF-8 character
         
     | 
| 
      
 14 
     | 
    
         
            +
            at_exit do
         
     | 
| 
      
 15 
     | 
    
         
            +
              Encoding.default_internal = 'UTF-8' if defined?(Encoding) && Encoding.respond_to?(:default_internal)
         
     | 
| 
      
 16 
     | 
    
         
            +
              Encoding.default_external = 'UTF-8' if defined?(Encoding) && Encoding.respond_to?(:default_external)
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            require 'test/unit'
         
     | 
| 
      
 20 
     | 
    
         
            +
            require 'fluent/test'
         
     | 
| 
      
 21 
     | 
    
         
            +
            require 'minitest/pride'
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            require 'webmock/test_unit'
         
     | 
| 
      
 24 
     | 
    
         
            +
            WebMock.disable_net_connect!
         
     | 
| 
         @@ -0,0 +1,525 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'fluent/plugin/out_elasticsearch'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'fluent/plugin/elasticsearch_error_handler'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            class TestElasticsearchErrorHandler < Test::Unit::TestCase
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              class TestPlugin
         
     | 
| 
      
 9 
     | 
    
         
            +
                attr_reader :log
         
     | 
| 
      
 10 
     | 
    
         
            +
                attr_reader :write_operation, :error_events
         
     | 
| 
      
 11 
     | 
    
         
            +
                attr_accessor :unrecoverable_error_types
         
     | 
| 
      
 12 
     | 
    
         
            +
                attr_accessor :log_es_400_reason
         
     | 
| 
      
 13 
     | 
    
         
            +
                def initialize(log, log_es_400_reason = false)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @log = log
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @write_operation = 'index'
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @error_events = []
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @unrecoverable_error_types = ["out_of_memory_error", "es_rejected_execution_exception"]
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @log_es_400_reason = log_es_400_reason
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def router
         
     | 
| 
      
 22 
     | 
    
         
            +
                  self
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def emit_error_event(tag, time, record, e)
         
     | 
| 
      
 26 
     | 
    
         
            +
                   @error_events << {:tag => tag, :time=>time, :record=>record, :error=>e}
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def process_message(tag, meta, header, time, record, extracted_values)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  return [meta, header, record]
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def append_record_to_messages(op, meta, header, record, msgs)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  if record.has_key?('raise') && record['raise']
         
     | 
| 
      
 35 
     | 
    
         
            +
                    raise Exception('process_message')
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
                  return true
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              class MockChunk
         
     | 
| 
      
 42 
     | 
    
         
            +
                def initialize(records)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  @records = records
         
     | 
| 
      
 44 
     | 
    
         
            +
                  @index = 0
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
                def msgpack_each
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @records.each { |item| yield(item[:time],item[:record]) }
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
              def setup
         
     | 
| 
      
 52 
     | 
    
         
            +
                Fluent::Test.setup
         
     | 
| 
      
 53 
     | 
    
         
            +
                @log_device = Fluent::Test::DummyLogDevice.new
         
     | 
| 
      
 54 
     | 
    
         
            +
                dl_opts = {:log_level => ServerEngine::DaemonLogger::INFO}
         
     | 
| 
      
 55 
     | 
    
         
            +
                logger = ServerEngine::DaemonLogger.new(@log_device, dl_opts)
         
     | 
| 
      
 56 
     | 
    
         
            +
                @log = Fluent::Log.new(logger)
         
     | 
| 
      
 57 
     | 
    
         
            +
                @plugin = TestPlugin.new(@log)
         
     | 
| 
      
 58 
     | 
    
         
            +
                @handler = Fluent::Plugin::ElasticsearchErrorHandler.new(@plugin)
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
              def parse_response(value)
         
     | 
| 
      
 62 
     | 
    
         
            +
                JSON.parse(value)
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
              class TEST400ResponseReason < self
         
     | 
| 
      
 66 
     | 
    
         
            +
                def setup
         
     | 
| 
      
 67 
     | 
    
         
            +
                  Fluent::Test.setup
         
     | 
| 
      
 68 
     | 
    
         
            +
                  @log_device = Fluent::Test::DummyLogDevice.new
         
     | 
| 
      
 69 
     | 
    
         
            +
                  dl_opts = {:log_level => ServerEngine::DaemonLogger::DEBUG}
         
     | 
| 
      
 70 
     | 
    
         
            +
                  logger = ServerEngine::DaemonLogger.new(@log_device, dl_opts)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  @log = Fluent::Log.new(logger)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  @plugin = TestPlugin.new(@log)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  @handler = Fluent::Plugin::ElasticsearchErrorHandler.new(@plugin)
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                def test_400_responses_reason_log
         
     | 
| 
      
 77 
     | 
    
         
            +
                  records = [{time: 123, record: {"foo" => "bar", '_id' => 'abc'}}]
         
     | 
| 
      
 78 
     | 
    
         
            +
                  response = parse_response(%({
         
     | 
| 
      
 79 
     | 
    
         
            +
                  "took" : 0,
         
     | 
| 
      
 80 
     | 
    
         
            +
                  "errors" : true,
         
     | 
| 
      
 81 
     | 
    
         
            +
                  "items" : [
         
     | 
| 
      
 82 
     | 
    
         
            +
                    {
         
     | 
| 
      
 83 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 84 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 85 
     | 
    
         
            +
                        "status" : 400,
         
     | 
| 
      
 86 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 87 
     | 
    
         
            +
                          "type"  : "mapper_parsing_exception",
         
     | 
| 
      
 88 
     | 
    
         
            +
                          "reason" : "failed to parse"
         
     | 
| 
      
 89 
     | 
    
         
            +
                        }
         
     | 
| 
      
 90 
     | 
    
         
            +
                      }
         
     | 
| 
      
 91 
     | 
    
         
            +
                    }
         
     | 
| 
      
 92 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 93 
     | 
    
         
            +
                 }))
         
     | 
| 
      
 94 
     | 
    
         
            +
                  chunk = MockChunk.new(records)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  dummy_extracted_values = []
         
     | 
| 
      
 96 
     | 
    
         
            +
                  @handler.handle_error(response, 'atag', chunk, records.length, dummy_extracted_values)
         
     | 
| 
      
 97 
     | 
    
         
            +
                  assert_equal(1, @plugin.error_events.size)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  expected_log = "failed to parse"
         
     | 
| 
      
 99 
     | 
    
         
            +
                  exception_message = @plugin.error_events.first[:error].message
         
     | 
| 
      
 100 
     | 
    
         
            +
                  assert_true(exception_message.include?(expected_log),
         
     | 
| 
      
 101 
     | 
    
         
            +
                              "Exception do not contain '#{exception_message}' '#{expected_log}'")
         
     | 
| 
      
 102 
     | 
    
         
            +
                  assert_true(@plugin.error_events[0][:error].respond_to?(:backtrace))
         
     | 
| 
      
 103 
     | 
    
         
            +
                end
         
     | 
| 
      
 104 
     | 
    
         
            +
              end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
              class TEST400ResponseReasonNoDebug < self
         
     | 
| 
      
 107 
     | 
    
         
            +
                def setup
         
     | 
| 
      
 108 
     | 
    
         
            +
                  Fluent::Test.setup
         
     | 
| 
      
 109 
     | 
    
         
            +
                  @log_device = Fluent::Test::DummyLogDevice.new
         
     | 
| 
      
 110 
     | 
    
         
            +
                  dl_opts = {:log_level => ServerEngine::DaemonLogger::INFO}
         
     | 
| 
      
 111 
     | 
    
         
            +
                  logger = ServerEngine::DaemonLogger.new(@log_device, dl_opts)
         
     | 
| 
      
 112 
     | 
    
         
            +
                  @log = Fluent::Log.new(logger)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  @plugin = TestPlugin.new(@log)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  @handler = Fluent::Plugin::ElasticsearchErrorHandler.new(@plugin)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  @plugin.log_es_400_reason = true
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                def test_400_responses_reason_log
         
     | 
| 
      
 119 
     | 
    
         
            +
                  records = [{time: 123, record: {"foo" => "bar", '_id' => 'abc'}}]
         
     | 
| 
      
 120 
     | 
    
         
            +
                  response = parse_response(%({
         
     | 
| 
      
 121 
     | 
    
         
            +
                  "took" : 0,
         
     | 
| 
      
 122 
     | 
    
         
            +
                  "errors" : true,
         
     | 
| 
      
 123 
     | 
    
         
            +
                  "items" : [
         
     | 
| 
      
 124 
     | 
    
         
            +
                    {
         
     | 
| 
      
 125 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 126 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 127 
     | 
    
         
            +
                        "status" : 400,
         
     | 
| 
      
 128 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 129 
     | 
    
         
            +
                          "type"  : "mapper_parsing_exception",
         
     | 
| 
      
 130 
     | 
    
         
            +
                          "reason" : "failed to parse"
         
     | 
| 
      
 131 
     | 
    
         
            +
                        }
         
     | 
| 
      
 132 
     | 
    
         
            +
                      }
         
     | 
| 
      
 133 
     | 
    
         
            +
                    }
         
     | 
| 
      
 134 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 135 
     | 
    
         
            +
                 }))
         
     | 
| 
      
 136 
     | 
    
         
            +
                  chunk = MockChunk.new(records)
         
     | 
| 
      
 137 
     | 
    
         
            +
                  dummy_extracted_values = []
         
     | 
| 
      
 138 
     | 
    
         
            +
                  @handler.handle_error(response, 'atag', chunk, records.length, dummy_extracted_values)
         
     | 
| 
      
 139 
     | 
    
         
            +
                  assert_equal(1, @plugin.error_events.size)
         
     | 
| 
      
 140 
     | 
    
         
            +
                  expected_log = "failed to parse"
         
     | 
| 
      
 141 
     | 
    
         
            +
                  exception_message = @plugin.error_events.first[:error].message
         
     | 
| 
      
 142 
     | 
    
         
            +
                  assert_true(exception_message.include?(expected_log),
         
     | 
| 
      
 143 
     | 
    
         
            +
                              "Exception do not contain '#{exception_message}' '#{expected_log}'")
         
     | 
| 
      
 144 
     | 
    
         
            +
                  assert_true(@plugin.error_events[0][:error].respond_to?(:backtrace))
         
     | 
| 
      
 145 
     | 
    
         
            +
                end
         
     | 
| 
      
 146 
     | 
    
         
            +
              end
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
              def test_nil_items_responses
         
     | 
| 
      
 149 
     | 
    
         
            +
                records = [{time: 123, record: {"foo" => "bar", '_id' => 'abc'}}]
         
     | 
| 
      
 150 
     | 
    
         
            +
                response = parse_response(%({
         
     | 
| 
      
 151 
     | 
    
         
            +
                  "took" : 0,
         
     | 
| 
      
 152 
     | 
    
         
            +
                  "errors" : true,
         
     | 
| 
      
 153 
     | 
    
         
            +
                  "items" : [{}]
         
     | 
| 
      
 154 
     | 
    
         
            +
                 }))
         
     | 
| 
      
 155 
     | 
    
         
            +
                chunk = MockChunk.new(records)
         
     | 
| 
      
 156 
     | 
    
         
            +
                dummy_extracted_values = []
         
     | 
| 
      
 157 
     | 
    
         
            +
                @handler.handle_error(response, 'atag', chunk, records.length, dummy_extracted_values)
         
     | 
| 
      
 158 
     | 
    
         
            +
                assert_equal(0, @plugin.error_events.size)
         
     | 
| 
      
 159 
     | 
    
         
            +
                assert_nil(@plugin.error_events[0])
         
     | 
| 
      
 160 
     | 
    
         
            +
              end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
              def test_blocked_items_responses
         
     | 
| 
      
 163 
     | 
    
         
            +
                records = [{time: 123, record: {"foo" => "bar", '_id' => 'abc'}}]
         
     | 
| 
      
 164 
     | 
    
         
            +
                response = parse_response(%({
         
     | 
| 
      
 165 
     | 
    
         
            +
                  "took" : 0,
         
     | 
| 
      
 166 
     | 
    
         
            +
                  "errors" : true,
         
     | 
| 
      
 167 
     | 
    
         
            +
                  "items" : [
         
     | 
| 
      
 168 
     | 
    
         
            +
                    {
         
     | 
| 
      
 169 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 170 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 171 
     | 
    
         
            +
                        "status" : 503,
         
     | 
| 
      
 172 
     | 
    
         
            +
                        "error" : "ClusterBlockException[blocked by: [SERVICE_UNAVAILABLE/1/state not recovered / initialized];]"
         
     | 
| 
      
 173 
     | 
    
         
            +
                      }
         
     | 
| 
      
 174 
     | 
    
         
            +
                    }
         
     | 
| 
      
 175 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 176 
     | 
    
         
            +
                 }))
         
     | 
| 
      
 177 
     | 
    
         
            +
                chunk = MockChunk.new(records)
         
     | 
| 
      
 178 
     | 
    
         
            +
                dummy_extracted_values = []
         
     | 
| 
      
 179 
     | 
    
         
            +
                @handler.handle_error(response, 'atag', chunk, records.length, dummy_extracted_values)
         
     | 
| 
      
 180 
     | 
    
         
            +
                assert_equal(1, @plugin.error_events.size)
         
     | 
| 
      
 181 
     | 
    
         
            +
                assert_true(@plugin.error_events[0][:error].respond_to?(:backtrace))
         
     | 
| 
      
 182 
     | 
    
         
            +
              end
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
              def test_dlq_400_responses
         
     | 
| 
      
 185 
     | 
    
         
            +
                records = [{time: 123, record: {"foo" => "bar", '_id' => 'abc'}}]
         
     | 
| 
      
 186 
     | 
    
         
            +
                response = parse_response(%({
         
     | 
| 
      
 187 
     | 
    
         
            +
                  "took" : 0,
         
     | 
| 
      
 188 
     | 
    
         
            +
                  "errors" : true,
         
     | 
| 
      
 189 
     | 
    
         
            +
                  "items" : [
         
     | 
| 
      
 190 
     | 
    
         
            +
                    {
         
     | 
| 
      
 191 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 192 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 193 
     | 
    
         
            +
                        "status" : 400,
         
     | 
| 
      
 194 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 195 
     | 
    
         
            +
                          "reason":"unrecognized error"
         
     | 
| 
      
 196 
     | 
    
         
            +
                        }
         
     | 
| 
      
 197 
     | 
    
         
            +
                    }
         
     | 
| 
      
 198 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 199 
     | 
    
         
            +
                 }))
         
     | 
| 
      
 200 
     | 
    
         
            +
                chunk = MockChunk.new(records)
         
     | 
| 
      
 201 
     | 
    
         
            +
                dummy_extracted_values = []
         
     | 
| 
      
 202 
     | 
    
         
            +
                @handler.handle_error(response, 'atag', chunk, records.length, dummy_extracted_values)
         
     | 
| 
      
 203 
     | 
    
         
            +
                assert_equal(1, @plugin.error_events.size)
         
     | 
| 
      
 204 
     | 
    
         
            +
                assert_true(@plugin.error_events[0][:error].respond_to?(:backtrace))
         
     | 
| 
      
 205 
     | 
    
         
            +
              end
         
     | 
| 
      
 206 
     | 
    
         
            +
             
     | 
| 
      
 207 
     | 
    
         
            +
              def test_out_of_memory_responses
         
     | 
| 
      
 208 
     | 
    
         
            +
                records = [{time: 123, record: {"foo" => "bar", '_id' => 'abc'}}]
         
     | 
| 
      
 209 
     | 
    
         
            +
                response = parse_response(%({
         
     | 
| 
      
 210 
     | 
    
         
            +
                  "took" : 0,
         
     | 
| 
      
 211 
     | 
    
         
            +
                  "errors" : true,
         
     | 
| 
      
 212 
     | 
    
         
            +
                  "items" : [
         
     | 
| 
      
 213 
     | 
    
         
            +
                    {
         
     | 
| 
      
 214 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 215 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 216 
     | 
    
         
            +
                        "status" : 500,
         
     | 
| 
      
 217 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 218 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 219 
     | 
    
         
            +
                          "type" : "out_of_memory_error",
         
     | 
| 
      
 220 
     | 
    
         
            +
                          "reason":"Java heap space"
         
     | 
| 
      
 221 
     | 
    
         
            +
                        }
         
     | 
| 
      
 222 
     | 
    
         
            +
                      }
         
     | 
| 
      
 223 
     | 
    
         
            +
                    }
         
     | 
| 
      
 224 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 225 
     | 
    
         
            +
                 }))
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
                  chunk = MockChunk.new(records)
         
     | 
| 
      
 228 
     | 
    
         
            +
                  dummy_extracted_values = []
         
     | 
| 
      
 229 
     | 
    
         
            +
                assert_raise(Fluent::Plugin::ElasticsearchErrorHandler::ElasticsearchRequestAbortError) do
         
     | 
| 
      
 230 
     | 
    
         
            +
                  @handler.handle_error(response, 'atag', chunk, records.length, dummy_extracted_values)
         
     | 
| 
      
 231 
     | 
    
         
            +
                end
         
     | 
| 
      
 232 
     | 
    
         
            +
              end
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
              def test_es_rejected_execution_exception_responses
         
     | 
| 
      
 235 
     | 
    
         
            +
                records = [{time: 123, record: {"foo" => "bar", '_id' => 'abc'}}]
         
     | 
| 
      
 236 
     | 
    
         
            +
                response = parse_response(%({
         
     | 
| 
      
 237 
     | 
    
         
            +
                  "took" : 0,
         
     | 
| 
      
 238 
     | 
    
         
            +
                  "errors" : true,
         
     | 
| 
      
 239 
     | 
    
         
            +
                  "items" : [
         
     | 
| 
      
 240 
     | 
    
         
            +
                    {
         
     | 
| 
      
 241 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 242 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 243 
     | 
    
         
            +
                        "status" : 429,
         
     | 
| 
      
 244 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 245 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 246 
     | 
    
         
            +
                          "type" : "es_rejected_execution_exception",
         
     | 
| 
      
 247 
     | 
    
         
            +
                          "reason":"rejected execution of org.elasticsearch.transport.TransportService"
         
     | 
| 
      
 248 
     | 
    
         
            +
                        }
         
     | 
| 
      
 249 
     | 
    
         
            +
                      }
         
     | 
| 
      
 250 
     | 
    
         
            +
                    }
         
     | 
| 
      
 251 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 252 
     | 
    
         
            +
                 }))
         
     | 
| 
      
 253 
     | 
    
         
            +
             
     | 
| 
      
 254 
     | 
    
         
            +
                  chunk = MockChunk.new(records)
         
     | 
| 
      
 255 
     | 
    
         
            +
                  dummy_extracted_values = []
         
     | 
| 
      
 256 
     | 
    
         
            +
                assert_raise(Fluent::Plugin::ElasticsearchErrorHandler::ElasticsearchRequestAbortError) do
         
     | 
| 
      
 257 
     | 
    
         
            +
                  @handler.handle_error(response, 'atag', chunk, records.length, dummy_extracted_values)
         
     | 
| 
      
 258 
     | 
    
         
            +
                end
         
     | 
| 
      
 259 
     | 
    
         
            +
              end
         
     | 
| 
      
 260 
     | 
    
         
            +
             
     | 
| 
      
 261 
     | 
    
         
            +
              def test_es_rejected_execution_exception_responses_as_not_error
         
     | 
| 
      
 262 
     | 
    
         
            +
                plugin = TestPlugin.new(@log)
         
     | 
| 
      
 263 
     | 
    
         
            +
                plugin.unrecoverable_error_types = ["out_of_memory_error"]
         
     | 
| 
      
 264 
     | 
    
         
            +
                handler = Fluent::Plugin::ElasticsearchErrorHandler.new(plugin)
         
     | 
| 
      
 265 
     | 
    
         
            +
                records = [{time: 123, record: {"foo" => "bar", '_id' => 'abc'}}]
         
     | 
| 
      
 266 
     | 
    
         
            +
                response = parse_response(%({
         
     | 
| 
      
 267 
     | 
    
         
            +
                  "took" : 0,
         
     | 
| 
      
 268 
     | 
    
         
            +
                  "errors" : true,
         
     | 
| 
      
 269 
     | 
    
         
            +
                  "items" : [
         
     | 
| 
      
 270 
     | 
    
         
            +
                    {
         
     | 
| 
      
 271 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 272 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 273 
     | 
    
         
            +
                        "status" : 429,
         
     | 
| 
      
 274 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 275 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 276 
     | 
    
         
            +
                          "type" : "es_rejected_execution_exception",
         
     | 
| 
      
 277 
     | 
    
         
            +
                          "reason":"rejected execution of org.elasticsearch.transport.TransportService"
         
     | 
| 
      
 278 
     | 
    
         
            +
                        }
         
     | 
| 
      
 279 
     | 
    
         
            +
                      }
         
     | 
| 
      
 280 
     | 
    
         
            +
                    }
         
     | 
| 
      
 281 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 282 
     | 
    
         
            +
                 }))
         
     | 
| 
      
 283 
     | 
    
         
            +
             
     | 
| 
      
 284 
     | 
    
         
            +
                begin
         
     | 
| 
      
 285 
     | 
    
         
            +
                  failed = false
         
     | 
| 
      
 286 
     | 
    
         
            +
                  chunk = MockChunk.new(records)
         
     | 
| 
      
 287 
     | 
    
         
            +
                  dummy_extracted_values = []
         
     | 
| 
      
 288 
     | 
    
         
            +
                  handler.handle_error(response, 'atag', chunk, response['items'].length, dummy_extracted_values)
         
     | 
| 
      
 289 
     | 
    
         
            +
                rescue Fluent::Plugin::ElasticsearchErrorHandler::ElasticsearchRequestAbortError, Fluent::Plugin::ElasticsearchOutput::RetryStreamError=>e
         
     | 
| 
      
 290 
     | 
    
         
            +
                  failed = true
         
     | 
| 
      
 291 
     | 
    
         
            +
                  records = [].tap do |records|
         
     | 
| 
      
 292 
     | 
    
         
            +
                    next unless e.respond_to?(:retry_stream)
         
     | 
| 
      
 293 
     | 
    
         
            +
                    e.retry_stream.each {|time, record| records << record}
         
     | 
| 
      
 294 
     | 
    
         
            +
                  end
         
     | 
| 
      
 295 
     | 
    
         
            +
                  # should retry chunk when unrecoverable error is not thrown
         
     | 
| 
      
 296 
     | 
    
         
            +
                  assert_equal 1, records.length
         
     | 
| 
      
 297 
     | 
    
         
            +
                end
         
     | 
| 
      
 298 
     | 
    
         
            +
                assert_true failed
         
     | 
| 
      
 299 
     | 
    
         
            +
              end
         
     | 
| 
      
 300 
     | 
    
         
            +
             
     | 
| 
      
 301 
     | 
    
         
            +
              def test_retry_error
         
     | 
| 
      
 302 
     | 
    
         
            +
                records = []
         
     | 
| 
      
 303 
     | 
    
         
            +
                error_records = Hash.new(false)
         
     | 
| 
      
 304 
     | 
    
         
            +
                error_records.merge!({0=>true, 4=>true, 9=>true})
         
     | 
| 
      
 305 
     | 
    
         
            +
                10.times do |i|
         
     | 
| 
      
 306 
     | 
    
         
            +
                  records << {time: 12345, record: {"message"=>"record #{i}","_id"=>i,"raise"=>error_records[i]}}
         
     | 
| 
      
 307 
     | 
    
         
            +
                end
         
     | 
| 
      
 308 
     | 
    
         
            +
                chunk = MockChunk.new(records)
         
     | 
| 
      
 309 
     | 
    
         
            +
             
     | 
| 
      
 310 
     | 
    
         
            +
                response = parse_response(%({
         
     | 
| 
      
 311 
     | 
    
         
            +
                  "took" : 1,
         
     | 
| 
      
 312 
     | 
    
         
            +
                  "errors" : true,
         
     | 
| 
      
 313 
     | 
    
         
            +
                  "items" : [
         
     | 
| 
      
 314 
     | 
    
         
            +
                    {
         
     | 
| 
      
 315 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 316 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 317 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 318 
     | 
    
         
            +
                        "_id" : "1",
         
     | 
| 
      
 319 
     | 
    
         
            +
                        "status" : 201
         
     | 
| 
      
 320 
     | 
    
         
            +
                      }
         
     | 
| 
      
 321 
     | 
    
         
            +
                    },
         
     | 
| 
      
 322 
     | 
    
         
            +
                    {
         
     | 
| 
      
 323 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 324 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 325 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 326 
     | 
    
         
            +
                        "_id" : "2",
         
     | 
| 
      
 327 
     | 
    
         
            +
                        "status" : 500,
         
     | 
| 
      
 328 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 329 
     | 
    
         
            +
                          "type" : "some unrecognized type",
         
     | 
| 
      
 330 
     | 
    
         
            +
                          "reason":"unrecognized error"
         
     | 
| 
      
 331 
     | 
    
         
            +
                        }
         
     | 
| 
      
 332 
     | 
    
         
            +
                      }
         
     | 
| 
      
 333 
     | 
    
         
            +
                    },
         
     | 
| 
      
 334 
     | 
    
         
            +
                    {
         
     | 
| 
      
 335 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 336 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 337 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 338 
     | 
    
         
            +
                        "_id" : "3",
         
     | 
| 
      
 339 
     | 
    
         
            +
                        "status" : 409
         
     | 
| 
      
 340 
     | 
    
         
            +
                      }
         
     | 
| 
      
 341 
     | 
    
         
            +
                    },
         
     | 
| 
      
 342 
     | 
    
         
            +
                    {
         
     | 
| 
      
 343 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 344 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 345 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 346 
     | 
    
         
            +
                        "_id" : "5",
         
     | 
| 
      
 347 
     | 
    
         
            +
                        "status" : 500,
         
     | 
| 
      
 348 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 349 
     | 
    
         
            +
                          "reason":"unrecognized error - no type field"
         
     | 
| 
      
 350 
     | 
    
         
            +
                        }
         
     | 
| 
      
 351 
     | 
    
         
            +
                      }
         
     | 
| 
      
 352 
     | 
    
         
            +
                    },
         
     | 
| 
      
 353 
     | 
    
         
            +
                    {
         
     | 
| 
      
 354 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 355 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 356 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 357 
     | 
    
         
            +
                        "_id" : "6",
         
     | 
| 
      
 358 
     | 
    
         
            +
                        "status" : 400,
         
     | 
| 
      
 359 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 360 
     | 
    
         
            +
                          "type" : "mapper_parsing_exception",
         
     | 
| 
      
 361 
     | 
    
         
            +
                          "reason":"failed to parse"
         
     | 
| 
      
 362 
     | 
    
         
            +
                        }
         
     | 
| 
      
 363 
     | 
    
         
            +
                      }
         
     | 
| 
      
 364 
     | 
    
         
            +
                    },
         
     | 
| 
      
 365 
     | 
    
         
            +
                    {
         
     | 
| 
      
 366 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 367 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 368 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 369 
     | 
    
         
            +
                        "_id" : "7",
         
     | 
| 
      
 370 
     | 
    
         
            +
                        "status" : 400,
         
     | 
| 
      
 371 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 372 
     | 
    
         
            +
                          "type" : "some unrecognized type",
         
     | 
| 
      
 373 
     | 
    
         
            +
                          "reason":"unrecognized error"
         
     | 
| 
      
 374 
     | 
    
         
            +
                        }
         
     | 
| 
      
 375 
     | 
    
         
            +
                      }
         
     | 
| 
      
 376 
     | 
    
         
            +
                    },
         
     | 
| 
      
 377 
     | 
    
         
            +
                    {
         
     | 
| 
      
 378 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 379 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 380 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 381 
     | 
    
         
            +
                        "_id" : "8",
         
     | 
| 
      
 382 
     | 
    
         
            +
                        "status" : 500,
         
     | 
| 
      
 383 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 384 
     | 
    
         
            +
                          "type" : "some unrecognized type",
         
     | 
| 
      
 385 
     | 
    
         
            +
                          "reason":"unrecognized error"
         
     | 
| 
      
 386 
     | 
    
         
            +
                        }
         
     | 
| 
      
 387 
     | 
    
         
            +
                      }
         
     | 
| 
      
 388 
     | 
    
         
            +
                    }
         
     | 
| 
      
 389 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 390 
     | 
    
         
            +
                }))
         
     | 
| 
      
 391 
     | 
    
         
            +
             
     | 
| 
      
 392 
     | 
    
         
            +
                begin
         
     | 
| 
      
 393 
     | 
    
         
            +
                  failed = false
         
     | 
| 
      
 394 
     | 
    
         
            +
                  dummy_extracted_values = []
         
     | 
| 
      
 395 
     | 
    
         
            +
                  @handler.handle_error(response, 'atag', chunk, response['items'].length, dummy_extracted_values)
         
     | 
| 
      
 396 
     | 
    
         
            +
                rescue Fluent::Plugin::ElasticsearchErrorHandler::ElasticsearchRequestAbortError, Fluent::Plugin::ElasticsearchOutput::RetryStreamError=>e
         
     | 
| 
      
 397 
     | 
    
         
            +
                  failed = true
         
     | 
| 
      
 398 
     | 
    
         
            +
                  records = [].tap do |records|
         
     | 
| 
      
 399 
     | 
    
         
            +
                    next unless e.respond_to?(:retry_stream)
         
     | 
| 
      
 400 
     | 
    
         
            +
                    e.retry_stream.each {|time, record| records << record}
         
     | 
| 
      
 401 
     | 
    
         
            +
                  end
         
     | 
| 
      
 402 
     | 
    
         
            +
                  assert_equal 2, records.length
         
     | 
| 
      
 403 
     | 
    
         
            +
                  assert_equal 2, records[0]['_id']
         
     | 
| 
      
 404 
     | 
    
         
            +
                  assert_equal 8, records[1]['_id']
         
     | 
| 
      
 405 
     | 
    
         
            +
                  error_ids = @plugin.error_events.collect {|h| h[:record]['_id']}
         
     | 
| 
      
 406 
     | 
    
         
            +
                  assert_equal 3, error_ids.length
         
     | 
| 
      
 407 
     | 
    
         
            +
                  assert_equal [5, 6, 7], error_ids
         
     | 
| 
      
 408 
     | 
    
         
            +
                  @plugin.error_events.collect {|h| h[:error]}.each do |e|
         
     | 
| 
      
 409 
     | 
    
         
            +
                    assert_true e.respond_to?(:backtrace)
         
     | 
| 
      
 410 
     | 
    
         
            +
                  end
         
     | 
| 
      
 411 
     | 
    
         
            +
                end
         
     | 
| 
      
 412 
     | 
    
         
            +
                assert_true failed
         
     | 
| 
      
 413 
     | 
    
         
            +
             
     | 
| 
      
 414 
     | 
    
         
            +
              end
         
     | 
| 
      
 415 
     | 
    
         
            +
             
     | 
| 
      
 416 
     | 
    
         
            +
              def test_unrecoverable_error_included_in_responses
         
     | 
| 
      
 417 
     | 
    
         
            +
                records = []
         
     | 
| 
      
 418 
     | 
    
         
            +
                error_records = Hash.new(false)
         
     | 
| 
      
 419 
     | 
    
         
            +
                error_records.merge!({0=>true, 4=>true, 9=>true})
         
     | 
| 
      
 420 
     | 
    
         
            +
                10.times do |i|
         
     | 
| 
      
 421 
     | 
    
         
            +
                  records << {time: 12345, record: {"message"=>"record #{i}","_id"=>i,"raise"=>error_records[i]}}
         
     | 
| 
      
 422 
     | 
    
         
            +
                end
         
     | 
| 
      
 423 
     | 
    
         
            +
                chunk = MockChunk.new(records)
         
     | 
| 
      
 424 
     | 
    
         
            +
             
     | 
| 
      
 425 
     | 
    
         
            +
                response = parse_response(%({
         
     | 
| 
      
 426 
     | 
    
         
            +
                  "took" : 1,
         
     | 
| 
      
 427 
     | 
    
         
            +
                  "errors" : true,
         
     | 
| 
      
 428 
     | 
    
         
            +
                  "items" : [
         
     | 
| 
      
 429 
     | 
    
         
            +
                    {
         
     | 
| 
      
 430 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 431 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 432 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 433 
     | 
    
         
            +
                        "_id" : "1",
         
     | 
| 
      
 434 
     | 
    
         
            +
                        "status" : 201
         
     | 
| 
      
 435 
     | 
    
         
            +
                      }
         
     | 
| 
      
 436 
     | 
    
         
            +
                    },
         
     | 
| 
      
 437 
     | 
    
         
            +
                    {
         
     | 
| 
      
 438 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 439 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 440 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 441 
     | 
    
         
            +
                        "_id" : "2",
         
     | 
| 
      
 442 
     | 
    
         
            +
                        "status" : 500,
         
     | 
| 
      
 443 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 444 
     | 
    
         
            +
                          "type" : "some unrecognized type",
         
     | 
| 
      
 445 
     | 
    
         
            +
                          "reason":"unrecognized error"
         
     | 
| 
      
 446 
     | 
    
         
            +
                        }
         
     | 
| 
      
 447 
     | 
    
         
            +
                      }
         
     | 
| 
      
 448 
     | 
    
         
            +
                    },
         
     | 
| 
      
 449 
     | 
    
         
            +
                    {
         
     | 
| 
      
 450 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 451 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 452 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 453 
     | 
    
         
            +
                        "_id" : "3",
         
     | 
| 
      
 454 
     | 
    
         
            +
                        "status" : 409
         
     | 
| 
      
 455 
     | 
    
         
            +
                      }
         
     | 
| 
      
 456 
     | 
    
         
            +
                    },
         
     | 
| 
      
 457 
     | 
    
         
            +
                    {
         
     | 
| 
      
 458 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 459 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 460 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 461 
     | 
    
         
            +
                        "_id" : "5",
         
     | 
| 
      
 462 
     | 
    
         
            +
                        "status" : 500,
         
     | 
| 
      
 463 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 464 
     | 
    
         
            +
                          "reason":"unrecognized error - no type field"
         
     | 
| 
      
 465 
     | 
    
         
            +
                        }
         
     | 
| 
      
 466 
     | 
    
         
            +
                      }
         
     | 
| 
      
 467 
     | 
    
         
            +
                    },
         
     | 
| 
      
 468 
     | 
    
         
            +
                    {
         
     | 
| 
      
 469 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 470 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 471 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 472 
     | 
    
         
            +
                        "_id" : "6",
         
     | 
| 
      
 473 
     | 
    
         
            +
                        "status" : 500,
         
     | 
| 
      
 474 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 475 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 476 
     | 
    
         
            +
                          "type" : "out_of_memory_error",
         
     | 
| 
      
 477 
     | 
    
         
            +
                          "reason":"Java heap space"
         
     | 
| 
      
 478 
     | 
    
         
            +
                        }
         
     | 
| 
      
 479 
     | 
    
         
            +
                      }
         
     | 
| 
      
 480 
     | 
    
         
            +
                    },
         
     | 
| 
      
 481 
     | 
    
         
            +
                    {
         
     | 
| 
      
 482 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 483 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 484 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 485 
     | 
    
         
            +
                        "_id" : "7",
         
     | 
| 
      
 486 
     | 
    
         
            +
                        "status" : 400,
         
     | 
| 
      
 487 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 488 
     | 
    
         
            +
                          "type" : "some unrecognized type",
         
     | 
| 
      
 489 
     | 
    
         
            +
                          "reason":"unrecognized error"
         
     | 
| 
      
 490 
     | 
    
         
            +
                        }
         
     | 
| 
      
 491 
     | 
    
         
            +
                      }
         
     | 
| 
      
 492 
     | 
    
         
            +
                    },
         
     | 
| 
      
 493 
     | 
    
         
            +
                    {
         
     | 
| 
      
 494 
     | 
    
         
            +
                      "create" : {
         
     | 
| 
      
 495 
     | 
    
         
            +
                        "_index" : "foo",
         
     | 
| 
      
 496 
     | 
    
         
            +
                        "_type"  : "bar",
         
     | 
| 
      
 497 
     | 
    
         
            +
                        "_id" : "8",
         
     | 
| 
      
 498 
     | 
    
         
            +
                        "status" : 500,
         
     | 
| 
      
 499 
     | 
    
         
            +
                        "error" : {
         
     | 
| 
      
 500 
     | 
    
         
            +
                          "type" : "some unrecognized type",
         
     | 
| 
      
 501 
     | 
    
         
            +
                          "reason":"unrecognized error"
         
     | 
| 
      
 502 
     | 
    
         
            +
                        }
         
     | 
| 
      
 503 
     | 
    
         
            +
                      }
         
     | 
| 
      
 504 
     | 
    
         
            +
                    }
         
     | 
| 
      
 505 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 506 
     | 
    
         
            +
                }))
         
     | 
| 
      
 507 
     | 
    
         
            +
             
     | 
| 
      
 508 
     | 
    
         
            +
                begin
         
     | 
| 
      
 509 
     | 
    
         
            +
                  failed = false
         
     | 
| 
      
 510 
     | 
    
         
            +
                  dummy_extracted_values = []
         
     | 
| 
      
 511 
     | 
    
         
            +
                  @handler.handle_error(response, 'atag', chunk, response['items'].length, dummy_extracted_values)
         
     | 
| 
      
 512 
     | 
    
         
            +
                rescue Fluent::Plugin::ElasticsearchErrorHandler::ElasticsearchRequestAbortError, Fluent::Plugin::ElasticsearchOutput::RetryStreamError=>e
         
     | 
| 
      
 513 
     | 
    
         
            +
                  failed = true
         
     | 
| 
      
 514 
     | 
    
         
            +
                  records = [].tap do |records|
         
     | 
| 
      
 515 
     | 
    
         
            +
                    next unless e.respond_to?(:retry_stream)
         
     | 
| 
      
 516 
     | 
    
         
            +
                    e.retry_stream.each {|time, record| records << record}
         
     | 
| 
      
 517 
     | 
    
         
            +
                  end
         
     | 
| 
      
 518 
     | 
    
         
            +
                  # should drop entire chunk when unrecoverable error response is replied
         
     | 
| 
      
 519 
     | 
    
         
            +
                  assert_equal 0, records.length
         
     | 
| 
      
 520 
     | 
    
         
            +
                end
         
     | 
| 
      
 521 
     | 
    
         
            +
                assert_true failed
         
     | 
| 
      
 522 
     | 
    
         
            +
             
     | 
| 
      
 523 
     | 
    
         
            +
              end
         
     | 
| 
      
 524 
     | 
    
         
            +
             
     | 
| 
      
 525 
     | 
    
         
            +
            end
         
     |