logstash-output-elastic_app_search 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4db812e586bb58a374dbf078f9d2fa42e43eae9a8f53f08dee0913e9690be733
4
- data.tar.gz: da43265b1a72eeaaa943291a9bc18f7aca3e87403461c01ffbfcc13152e4359d
3
+ metadata.gz: 074b1a0e77284e01d820135619726f4cdcc5e0626bae4ba694d1c08870184f46
4
+ data.tar.gz: 5fac5d1794454fd80ec91942a15a34497c709a74a64e6b3e19eebb184f6c4746
5
5
  SHA512:
6
- metadata.gz: a01665272fe1993eefb689bc088fe32fdbaebbb1fe46dbff6f9cb8e90220dae1008612bfbefa92a2a3ea033bf86975d10d976feff2d0247affa4330bbbbe04d5
7
- data.tar.gz: a5f9601d14d30b373ed918652afcd822d6bd17bd0cb62789c591ed2b443a55631cc593d0fcc6faef565466f8177056afcbda803229361fdec2f944384e02b345
6
+ metadata.gz: 8244e5bc5378bdb4b05ff48ee7da23f549e6e62f47b828b2c8f384633e635cf3fb3a5a16dba6b5b7a9028ab2c1b431a3227c45e181584b386ba547fd6384e839
7
+ data.tar.gz: 4e00d97b9d9d360dfa3bfb747797b35efc888e868c5663154b296d4a8716fbd96c42aae0b1742458f56a4bfdd2d4c75b9affa57fdd7961f0bd993241c21fa903
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 1.2.0
2
+ - Changed evaluation of `engine` option to use event's sprintf format, [#25](https://github.com/logstash-plugins/logstash-output-elastic_app_search/pull/25)
3
+
1
4
  ## 1.1.1
2
5
  - Added missed dependency (elastic-app-search) to the gemspec, fixes issue [#17](https://github.com/logstash-plugins/logstash-output-elastic_app_search/issues/17)
3
6
 
@@ -13,6 +13,8 @@ class LogStash::Outputs::ElasticAppSearch < LogStash::Outputs::Base
13
13
  config :document_id, :validate => :string
14
14
  config :path, :validate => :string, :default => "/api/as/v1/"
15
15
 
16
+ ENGINE_WITH_SPRINTF_REGEX = /^.*%\{.+\}.*$/
17
+
16
18
  public
17
19
  def register
18
20
  if @host.nil? && @url.nil?
@@ -26,7 +28,7 @@ class LogStash::Outputs::ElasticAppSearch < LogStash::Outputs::Base
26
28
  elsif @url
27
29
  @client = Elastic::AppSearch::Client.new(:api_endpoint => @url + @path, :api_key => @api_key.value)
28
30
  end
29
- check_connection!
31
+ check_connection! unless @engine =~ ENGINE_WITH_SPRINTF_REGEX
30
32
  rescue => e
31
33
  if e.message =~ /401/
32
34
  raise ::LogStash::ConfigurationError.new("Failed to connect to App Search. Error: 401. Please check your credentials")
@@ -51,7 +53,8 @@ class LogStash::Outputs::ElasticAppSearch < LogStash::Outputs::Base
51
53
 
52
54
  private
53
55
  def format_batch(events)
54
- events.map do |event|
56
+ docs_for_engine = {}
57
+ events.each do |event|
55
58
  doc = event.to_hash
56
59
  # we need to remove default fields that start with "@"
57
60
  # since Elastic App Search doesn't accept them
@@ -64,17 +67,33 @@ class LogStash::Outputs::ElasticAppSearch < LogStash::Outputs::Base
64
67
  doc["id"] = event.sprintf(@document_id)
65
68
  end
66
69
  doc.delete("@version")
67
- doc
70
+ resolved_engine = event.sprintf(@engine)
71
+ unless docs_for_engine[resolved_engine]
72
+ if @logger.debug?
73
+ @logger.debug("Creating new engine segment in batch to send", :resolved_engine => resolved_engine)
74
+ end
75
+ docs_for_engine[resolved_engine] = []
76
+ end
77
+ docs_for_engine[resolved_engine] << doc
68
78
  end
79
+ docs_for_engine
69
80
  end
70
81
 
71
- def index(documents)
72
- response = @client.index_documents(@engine, documents)
73
- report(documents, response)
74
- rescue => e
75
- @logger.error("Failed to execute index operation. Retrying..", :exception => e.class, :reason => e.message)
76
- sleep(1)
77
- retry
82
+ def index(batch)
83
+ batch.each do |resolved_engine, documents|
84
+ begin
85
+ if resolved_engine =~ ENGINE_WITH_SPRINTF_REGEX || resolved_engine =~ /^\s*$/
86
+ raise "Cannot resolve engine field name #{@engine} from event"
87
+ end
88
+ response = @client.index_documents(resolved_engine, documents)
89
+ report(documents, response)
90
+ rescue => e
91
+ @logger.error("Failed to execute index operation. Retrying..", :exception => e.class, :reason => e.message,
92
+ :resolved_engine => resolved_engine)
93
+ sleep(1)
94
+ retry
95
+ end
96
+ end
78
97
  end
79
98
 
80
99
  def report(documents, response)
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-output-elastic_app_search'
3
- s.version = '1.1.1'
3
+ s.version = '1.2.0'
4
4
  s.licenses = ['Apache-2.0']
5
5
  s.summary = 'Index data to Elastic App Search'
6
6
  s.description = 'This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program'
@@ -48,6 +48,22 @@ describe "indexing against running AppSearch", :integration => true do
48
48
  end
49
49
  end
50
50
 
51
+ describe "register" do
52
+ let(:config) do
53
+ {
54
+ "api_key" => ENV['APPSEARCH_PRIVATE_KEY'],
55
+ "engine" => "%{engine_name_field}",
56
+ "url" => "http://appsearch:3002"
57
+ }
58
+ end
59
+
60
+ context "when engine is defined in sprintf format" do
61
+ it "does not raise an error" do
62
+ expect { subject.register }.to_not raise_error
63
+ end
64
+ end
65
+ end
66
+
51
67
  describe "indexing" do
52
68
 
53
69
  before do
@@ -69,6 +85,31 @@ describe "indexing against running AppSearch", :integration => true do
69
85
  end
70
86
  expect(results.first.dig("message", "raw")).to eq "an event to index"
71
87
  end
88
+
89
+ context "using sprintf-ed engine" do
90
+ let(:config) do
91
+ {
92
+ "api_key" => ENV['APPSEARCH_PRIVATE_KEY'],
93
+ "engine" => "%{engine_name_field}",
94
+ "url" => "http://appsearch:3002"
95
+ }
96
+ end
97
+
98
+ let(:event) { LogStash::Event.new("message" => "an event to index", "engine_name_field" => engine_name) }
99
+
100
+ it "should be indexed" do
101
+ app_search_output.multi_receive([event])
102
+
103
+ results = Stud.try(20.times, RSpec::Expectations::ExpectationNotMetError) do
104
+ attempt_response = execute_search_call(engine_name)
105
+ expect(attempt_response.status).to eq(200)
106
+ parsed_resp = JSON.parse(attempt_response.body)
107
+ expect(parsed_resp.dig("meta", "page", "total_pages")).to eq(1)
108
+ parsed_resp["results"]
109
+ end
110
+ expect(results.first.dig("message", "raw")).to eq "an event to index"
111
+ end
112
+ end
72
113
  end
73
114
 
74
115
  private
@@ -80,24 +121,60 @@ describe "indexing against running AppSearch", :integration => true do
80
121
  end
81
122
 
82
123
  describe "multiple events" do
83
- let(:events) { generate_events(200) } #2 times the slice size used to batch
124
+ context "single static engine" do
125
+ let(:events) { generate_events(200) } #2 times the slice size used to batch
84
126
 
85
- it "all should be indexed" do
86
- app_search_output.multi_receive(events)
87
- results = Stud.try(20.times, RSpec::Expectations::ExpectationNotMetError) do
88
- attempt_response = execute_search_call(engine_name)
89
- expect(attempt_response.status).to eq(200)
90
- parsed_resp = JSON.parse(attempt_response.body)
91
- expect(parsed_resp.dig("meta", "page", "total_results")).to eq(200)
92
- parsed_resp["results"]
127
+ it "all should be indexed" do
128
+ app_search_output.multi_receive(events)
129
+
130
+ expect_indexed(engine_name, 200)
131
+ end
132
+ end
133
+
134
+ context "multiple sprintf engines" do
135
+ let(:config) do
136
+ {
137
+ "api_key" => ENV['APPSEARCH_PRIVATE_KEY'],
138
+ "engine" => "%{engine_name_field}",
139
+ "url" => "http://appsearch:3002"
140
+ }
141
+ end
142
+
143
+ it "all should be indexed" do
144
+ create_engine('testengin1', "http://appsearch:3002", ENV['APPSEARCH_PRIVATE_KEY'])
145
+ create_engine('testengin2', "http://appsearch:3002", ENV['APPSEARCH_PRIVATE_KEY'])
146
+ events = generate_events(100, 'testengin1')
147
+ events += generate_events(100, 'testengin2')
148
+ events.shuffle!
149
+
150
+ app_search_output.multi_receive(events)
151
+
152
+ expect_indexed('testengin1', 100)
153
+ expect_indexed('testengin2', 100)
93
154
  end
94
- expect(results.first.dig("message", "raw")).to start_with("an event to index")
95
155
  end
96
156
  end
97
157
 
98
158
  private
99
- def generate_events(num_events)
100
- (1..num_events).map { |i| LogStash::Event.new("message" => "an event to index #{i}")}
159
+ def expect_indexed(engine_name, expected_docs_count)
160
+ results = Stud.try(20.times, RSpec::Expectations::ExpectationNotMetError) do
161
+ attempt_response = execute_search_call(engine_name)
162
+ expect(attempt_response.status).to eq(200)
163
+ parsed_resp = JSON.parse(attempt_response.body)
164
+ expect(parsed_resp.dig("meta", "page", "total_results")).to eq(expected_docs_count)
165
+ parsed_resp["results"]
166
+ end
167
+ expect(results.first.dig("message", "raw")).to start_with("an event to index")
168
+ end
169
+
170
+ def generate_events(num_events, engine_name = nil)
171
+ (1..num_events).map do |i|
172
+ if engine_name
173
+ LogStash::Event.new("message" => "an event to index #{i}", "engine_name_field" => engine_name)
174
+ else
175
+ LogStash::Event.new("message" => "an event to index #{i}")
176
+ end
177
+ end
101
178
  end
102
179
  end
103
180
  end
@@ -39,5 +39,12 @@ describe LogStash::Outputs::ElasticAppSearch do
39
39
  expect { subject.register }.to raise_error(LogStash::ConfigurationError)
40
40
  end
41
41
  end
42
+ context "when engine is in sprintf format" do
43
+ let(:config) { { "host" => host, "api_key" => api_key, "engine" => "%{type}" } }
44
+ it "connection is not checked" do
45
+ expect { subject.register }.to_not raise_error
46
+ expect(subject).not_to receive(:check_connection!)
47
+ end
48
+ end
42
49
  end
43
50
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-output-elastic_app_search
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joao Duarte
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-09-16 00:00:00.000000000 Z
12
+ date: 2021-05-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  requirement: !ruby/object:Gem::Requirement