logstash-output-elasticsearch 1.1.0-java → 2.0.0.beta4-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +10 -3
  4. data/README.md +4 -4
  5. data/lib/logstash/outputs/elasticsearch/http_client.rb +144 -0
  6. data/lib/logstash/outputs/elasticsearch.rb +93 -319
  7. data/logstash-output-elasticsearch.gemspec +1 -3
  8. data/spec/es_spec_helper.rb +38 -34
  9. data/spec/integration/outputs/create_spec.rb +56 -0
  10. data/spec/integration/outputs/index_spec.rb +5 -7
  11. data/spec/integration/outputs/retry_spec.rb +118 -126
  12. data/spec/integration/outputs/routing_spec.rb +5 -33
  13. data/spec/integration/outputs/secure_spec.rb +4 -9
  14. data/spec/integration/outputs/templates_spec.rb +85 -91
  15. data/spec/integration/outputs/update_spec.rb +41 -46
  16. data/spec/unit/outputs/elasticsearch/protocol_spec.rb +45 -36
  17. data/spec/unit/outputs/elasticsearch_proxy_spec.rb +3 -4
  18. data/spec/unit/outputs/elasticsearch_spec.rb +2 -151
  19. data/spec/unit/outputs/elasticsearch_ssl_spec.rb +38 -63
  20. metadata +67 -101
  21. data/lib/logstash/outputs/elasticsearch/protocol.rb +0 -333
  22. data/lib/logstash-output-elasticsearch_jars.rb +0 -5
  23. data/spec/integration/outputs/elasticsearch/node_spec.rb +0 -36
  24. data/spec/integration/outputs/transport_create_spec.rb +0 -94
  25. data/vendor/jar-dependencies/runtime-jars/antlr-runtime-3.5.jar +0 -0
  26. data/vendor/jar-dependencies/runtime-jars/asm-4.1.jar +0 -0
  27. data/vendor/jar-dependencies/runtime-jars/asm-commons-4.1.jar +0 -0
  28. data/vendor/jar-dependencies/runtime-jars/elasticsearch-1.7.0.jar +0 -0
  29. data/vendor/jar-dependencies/runtime-jars/lucene-analyzers-common-4.10.4.jar +0 -0
  30. data/vendor/jar-dependencies/runtime-jars/lucene-core-4.10.4.jar +0 -0
  31. data/vendor/jar-dependencies/runtime-jars/lucene-grouping-4.10.4.jar +0 -0
  32. data/vendor/jar-dependencies/runtime-jars/lucene-highlighter-4.10.4.jar +0 -0
  33. data/vendor/jar-dependencies/runtime-jars/lucene-join-4.10.4.jar +0 -0
  34. data/vendor/jar-dependencies/runtime-jars/lucene-memory-4.10.4.jar +0 -0
  35. data/vendor/jar-dependencies/runtime-jars/lucene-misc-4.10.4.jar +0 -0
  36. data/vendor/jar-dependencies/runtime-jars/lucene-queries-4.10.4.jar +0 -0
  37. data/vendor/jar-dependencies/runtime-jars/lucene-queryparser-4.10.4.jar +0 -0
  38. data/vendor/jar-dependencies/runtime-jars/lucene-sandbox-4.10.4.jar +0 -0
  39. data/vendor/jar-dependencies/runtime-jars/lucene-spatial-4.10.4.jar +0 -0
  40. data/vendor/jar-dependencies/runtime-jars/lucene-suggest-4.10.4.jar +0 -0
  41. data/vendor/jar-dependencies/runtime-jars/spatial4j-0.4.1.jar +0 -0
@@ -1,333 +0,0 @@
1
- require "logstash/outputs/elasticsearch"
2
- require "cabin"
3
- require "base64"
4
-
5
- module LogStash::Outputs::Elasticsearch
6
- module Protocols
7
- class Base
8
- private
9
- def initialize(options={})
10
- # host(s), port, cluster
11
- @logger = Cabin::Channel.get
12
- end
13
-
14
- def client
15
- return @client if @client
16
- @client = build_client(@options)
17
- return @client
18
- end # def client
19
-
20
-
21
- def template_install(name, template, force=false)
22
- if template_exists?(name) && !force
23
- @logger.debug("Found existing Elasticsearch template. Skipping template management", :name => name)
24
- return
25
- end
26
- template_put(name, template)
27
- end
28
-
29
- # Do a bulk request with the given actions.
30
- #
31
- # 'actions' is expected to be an array of bulk requests as string json
32
- # values.
33
- #
34
- # Each 'action' becomes a single line in the bulk api call. For more
35
- # details on the format of each.
36
- def bulk(actions)
37
- raise NotImplemented, "You must implement this yourself"
38
- # bulk([
39
- # '{ "index" : { "_index" : "test", "_type" : "type1", "_id" : "1" } }',
40
- # '{ "field1" : "value1" }'
41
- #])
42
- end
43
-
44
- public(:initialize, :template_install)
45
- end
46
-
47
- class HTTPClient < Base
48
- private
49
-
50
- DEFAULT_OPTIONS = {
51
- :port => 9200
52
- }
53
-
54
- def initialize(options={})
55
- super
56
- require "elasticsearch" # gem 'elasticsearch-ruby'
57
- # manticore http transport
58
- require "elasticsearch/transport/transport/http/manticore"
59
- @options = DEFAULT_OPTIONS.merge(options)
60
- @client = client
61
- end
62
-
63
- def build_client(options)
64
- uri = "#{options[:protocol]}://#{options[:host]}:#{options[:port]}#{options[:client_settings][:path]}"
65
- timeout = options[:timeout] || 0
66
-
67
- client_options = {
68
- :host => [uri],
69
- :ssl => options[:client_settings][:ssl],
70
- :transport_options => { # manticore settings so we
71
- :socket_timeout => timeout, # do not timeout socket reads
72
- :request_timeout => timeout, # and requests
73
- :proxy => options[:client_settings][:proxy]
74
- },
75
- :transport_class => ::Elasticsearch::Transport::Transport::HTTP::Manticore
76
- }
77
-
78
- if options[:user] && options[:password] then
79
- token = Base64.strict_encode64(options[:user] + ":" + options[:password])
80
- client_options[:headers] = { "Authorization" => "Basic #{token}" }
81
- end
82
-
83
- Elasticsearch::Client.new client_options
84
- end
85
-
86
- def self.normalize_bulk_response(bulk_response)
87
- if bulk_response["errors"]
88
- # The structure of the response from the REST Bulk API is follows:
89
- # {"took"=>74, "errors"=>true, "items"=>[{"create"=>{"_index"=>"logstash-2014.11.17",
90
- # "_type"=>"logs",
91
- # "_id"=>"AUxTS2C55Jrgi-hC6rQF",
92
- # "_version"=>1,
93
- # "status"=>400,
94
- # "error"=>"MapperParsingException[failed to parse]..."}}]}
95
- # where each `item` is a hash of {OPTYPE => Hash[]}. calling first, will retrieve
96
- # this hash as a single array with two elements, where the value is the second element (i.first[1])
97
- # then the status of that item is retrieved.
98
- {"errors" => true, "statuses" => bulk_response["items"].map { |i| i.first[1]['status'] }}
99
- else
100
- {"errors" => false}
101
- end
102
- end
103
-
104
- def bulk(actions)
105
- bulk_response = @client.bulk(:body => actions.collect do |action, args, source|
106
- if action == 'update'
107
- if args[:_id]
108
- source = { 'doc' => source }
109
- if @options[:doc_as_upsert]
110
- source['doc_as_upsert'] = true
111
- else
112
- source['upsert'] = args[:_upsert] if args[:_upsert]
113
- end
114
- else
115
- raise(LogStash::ConfigurationError, "Specifying action => 'update' without a document '_id' is not supported.")
116
- end
117
- end
118
- args.delete(:_upsert)
119
- if source
120
- next [ { action => args }, source ]
121
- else
122
- next { action => args }
123
- end
124
- end.flatten)
125
-
126
- self.class.normalize_bulk_response(bulk_response)
127
- end # def bulk
128
-
129
- def template_exists?(name)
130
- @client.indices.get_template(:name => name)
131
- return true
132
- rescue Elasticsearch::Transport::Transport::Errors::NotFound
133
- return false
134
- end # def template_exists?
135
-
136
- def template_put(name, template)
137
- @client.indices.put_template(:name => name, :body => template)
138
- end # template_put
139
-
140
- public(:bulk)
141
- end # class HTTPClient
142
-
143
- class NodeClient < Base
144
- private
145
-
146
- DEFAULT_OPTIONS = {
147
- :port => 9300,
148
- }
149
-
150
- def initialize(options={})
151
- super
152
- require "java"
153
- @options = DEFAULT_OPTIONS.merge(options)
154
- setup(@options)
155
- @client = client
156
- end # def initialize
157
-
158
- def settings
159
- return @settings
160
- end
161
-
162
- def setup(options={})
163
- @settings = org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder
164
- if options[:host]
165
- @settings.put("discovery.zen.ping.multicast.enabled", false)
166
- @settings.put("discovery.zen.ping.unicast.hosts", NodeClient.hosts(options))
167
- end
168
-
169
- @settings.put("node.client", true)
170
- @settings.put("http.enabled", false)
171
-
172
- if options[:client_settings]
173
- options[:client_settings].each do |key, value|
174
- @settings.put(key, value)
175
- end
176
- end
177
-
178
- return @settings
179
- end
180
-
181
- def self.hosts(options)
182
- # http://www.elasticsearch.org/guide/reference/modules/discovery/zen/
183
- result = Array.new
184
- if options[:host].class == Array
185
- options[:host].each do |host|
186
- if host.to_s =~ /^.+:.+$/
187
- # For host in format: host:port, ignore options[:port]
188
- result << host
189
- else
190
- if options[:port].to_s =~ /^\d+-\d+$/
191
- # port ranges are 'host[port1-port2]'
192
- result << Range.new(*options[:port].split("-")).collect { |p| "#{host}:#{p}" }
193
- else
194
- result << "#{host}:#{options[:port]}"
195
- end
196
- end
197
- end
198
- else
199
- if options[:host].to_s =~ /^.+:.+$/
200
- # For host in format: host:port, ignore options[:port]
201
- result << options[:host]
202
- else
203
- if options[:port].to_s =~ /^\d+-\d+$/
204
- # port ranges are 'host[port1-port2]' according to
205
- # http://www.elasticsearch.org/guide/reference/modules/discovery/zen/
206
- # However, it seems to only query the first port.
207
- # So generate our own list of unicast hosts to scan.
208
- range = Range.new(*options[:port].split("-"))
209
- result << range.collect { |p| "#{options[:host]}:#{p}" }
210
- else
211
- result << "#{options[:host]}:#{options[:port]}"
212
- end
213
- end
214
- end
215
- result.flatten.join(",")
216
- end # def self.hosts
217
-
218
- def build_client(options)
219
- nodebuilder = org.elasticsearch.node.NodeBuilder.nodeBuilder
220
- return nodebuilder.settings(@settings).node.client
221
- end # def build_client
222
-
223
- def self.normalize_bulk_response(bulk_response)
224
- # TODO(talevy): parse item response objects to retrieve correct 200 (OK) or 201(created) status codes
225
- if bulk_response.has_failures()
226
- {"errors" => true,
227
- "statuses" => bulk_response.map { |i| (i.is_failed && i.get_failure.get_status.get_status) || 200 }}
228
- else
229
- {"errors" => false}
230
- end
231
- end
232
-
233
- def bulk(actions)
234
- # Actions an array of [ action, action_metadata, source ]
235
- prep = @client.prepareBulk
236
- actions.each do |action, args, source|
237
- prep.add(build_request(action, args, source))
238
- end
239
- response = prep.execute.actionGet()
240
-
241
- self.class.normalize_bulk_response(response)
242
- end # def bulk
243
-
244
- def build_request(action, args, source)
245
- case action
246
- when "index"
247
- request = org.elasticsearch.action.index.IndexRequest.new(args[:_index])
248
- request.id(args[:_id]) if args[:_id]
249
- request.routing(args[:_routing]) if args[:_routing]
250
- request.source(source)
251
- when "delete"
252
- request = org.elasticsearch.action.delete.DeleteRequest.new(args[:_index])
253
- request.id(args[:_id])
254
- request.routing(args[:_routing]) if args[:_routing]
255
- when "create"
256
- request = org.elasticsearch.action.index.IndexRequest.new(args[:_index])
257
- request.id(args[:_id]) if args[:_id]
258
- request.routing(args[:_routing]) if args[:_routing]
259
- request.source(source)
260
- request.opType("create")
261
- when "create_unless_exists"
262
- unless args[:_id].nil?
263
- request = org.elasticsearch.action.index.IndexRequest.new(args[:_index])
264
- request.id(args[:_id])
265
- request.routing(args[:_routing]) if args[:_routing]
266
- request.source(source)
267
- request.opType("create")
268
- else
269
- raise(LogStash::ConfigurationError, "Specifying action => 'create_unless_exists' without a document '_id' is not supported.")
270
- end
271
- when "update"
272
- unless args[:_id].nil?
273
- request = org.elasticsearch.action.update.UpdateRequest.new(args[:_index], args[:_type], args[:_id])
274
- request.routing(args[:_routing]) if args[:_routing]
275
- request.doc(source)
276
- if @options[:doc_as_upsert]
277
- request.docAsUpsert(true)
278
- else
279
- request.upsert(args[:_upsert]) if args[:_upsert]
280
- end
281
- else
282
- raise(LogStash::ConfigurationError, "Specifying action => 'update' without a document '_id' is not supported.")
283
- end
284
- else
285
- raise(LogStash::ConfigurationError, "action => '#{action_name}' is not currently supported.")
286
- end # case action
287
-
288
- request.type(args[:_type]) if args[:_type]
289
- return request
290
- end # def build_request
291
-
292
- def template_exists?(name)
293
- request = org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequestBuilder.new(@client.admin.indices, name)
294
- response = request.get
295
- return !response.getIndexTemplates.isEmpty
296
- end # def template_exists?
297
-
298
- def template_put(name, template)
299
- request = org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequestBuilder.new(@client.admin.indices, name)
300
- request.setSource(LogStash::Json.dump(template))
301
-
302
- # execute the request and get the response, if it fails, we'll get an exception.
303
- request.get
304
- end # template_put
305
-
306
- public(:initialize, :bulk)
307
- end # class NodeClient
308
-
309
- class TransportClient < NodeClient
310
- private
311
- def build_client(options)
312
- client = org.elasticsearch.client.transport.TransportClient.new(settings.build)
313
-
314
- if options[:host]
315
- client.addTransportAddress(
316
- org.elasticsearch.common.transport.InetSocketTransportAddress.new(
317
- options[:host], options[:port].to_i
318
- )
319
- )
320
- end
321
-
322
- return client
323
- end # def build_client
324
- end # class TransportClient
325
- end # module Protocols
326
-
327
- module Requests
328
- class GetIndexTemplates; end
329
- class Bulk; end
330
- class Index; end
331
- class Delete; end
332
- end
333
- end
@@ -1,5 +0,0 @@
1
- # encoding: utf-8
2
- require 'logstash/environment'
3
-
4
- root_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
5
- LogStash::Environment.load_runtime_jars! File.join(root_dir, "vendor")
@@ -1,36 +0,0 @@
1
- require_relative "../../../../spec/es_spec_helper"
2
- require "logstash/outputs/elasticsearch/protocol"
3
-
4
- describe "elasticsearch node client", :integration => true do
5
- # Test ElasticSearch Node Client
6
- # Reference: http://www.elasticsearch.org/guide/reference/modules/discovery/zen/
7
-
8
- subject { LogStash::Outputs::Elasticsearch::Protocols::NodeClient }
9
-
10
- it "should support hosts in both string and array" do
11
- # Because we defined *hosts* method in NodeClient as private,
12
- # we use *obj.send :method,[args...]* to call method *hosts*
13
-
14
- # Node client should support host in string
15
- # Case 1: default :host in string
16
- insist { subject.send :hosts, :host => "host",:port => 9300 } == "host:9300"
17
- # Case 2: :port =~ /^\d+_\d+$/
18
- insist { subject.send :hosts, :host => "host",:port => "9300-9302"} == "host:9300,host:9301,host:9302"
19
- # Case 3: :host =~ /^.+:.+$/
20
- insist { subject.send :hosts, :host => "host:9303",:port => 9300 } == "host:9303"
21
- # Case 4: :host =~ /^.+:.+$/ and :port =~ /^\d+_\d+$/
22
- insist { subject.send :hosts, :host => "host:9303",:port => "9300-9302"} == "host:9303"
23
-
24
- # Node client should support host in array
25
- # Case 5: :host in array with single item
26
- insist { subject.send :hosts, :host => ["host"],:port => 9300 } == ("host:9300")
27
- # Case 6: :host in array with more than one items
28
- insist { subject.send :hosts, :host => ["host1","host2"],:port => 9300 } == "host1:9300,host2:9300"
29
- # Case 7: :host in array with more than one items and :port =~ /^\d+_\d+$/
30
- insist { subject.send :hosts, :host => ["host1","host2"],:port => "9300-9302" } == "host1:9300,host1:9301,host1:9302,host2:9300,host2:9301,host2:9302"
31
- # Case 8: :host in array with more than one items and some :host =~ /^.+:.+$/
32
- insist { subject.send :hosts, :host => ["host1","host2:9303"],:port => 9300 } == "host1:9300,host2:9303"
33
- # Case 9: :host in array with more than one items, :port =~ /^\d+_\d+$/ and some :host =~ /^.+:.+$/
34
- insist { subject.send :hosts, :host => ["host1","host2:9303"],:port => "9300-9302" } == "host1:9300,host1:9301,host1:9302,host2:9303"
35
- end
36
- end
@@ -1,94 +0,0 @@
1
- require_relative "../../../spec/es_spec_helper"
2
-
3
- describe "transport client create actions", :integration => true do
4
- require "logstash/outputs/elasticsearch"
5
- require "elasticsearch"
6
-
7
- def get_es_output(action, id = nil)
8
- settings = {
9
- "manage_template" => true,
10
- "index" => "logstash-create",
11
- "template_overwrite" => true,
12
- "protocol" => "transport",
13
- "host" => get_host(),
14
- "port" => get_port('transport'),
15
- "action" => action
16
- }
17
- settings['document_id'] = id unless id.nil?
18
- LogStash::Outputs::ElasticSearch.new(settings)
19
- end
20
-
21
- before :each do
22
- @es = get_client
23
- # Delete all templates first.
24
- # Clean ES of data before we start.
25
- @es.indices.delete_template(:name => "*")
26
- # This can fail if there are no indexes, ignore failure.
27
- @es.indices.delete(:index => "*") rescue nil
28
- end
29
-
30
- context "when action => create" do
31
- it "should create new documents with or without id" do
32
- subject = get_es_output("create", "id123")
33
- subject.register
34
- subject.receive(LogStash::Event.new("message" => "sample message here"))
35
- subject.buffer_flush(:final => true)
36
- @es.indices.refresh
37
- # Wait or fail until everything's indexed.
38
- Stud::try(3.times) do
39
- r = @es.search
40
- insist { r["hits"]["total"] } == 1
41
- end
42
- end
43
-
44
- it "should create new documents without id" do
45
- subject = get_es_output("create")
46
- subject.register
47
- subject.receive(LogStash::Event.new("message" => "sample message here"))
48
- subject.buffer_flush(:final => true)
49
- @es.indices.refresh
50
- # Wait or fail until everything's indexed.
51
- Stud::try(3.times) do
52
- r = @es.search
53
- insist { r["hits"]["total"] } == 1
54
- end
55
- end
56
- end
57
-
58
- context "when action => create_unless_exists" do
59
- it "should create new documents when specific id is specified" do
60
- subject = get_es_output("create_unless_exists", "id123")
61
- subject.register
62
- subject.receive(LogStash::Event.new("message" => "sample message here"))
63
- subject.buffer_flush(:final => true)
64
- @es.indices.refresh
65
- # Wait or fail until everything's indexed.
66
- Stud::try(3.times) do
67
- r = @es.search
68
- insist { r["hits"]["total"] } == 1
69
- end
70
- end
71
-
72
- it "should fail to create a document when no id is specified" do
73
- event = LogStash::Event.new("somevalue" => 100, "@timestamp" => "2014-11-17T20:37:17.223Z", "@metadata" => {"retry_count" => 0})
74
- action = ["create_unless_exists", {:_id=>nil, :_index=>"logstash-2014.11.17", :_type=>"logs"}, event]
75
- subject = get_es_output(action[0])
76
- subject.register
77
- expect { subject.flush([action]) }.to raise_error
78
- end
79
-
80
- it "should unsuccesfully submit two records with the same document id" do
81
- subject = get_es_output("create_unless_exists", "id123")
82
- subject.register
83
- subject.receive(LogStash::Event.new("message" => "sample message here"))
84
- subject.receive(LogStash::Event.new("message" => "sample message here")) # 400 status failure (same id)
85
- subject.buffer_flush(:final => true)
86
- @es.indices.refresh
87
- # Wait or fail until everything's indexed.
88
- Stud::try(3.times) do
89
- r = @es.search
90
- insist { r["hits"]["total"] } == 1
91
- end
92
- end
93
- end
94
- end