logstash-filter-elasticsearch 4.1.1 → 4.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: 1ca5815c2b1127152e31b4ca9e0192021bfb0b0d9d2807ca090c2123feea2bb8
4
- data.tar.gz: b3983e01f4e73076e48f3d793f2061cb3210490b09394352146b65377f946137
3
+ metadata.gz: 1e52f705a48101e5fa0848fd410b13ce60f4d56912539905824c302b684d8bab
4
+ data.tar.gz: de8e58fa7515cd3096250b770739c5de74a421172452c259b2bcc9c34c62b8e0
5
5
  SHA512:
6
- metadata.gz: 65822b043de7055e2422810c4b03662c45fb15d58f45edadd677a220287d37a8f8c7d1382e3b1dc04e00ac51e261a1e5f7e11c06b4d57481218be9da2f498534
7
- data.tar.gz: 8738742640bf8729297f556154d6ef11878e99b55bfe1193da459f2f83cf255d5e5973537bc6e97e5d32021d1976f6aa4c284081a98f5b7a84c1073dfa039cc2
6
+ metadata.gz: 0becf0596e1f620b90170d0a36006f5664f62453ab37a8be30d8da487ce777479fab3664890ce130da33a370c115a274721a4ea9edd6e6096f400941c50d750f
7
+ data.tar.gz: 5e0a0d77375a2f181d07af70cdcefacc22534043e323cf97d1f0c291853f4b02d79fba7873d51d40a4e4b256494fac1df7149684e962849cc557f472cf6e7f3c
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 4.2.0
2
+ - Add `target` configuration option to store the result into it [#196](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/196)
3
+
1
4
  ## 4.1.1
2
5
  - Add elastic-transport client support used in elasticsearch-ruby 8.x [#191](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/191)
3
6
 
data/docs/index.asciidoc CHANGED
@@ -162,6 +162,7 @@ NOTE: As of version `4.0.0` of this plugin, a number of previously deprecated se
162
162
  | <<plugins-{type}s-{plugin}-ssl_truststore_type>> |<<string,string>>|No
163
163
  | <<plugins-{type}s-{plugin}-ssl_verification_mode>> |<<string,string>>, one of `["full", "none"]`|No
164
164
  | <<plugins-{type}s-{plugin}-tag_on_failure>> |<<array,array>>|No
165
+ | <<plugins-{type}s-{plugin}-target>> |<<string,string>>|No
165
166
  | <<plugins-{type}s-{plugin}-user>> |<<string,string>>|No
166
167
  |=======================================================================
167
168
 
@@ -175,8 +176,11 @@ filter plugins.
175
176
 
176
177
  * Value type is <<hash,hash>>
177
178
  * Default value is `{}`
179
+ * Format: `"aggregation_name" => "[path][on][event]"`:
180
+ ** `aggregation_name`: aggregation name in result from {es}
181
+ ** `[path][on][event]`: path for where to place the value on the current event, using field-reference notation
178
182
 
179
- Hash of aggregation names to copy from elasticsearch response into Logstash event fields
183
+ A mapping of aggregations to copy into the <<plugins-{type}s-{plugin}-target>> of the current event.
180
184
 
181
185
  Example:
182
186
  [source,ruby]
@@ -246,8 +250,11 @@ These custom headers will override any headers previously set by the plugin such
246
250
 
247
251
  * Value type is <<hash,hash>>
248
252
  * Default value is `{}`
253
+ * Format: `"path.in.source" => "[path][on][event]"`:
254
+ ** `path.in.source`: field path in document source of result from {es}, using dot-notation
255
+ ** `[path][on][event]`: path for where to place the value on the current event, using field-reference notation
249
256
 
250
- Hash of docinfo fields to copy from old event (found via elasticsearch) into new event
257
+ A mapping of docinfo (`_source`) fields to copy into the <<plugins-{type}s-{plugin}-target>> of the current event.
251
258
 
252
259
  Example:
253
260
  [source,ruby]
@@ -273,9 +280,11 @@ Whether results should be sorted or not
273
280
 
274
281
  * Value type is <<array,array>>
275
282
  * Default value is `{}`
283
+ * Format: `"path.in.result" => "[path][on][event]"`:
284
+ ** `path.in.result`: field path in indexed result from {es}, using dot-notation
285
+ ** `[path][on][event]`: path for where to place the value on the current event, using field-reference notation
276
286
 
277
- An array of fields to copy from the old event (found via elasticsearch) into the
278
- new event, currently being processed.
287
+ A mapping of indexed fields to copy into the <<plugins-{type}s-{plugin}-target>> of the current event.
279
288
 
280
289
  In the following example, the values of `@timestamp` and `event_id` on the event
281
290
  found via elasticsearch are copied to the current event's
@@ -523,6 +532,43 @@ WARNING: Setting certificate verification to `none` disables many security benef
523
532
 
524
533
  Tags the event on failure to look up previous log event information. This can be used in later analysis.
525
534
 
535
+ [id="plugins-{type}s-{plugin}-target"]
536
+ ===== `target`
537
+
538
+ * Value type is <<string,string>>
539
+ * There is no default value for this setting.
540
+
541
+ Define the target field for placing the result data.
542
+ If this setting is omitted, the target will be the root (top level) of the event.
543
+
544
+ The destination fields specified in <<plugins-{type}s-{plugin}-fields>>, <<plugins-{type}s-{plugin}-aggregation_fields>>, and <<plugins-{type}s-{plugin}-docinfo_fields>> are relative to this target.
545
+
546
+ For example, if you want the data to be put in the `operation` field:
547
+ [source,ruby]
548
+ if [type] == "end" {
549
+ filter {
550
+ query => "type:start AND transaction:%{[transactionId]}"
551
+ elasticsearch {
552
+ target => "transaction"
553
+ fields => {
554
+ "@timestamp" => "started"
555
+ "transaction_id" => "id"
556
+ }
557
+ }
558
+ }
559
+ }
560
+
561
+ `fields` fields will be expanded into a data structure in the `target` field, overall shape looks like this:
562
+ [source,ruby]
563
+ {
564
+ "transaction" => {
565
+ "started" => "2025-04-29T12:01:46.263Z"
566
+ "id" => "1234567890"
567
+ }
568
+ }
569
+
570
+ NOTE: when writing to a field that already exists on the event, the previous value will be overwritten.
571
+
526
572
  [id="plugins-{type}s-{plugin}-user"]
527
573
  ===== `user`
528
574
 
@@ -2,12 +2,19 @@
2
2
  require "logstash/filters/base"
3
3
  require "logstash/namespace"
4
4
  require "logstash/json"
5
+ require 'logstash/plugin_mixins/ecs_compatibility_support'
6
+ require 'logstash/plugin_mixins/ecs_compatibility_support/target_check'
5
7
  require 'logstash/plugin_mixins/ca_trusted_fingerprint_support'
8
+ require 'logstash/plugin_mixins/validator_support/field_reference_validation_adapter'
6
9
  require "monitor"
7
10
 
8
11
  require_relative "elasticsearch/client"
9
12
 
10
13
  class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
14
+
15
+ include LogStash::PluginMixins::ECSCompatibilitySupport
16
+ include LogStash::PluginMixins::ECSCompatibilitySupport::TargetCheck
17
+
11
18
  config_name "elasticsearch"
12
19
 
13
20
  # List of elasticsearch hosts to use for querying.
@@ -118,6 +125,9 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
118
125
  # Tags the event on failure to look up geo information. This can be used in later analysis.
119
126
  config :tag_on_failure, :validate => :array, :default => ["_elasticsearch_lookup_failure"]
120
127
 
128
+ # If set, the the result set will be nested under the target field
129
+ config :target, :validate => :field_reference
130
+
121
131
  # How many times to retry on failure?
122
132
  config :retry_on_failure, :validate => :number, :default => 0
123
133
 
@@ -205,17 +215,19 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
205
215
  matched = true
206
216
  @fields.each do |old_key, new_key|
207
217
  old_key_path = extract_path(old_key)
208
- set = resultsHits.map do |doc|
218
+ extracted_hit_values = resultsHits.map do |doc|
209
219
  extract_value(doc["_source"], old_key_path)
210
220
  end
211
- event.set(new_key, set.count > 1 ? set : set.first)
221
+ value_to_set = extracted_hit_values.count > 1 ? extracted_hit_values : extracted_hit_values.first
222
+ set_to_event_target(event, new_key, value_to_set)
212
223
  end
213
224
  @docinfo_fields.each do |old_key, new_key|
214
225
  old_key_path = extract_path(old_key)
215
- set = resultsHits.map do |doc|
226
+ extracted_docs_info = resultsHits.map do |doc|
216
227
  extract_value(doc, old_key_path)
217
228
  end
218
- event.set(new_key, set.count > 1 ? set : set.first)
229
+ value_to_set = extracted_docs_info.count > 1 ? extracted_docs_info : extracted_docs_info.first
230
+ set_to_event_target(event, new_key, value_to_set)
219
231
  end
220
232
  end
221
233
 
@@ -223,7 +235,7 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
223
235
  if !resultsAggs.nil? && !resultsAggs.empty?
224
236
  matched = true
225
237
  @aggregation_fields.each do |agg_name, ls_field|
226
- event.set(ls_field, resultsAggs[agg_name])
238
+ set_to_event_target(event, ls_field, resultsAggs[agg_name])
227
239
  end
228
240
  end
229
241
 
@@ -256,6 +268,18 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
256
268
 
257
269
  private
258
270
 
271
+ # if @target is defined, creates a nested structure to inject result into target field
272
+ # if not defined, directly sets to the top-level event field
273
+ # @param event [LogStash::Event]
274
+ # @param new_key [String] name of the field to set
275
+ # @param value_to_set [Array] values to set
276
+ # @return [void]
277
+ def set_to_event_target(event, new_key, value_to_set)
278
+ key_to_set = target ? "[#{target}][#{new_key}]" : new_key
279
+
280
+ event.set(key_to_set, value_to_set)
281
+ end
282
+
259
283
  def client_options
260
284
  @client_options ||= {
261
285
  :user => @user,
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-filter-elasticsearch'
4
- s.version = '4.1.1'
4
+ s.version = '4.2.0'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Copies fields from previous log events in Elasticsearch to current events "
7
7
  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"
@@ -23,7 +23,9 @@ Gem::Specification.new do |s|
23
23
  s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
24
24
  s.add_runtime_dependency 'elasticsearch', ">= 7.14.9", '< 9'
25
25
  s.add_runtime_dependency 'manticore', ">= 0.7.1"
26
+ s.add_runtime_dependency 'logstash-mixin-ecs_compatibility_support', '~> 1.3'
26
27
  s.add_runtime_dependency 'logstash-mixin-ca_trusted_fingerprint_support', '~> 1.0'
28
+ s.add_runtime_dependency 'logstash-mixin-validator_support', '~> 1.0'
27
29
  s.add_development_dependency 'cabin', ['~> 0.6']
28
30
  s.add_development_dependency 'webrick'
29
31
  s.add_development_dependency 'logstash-devutils'
@@ -864,6 +864,33 @@ describe LogStash::Filters::Elasticsearch do
864
864
  end
865
865
  end
866
866
 
867
+ describe "#set_to_event_target" do
868
+
869
+ context "when `@target` is nil, default behavior" do
870
+ let(:config) {{ }}
871
+
872
+ it "sets the value directly to the top-level event field" do
873
+ plugin.send(:set_to_event_target, event, "new_field", %w[value1 value2])
874
+ expect(event.get("new_field")).to eq(%w[value1 value2])
875
+ end
876
+ end
877
+
878
+ context "when @target is defined" do
879
+ let(:config) {{ "target" => "nested" }}
880
+
881
+ it "creates a nested structure under the target field" do
882
+ plugin.send(:set_to_event_target, event, "new_field", %w[value1 value2])
883
+ expect(event.get("nested")).to eq({ "new_field" => %w[value1 value2] })
884
+ end
885
+
886
+ it "overwrites existing target field with new data" do
887
+ event.set("nested", { "existing_field" => "existing_value", "new_field" => "value0" })
888
+ plugin.send(:set_to_event_target, event, "new_field", ["value1"])
889
+ expect(event.get("nested")).to eq({ "existing_field" => "existing_value", "new_field" => ["value1"] })
890
+ end
891
+ end
892
+ end
893
+
867
894
  def extract_transport(client)
868
895
  # on 7x: client.transport.transport
869
896
  # on >=8.x: client.transport
@@ -84,7 +84,9 @@ describe LogStash::Filters::Elasticsearch, :integration => true do
84
84
  end
85
85
 
86
86
  it "fails to register plugin" do
87
- expect { plugin.register }.to raise_error Elasticsearch::Transport::Transport::Errors::Unauthorized
87
+ expect { plugin.register }.to raise_error elastic_ruby_v8_client_available? ?
88
+ Elastic::Transport::Transport::Errors::Unauthorized :
89
+ Elasticsearch::Transport::Transport::Errors::Unauthorized
88
90
  end
89
91
 
90
92
  end if ELASTIC_SECURITY_ENABLED
@@ -150,5 +152,10 @@ describe LogStash::Filters::Elasticsearch, :integration => true do
150
152
  end
151
153
  end
152
154
  end
153
-
155
+ def elastic_ruby_v8_client_available?
156
+ Elasticsearch::Transport
157
+ false
158
+ rescue NameError # NameError: uninitialized constant Elasticsearch::Transport if Elastic Ruby client is not available
159
+ true
160
+ end
154
161
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-filter-elasticsearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.1
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-17 00:00:00.000000000 Z
11
+ date: 2025-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -64,6 +64,20 @@ dependencies:
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
66
  version: 0.7.1
67
+ - !ruby/object:Gem::Dependency
68
+ requirement: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - "~>"
71
+ - !ruby/object:Gem::Version
72
+ version: '1.3'
73
+ name: logstash-mixin-ecs_compatibility_support
74
+ type: :runtime
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '1.3'
67
81
  - !ruby/object:Gem::Dependency
68
82
  requirement: !ruby/object:Gem::Requirement
69
83
  requirements:
@@ -78,6 +92,20 @@ dependencies:
78
92
  - - "~>"
79
93
  - !ruby/object:Gem::Version
80
94
  version: '1.0'
95
+ - !ruby/object:Gem::Dependency
96
+ requirement: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - "~>"
99
+ - !ruby/object:Gem::Version
100
+ version: '1.0'
101
+ name: logstash-mixin-validator_support
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '1.0'
81
109
  - !ruby/object:Gem::Dependency
82
110
  requirement: !ruby/object:Gem::Requirement
83
111
  requirements: