logstash-input-elasticsearch 4.23.1 → 5.0.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 +4 -4
- data/CHANGELOG.md +7 -18
- data/docs/index.asciidoc +25 -359
- data/lib/logstash/inputs/elasticsearch/aggregation.rb +8 -11
- data/lib/logstash/inputs/elasticsearch/paginated_search.rb +2 -12
- data/lib/logstash/inputs/elasticsearch.rb +50 -237
- data/logstash-input-elasticsearch.gemspec +4 -4
- data/spec/fixtures/test_certs/ca.crt +18 -17
- data/spec/fixtures/test_certs/ca.der.sha256 +1 -1
- data/spec/fixtures/test_certs/es.crt +18 -17
- data/spec/inputs/elasticsearch_spec.rb +28 -293
- data/spec/inputs/integration/elasticsearch_spec.rb +2 -10
- metadata +24 -42
- data/lib/logstash/inputs/elasticsearch/cursor_tracker.rb +0 -58
- data/lib/logstash/inputs/elasticsearch/esql.rb +0 -153
- data/spec/fixtures/test_certs/GENERATED_AT +0 -1
- data/spec/fixtures/test_certs/es.chain.crt +0 -38
- data/spec/fixtures/test_certs/renew.sh +0 -15
- data/spec/inputs/cursor_tracker_spec.rb +0 -72
- data/spec/inputs/elasticsearch_esql_spec.rb +0 -180
- data/spec/inputs/integration/elasticsearch_esql_spec.rb +0 -150
- data/version +0 -1
metadata
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: logstash-input-elasticsearch
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 5.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Elastic
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2024-12-18 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
|
-
name: logstash-core-plugin-api
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
@@ -19,6 +19,7 @@ dependencies:
|
|
|
19
19
|
- - "<="
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
21
|
version: '2.99'
|
|
22
|
+
name: logstash-core-plugin-api
|
|
22
23
|
type: :runtime
|
|
23
24
|
prerelease: false
|
|
24
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -30,12 +31,12 @@ dependencies:
|
|
|
30
31
|
- !ruby/object:Gem::Version
|
|
31
32
|
version: '2.99'
|
|
32
33
|
- !ruby/object:Gem::Dependency
|
|
33
|
-
name: logstash-mixin-ecs_compatibility_support
|
|
34
34
|
requirement: !ruby/object:Gem::Requirement
|
|
35
35
|
requirements:
|
|
36
36
|
- - "~>"
|
|
37
37
|
- !ruby/object:Gem::Version
|
|
38
38
|
version: '1.3'
|
|
39
|
+
name: logstash-mixin-ecs_compatibility_support
|
|
39
40
|
type: :runtime
|
|
40
41
|
prerelease: false
|
|
41
42
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -44,12 +45,12 @@ dependencies:
|
|
|
44
45
|
- !ruby/object:Gem::Version
|
|
45
46
|
version: '1.3'
|
|
46
47
|
- !ruby/object:Gem::Dependency
|
|
47
|
-
name: logstash-mixin-event_support
|
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
|
49
49
|
requirements:
|
|
50
50
|
- - "~>"
|
|
51
51
|
- !ruby/object:Gem::Version
|
|
52
52
|
version: '1.0'
|
|
53
|
+
name: logstash-mixin-event_support
|
|
53
54
|
type: :runtime
|
|
54
55
|
prerelease: false
|
|
55
56
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -58,12 +59,12 @@ dependencies:
|
|
|
58
59
|
- !ruby/object:Gem::Version
|
|
59
60
|
version: '1.0'
|
|
60
61
|
- !ruby/object:Gem::Dependency
|
|
61
|
-
name: logstash-mixin-validator_support
|
|
62
62
|
requirement: !ruby/object:Gem::Requirement
|
|
63
63
|
requirements:
|
|
64
64
|
- - "~>"
|
|
65
65
|
- !ruby/object:Gem::Version
|
|
66
66
|
version: '1.0'
|
|
67
|
+
name: logstash-mixin-validator_support
|
|
67
68
|
type: :runtime
|
|
68
69
|
prerelease: false
|
|
69
70
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -72,12 +73,12 @@ dependencies:
|
|
|
72
73
|
- !ruby/object:Gem::Version
|
|
73
74
|
version: '1.0'
|
|
74
75
|
- !ruby/object:Gem::Dependency
|
|
75
|
-
name: logstash-mixin-scheduler
|
|
76
76
|
requirement: !ruby/object:Gem::Requirement
|
|
77
77
|
requirements:
|
|
78
78
|
- - "~>"
|
|
79
79
|
- !ruby/object:Gem::Version
|
|
80
80
|
version: '1.0'
|
|
81
|
+
name: logstash-mixin-scheduler
|
|
81
82
|
type: :runtime
|
|
82
83
|
prerelease: false
|
|
83
84
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -86,15 +87,12 @@ dependencies:
|
|
|
86
87
|
- !ruby/object:Gem::Version
|
|
87
88
|
version: '1.0'
|
|
88
89
|
- !ruby/object:Gem::Dependency
|
|
89
|
-
name: elasticsearch
|
|
90
90
|
requirement: !ruby/object:Gem::Requirement
|
|
91
91
|
requirements:
|
|
92
92
|
- - ">="
|
|
93
93
|
- !ruby/object:Gem::Version
|
|
94
94
|
version: 7.17.9
|
|
95
|
-
|
|
96
|
-
- !ruby/object:Gem::Version
|
|
97
|
-
version: '9'
|
|
95
|
+
name: elasticsearch
|
|
98
96
|
type: :runtime
|
|
99
97
|
prerelease: false
|
|
100
98
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -102,16 +100,13 @@ dependencies:
|
|
|
102
100
|
- - ">="
|
|
103
101
|
- !ruby/object:Gem::Version
|
|
104
102
|
version: 7.17.9
|
|
105
|
-
- - "<"
|
|
106
|
-
- !ruby/object:Gem::Version
|
|
107
|
-
version: '9'
|
|
108
103
|
- !ruby/object:Gem::Dependency
|
|
109
|
-
name: logstash-mixin-ca_trusted_fingerprint_support
|
|
110
104
|
requirement: !ruby/object:Gem::Requirement
|
|
111
105
|
requirements:
|
|
112
106
|
- - "~>"
|
|
113
107
|
- !ruby/object:Gem::Version
|
|
114
108
|
version: '1.0'
|
|
109
|
+
name: logstash-mixin-ca_trusted_fingerprint_support
|
|
115
110
|
type: :runtime
|
|
116
111
|
prerelease: false
|
|
117
112
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -120,12 +115,12 @@ dependencies:
|
|
|
120
115
|
- !ruby/object:Gem::Version
|
|
121
116
|
version: '1.0'
|
|
122
117
|
- !ruby/object:Gem::Dependency
|
|
123
|
-
name: logstash-mixin-normalize_config_support
|
|
124
118
|
requirement: !ruby/object:Gem::Requirement
|
|
125
119
|
requirements:
|
|
126
120
|
- - "~>"
|
|
127
121
|
- !ruby/object:Gem::Version
|
|
128
122
|
version: '1.0'
|
|
123
|
+
name: logstash-mixin-normalize_config_support
|
|
129
124
|
type: :runtime
|
|
130
125
|
prerelease: false
|
|
131
126
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -134,12 +129,12 @@ dependencies:
|
|
|
134
129
|
- !ruby/object:Gem::Version
|
|
135
130
|
version: '1.0'
|
|
136
131
|
- !ruby/object:Gem::Dependency
|
|
137
|
-
name: tzinfo
|
|
138
132
|
requirement: !ruby/object:Gem::Requirement
|
|
139
133
|
requirements:
|
|
140
134
|
- - ">="
|
|
141
135
|
- !ruby/object:Gem::Version
|
|
142
136
|
version: '0'
|
|
137
|
+
name: tzinfo
|
|
143
138
|
type: :runtime
|
|
144
139
|
prerelease: false
|
|
145
140
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -148,12 +143,12 @@ dependencies:
|
|
|
148
143
|
- !ruby/object:Gem::Version
|
|
149
144
|
version: '0'
|
|
150
145
|
- !ruby/object:Gem::Dependency
|
|
151
|
-
name: tzinfo-data
|
|
152
146
|
requirement: !ruby/object:Gem::Requirement
|
|
153
147
|
requirements:
|
|
154
148
|
- - ">="
|
|
155
149
|
- !ruby/object:Gem::Version
|
|
156
150
|
version: '0'
|
|
151
|
+
name: tzinfo-data
|
|
157
152
|
type: :runtime
|
|
158
153
|
prerelease: false
|
|
159
154
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -162,12 +157,12 @@ dependencies:
|
|
|
162
157
|
- !ruby/object:Gem::Version
|
|
163
158
|
version: '0'
|
|
164
159
|
- !ruby/object:Gem::Dependency
|
|
165
|
-
name: manticore
|
|
166
160
|
requirement: !ruby/object:Gem::Requirement
|
|
167
161
|
requirements:
|
|
168
162
|
- - ">="
|
|
169
163
|
- !ruby/object:Gem::Version
|
|
170
164
|
version: 0.7.1
|
|
165
|
+
name: manticore
|
|
171
166
|
type: :runtime
|
|
172
167
|
prerelease: false
|
|
173
168
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -176,12 +171,12 @@ dependencies:
|
|
|
176
171
|
- !ruby/object:Gem::Version
|
|
177
172
|
version: 0.7.1
|
|
178
173
|
- !ruby/object:Gem::Dependency
|
|
179
|
-
name: logstash-codec-plain
|
|
180
174
|
requirement: !ruby/object:Gem::Requirement
|
|
181
175
|
requirements:
|
|
182
176
|
- - ">="
|
|
183
177
|
- !ruby/object:Gem::Version
|
|
184
178
|
version: '0'
|
|
179
|
+
name: logstash-codec-plain
|
|
185
180
|
type: :development
|
|
186
181
|
prerelease: false
|
|
187
182
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -190,12 +185,12 @@ dependencies:
|
|
|
190
185
|
- !ruby/object:Gem::Version
|
|
191
186
|
version: '0'
|
|
192
187
|
- !ruby/object:Gem::Dependency
|
|
193
|
-
name: logstash-devutils
|
|
194
188
|
requirement: !ruby/object:Gem::Requirement
|
|
195
189
|
requirements:
|
|
196
190
|
- - ">="
|
|
197
191
|
- !ruby/object:Gem::Version
|
|
198
192
|
version: '0'
|
|
193
|
+
name: logstash-devutils
|
|
199
194
|
type: :development
|
|
200
195
|
prerelease: false
|
|
201
196
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -204,12 +199,12 @@ dependencies:
|
|
|
204
199
|
- !ruby/object:Gem::Version
|
|
205
200
|
version: '0'
|
|
206
201
|
- !ruby/object:Gem::Dependency
|
|
207
|
-
name: timecop
|
|
208
202
|
requirement: !ruby/object:Gem::Requirement
|
|
209
203
|
requirements:
|
|
210
204
|
- - ">="
|
|
211
205
|
- !ruby/object:Gem::Version
|
|
212
206
|
version: '0'
|
|
207
|
+
name: timecop
|
|
213
208
|
type: :development
|
|
214
209
|
prerelease: false
|
|
215
210
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -218,12 +213,12 @@ dependencies:
|
|
|
218
213
|
- !ruby/object:Gem::Version
|
|
219
214
|
version: '0'
|
|
220
215
|
- !ruby/object:Gem::Dependency
|
|
221
|
-
name: cabin
|
|
222
216
|
requirement: !ruby/object:Gem::Requirement
|
|
223
217
|
requirements:
|
|
224
218
|
- - "~>"
|
|
225
219
|
- !ruby/object:Gem::Version
|
|
226
220
|
version: '0.6'
|
|
221
|
+
name: cabin
|
|
227
222
|
type: :development
|
|
228
223
|
prerelease: false
|
|
229
224
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -232,12 +227,12 @@ dependencies:
|
|
|
232
227
|
- !ruby/object:Gem::Version
|
|
233
228
|
version: '0.6'
|
|
234
229
|
- !ruby/object:Gem::Dependency
|
|
235
|
-
name: webrick
|
|
236
230
|
requirement: !ruby/object:Gem::Requirement
|
|
237
231
|
requirements:
|
|
238
232
|
- - ">="
|
|
239
233
|
- !ruby/object:Gem::Version
|
|
240
234
|
version: '0'
|
|
235
|
+
name: webrick
|
|
241
236
|
type: :development
|
|
242
237
|
prerelease: false
|
|
243
238
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -246,12 +241,12 @@ dependencies:
|
|
|
246
241
|
- !ruby/object:Gem::Version
|
|
247
242
|
version: '0'
|
|
248
243
|
- !ruby/object:Gem::Dependency
|
|
249
|
-
name: rufus-scheduler
|
|
250
244
|
requirement: !ruby/object:Gem::Requirement
|
|
251
245
|
requirements:
|
|
252
246
|
- - "~>"
|
|
253
247
|
- !ruby/object:Gem::Version
|
|
254
248
|
version: 3.0.9
|
|
249
|
+
name: rufus-scheduler
|
|
255
250
|
type: :development
|
|
256
251
|
prerelease: false
|
|
257
252
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -277,35 +272,27 @@ files:
|
|
|
277
272
|
- lib/logstash/helpers/loggable_try.rb
|
|
278
273
|
- lib/logstash/inputs/elasticsearch.rb
|
|
279
274
|
- lib/logstash/inputs/elasticsearch/aggregation.rb
|
|
280
|
-
- lib/logstash/inputs/elasticsearch/cursor_tracker.rb
|
|
281
|
-
- lib/logstash/inputs/elasticsearch/esql.rb
|
|
282
275
|
- lib/logstash/inputs/elasticsearch/paginated_search.rb
|
|
283
276
|
- lib/logstash/inputs/elasticsearch/patches/_elasticsearch_transport_connections_selector.rb
|
|
284
277
|
- lib/logstash/inputs/elasticsearch/patches/_elasticsearch_transport_http_manticore.rb
|
|
285
278
|
- logstash-input-elasticsearch.gemspec
|
|
286
279
|
- spec/es_helper.rb
|
|
287
|
-
- spec/fixtures/test_certs/GENERATED_AT
|
|
288
280
|
- spec/fixtures/test_certs/ca.crt
|
|
289
281
|
- spec/fixtures/test_certs/ca.der.sha256
|
|
290
282
|
- spec/fixtures/test_certs/ca.key
|
|
291
|
-
- spec/fixtures/test_certs/es.chain.crt
|
|
292
283
|
- spec/fixtures/test_certs/es.crt
|
|
293
284
|
- spec/fixtures/test_certs/es.key
|
|
294
|
-
- spec/fixtures/test_certs/renew.sh
|
|
295
|
-
- spec/inputs/cursor_tracker_spec.rb
|
|
296
|
-
- spec/inputs/elasticsearch_esql_spec.rb
|
|
297
285
|
- spec/inputs/elasticsearch_spec.rb
|
|
298
286
|
- spec/inputs/elasticsearch_ssl_spec.rb
|
|
299
|
-
- spec/inputs/integration/elasticsearch_esql_spec.rb
|
|
300
287
|
- spec/inputs/integration/elasticsearch_spec.rb
|
|
301
288
|
- spec/inputs/paginated_search_spec.rb
|
|
302
|
-
|
|
303
|
-
homepage: https://elastic.co/logstash
|
|
289
|
+
homepage: http://www.elastic.co/guide/en/logstash/current/index.html
|
|
304
290
|
licenses:
|
|
305
291
|
- Apache License (2.0)
|
|
306
292
|
metadata:
|
|
307
293
|
logstash_plugin: 'true'
|
|
308
294
|
logstash_group: input
|
|
295
|
+
post_install_message:
|
|
309
296
|
rdoc_options: []
|
|
310
297
|
require_paths:
|
|
311
298
|
- lib
|
|
@@ -320,23 +307,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
320
307
|
- !ruby/object:Gem::Version
|
|
321
308
|
version: '0'
|
|
322
309
|
requirements: []
|
|
323
|
-
rubygems_version: 3.
|
|
310
|
+
rubygems_version: 3.3.26
|
|
311
|
+
signing_key:
|
|
324
312
|
specification_version: 4
|
|
325
313
|
summary: Reads query results from an Elasticsearch cluster
|
|
326
314
|
test_files:
|
|
327
315
|
- spec/es_helper.rb
|
|
328
|
-
- spec/fixtures/test_certs/GENERATED_AT
|
|
329
316
|
- spec/fixtures/test_certs/ca.crt
|
|
330
317
|
- spec/fixtures/test_certs/ca.der.sha256
|
|
331
318
|
- spec/fixtures/test_certs/ca.key
|
|
332
|
-
- spec/fixtures/test_certs/es.chain.crt
|
|
333
319
|
- spec/fixtures/test_certs/es.crt
|
|
334
320
|
- spec/fixtures/test_certs/es.key
|
|
335
|
-
- spec/fixtures/test_certs/renew.sh
|
|
336
|
-
- spec/inputs/cursor_tracker_spec.rb
|
|
337
|
-
- spec/inputs/elasticsearch_esql_spec.rb
|
|
338
321
|
- spec/inputs/elasticsearch_spec.rb
|
|
339
322
|
- spec/inputs/elasticsearch_ssl_spec.rb
|
|
340
|
-
- spec/inputs/integration/elasticsearch_esql_spec.rb
|
|
341
323
|
- spec/inputs/integration/elasticsearch_spec.rb
|
|
342
324
|
- spec/inputs/paginated_search_spec.rb
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
require 'fileutils'
|
|
2
|
-
|
|
3
|
-
module LogStash; module Inputs; class Elasticsearch
|
|
4
|
-
class CursorTracker
|
|
5
|
-
include LogStash::Util::Loggable
|
|
6
|
-
|
|
7
|
-
attr_reader :last_value
|
|
8
|
-
|
|
9
|
-
def initialize(last_run_metadata_path:, tracking_field:, tracking_field_seed:)
|
|
10
|
-
@last_run_metadata_path = last_run_metadata_path
|
|
11
|
-
@last_value_hashmap = Java::java.util.concurrent.ConcurrentHashMap.new
|
|
12
|
-
@last_value = IO.read(@last_run_metadata_path) rescue nil || tracking_field_seed
|
|
13
|
-
@tracking_field = tracking_field
|
|
14
|
-
logger.info "Starting value for cursor field \"#{@tracking_field}\": #{@last_value}"
|
|
15
|
-
@mutex = Mutex.new
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def checkpoint_cursor(intermediate: true)
|
|
19
|
-
@mutex.synchronize do
|
|
20
|
-
if intermediate
|
|
21
|
-
# in intermediate checkpoints pick the smallest
|
|
22
|
-
converge_last_value {|v1, v2| v1 < v2 ? v1 : v2}
|
|
23
|
-
else
|
|
24
|
-
# in the last search of a PIT choose the largest
|
|
25
|
-
converge_last_value {|v1, v2| v1 > v2 ? v1 : v2}
|
|
26
|
-
@last_value_hashmap.clear
|
|
27
|
-
end
|
|
28
|
-
IO.write(@last_run_metadata_path, @last_value)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def converge_last_value(&block)
|
|
33
|
-
return if @last_value_hashmap.empty?
|
|
34
|
-
new_last_value = @last_value_hashmap.reduceValues(1000, &block)
|
|
35
|
-
logger.debug? && logger.debug("converge_last_value: got #{@last_value_hashmap.values.inspect}. won: #{new_last_value}")
|
|
36
|
-
return if new_last_value == @last_value
|
|
37
|
-
@last_value = new_last_value
|
|
38
|
-
logger.info "New cursor value for field \"#{@tracking_field}\" is: #{new_last_value}"
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def record_last_value(event)
|
|
42
|
-
value = event.get(@tracking_field)
|
|
43
|
-
logger.trace? && logger.trace("storing last_value if #{@tracking_field} for #{Thread.current.object_id}: #{value}")
|
|
44
|
-
@last_value_hashmap.put(Thread.current.object_id, value)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def inject_cursor(query_json)
|
|
48
|
-
# ":present" means "now - 30s" to avoid grabbing partially visible data in the PIT
|
|
49
|
-
result = query_json.gsub(":last_value", @last_value.to_s).gsub(":present", now_minus_30s)
|
|
50
|
-
logger.debug("inject_cursor: injected values for ':last_value' and ':present'", :query => result)
|
|
51
|
-
result
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def now_minus_30s
|
|
55
|
-
Java::java.time.Instant.now.minusSeconds(30).to_s
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end; end; end
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
require 'logstash/helpers/loggable_try'
|
|
2
|
-
|
|
3
|
-
module LogStash
|
|
4
|
-
module Inputs
|
|
5
|
-
class Elasticsearch
|
|
6
|
-
class Esql
|
|
7
|
-
include LogStash::Util::Loggable
|
|
8
|
-
|
|
9
|
-
ESQL_JOB = "ES|QL job"
|
|
10
|
-
|
|
11
|
-
ESQL_PARSERS_BY_TYPE = Hash.new(lambda { |x| x }).merge(
|
|
12
|
-
'date' => ->(value) { value && LogStash::Timestamp.new(value) },
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
# Initialize the ESQL query executor
|
|
16
|
-
# @param client [Elasticsearch::Client] The Elasticsearch client instance
|
|
17
|
-
# @param plugin [LogStash::Inputs::Elasticsearch] The parent plugin instance
|
|
18
|
-
def initialize(client, plugin)
|
|
19
|
-
@client = client
|
|
20
|
-
@event_decorator = plugin.method(:decorate_event)
|
|
21
|
-
@retries = plugin.params["retries"]
|
|
22
|
-
|
|
23
|
-
target_field = plugin.params["target"]
|
|
24
|
-
if target_field
|
|
25
|
-
def self.apply_target(path); "[#{target_field}][#{path}]"; end
|
|
26
|
-
else
|
|
27
|
-
def self.apply_target(path); path; end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
@query = plugin.params["query"]
|
|
31
|
-
unless @query.include?('METADATA')
|
|
32
|
-
logger.info("`METADATA` not found the query. `_id`, `_version` and `_index` will not be available in the result", {:query => @query})
|
|
33
|
-
end
|
|
34
|
-
logger.debug("ES|QL executor initialized with", {:query => @query})
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Execute the ESQL query and process results
|
|
38
|
-
# @param output_queue [Queue] The queue to push processed events to
|
|
39
|
-
# @param query A query (to obey interface definition)
|
|
40
|
-
def do_run(output_queue, query)
|
|
41
|
-
logger.info("ES|QL executor has started")
|
|
42
|
-
response = retryable(ESQL_JOB) do
|
|
43
|
-
@client.esql.query({ body: { query: @query }, format: 'json', drop_null_columns: true })
|
|
44
|
-
end
|
|
45
|
-
# retriable already printed error details
|
|
46
|
-
return if response == false
|
|
47
|
-
|
|
48
|
-
if response&.headers&.dig("warning")
|
|
49
|
-
logger.warn("ES|QL executor received warning", {:warning_message => response.headers["warning"]})
|
|
50
|
-
end
|
|
51
|
-
columns = response['columns']&.freeze
|
|
52
|
-
values = response['values']&.freeze
|
|
53
|
-
logger.debug("ES|QL query response size: #{values&.size}")
|
|
54
|
-
|
|
55
|
-
process_response(columns, values, output_queue) if columns && values
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Execute a retryable operation with proper error handling
|
|
59
|
-
# @param job_name [String] Name of the job for logging purposes
|
|
60
|
-
# @yield The block to execute
|
|
61
|
-
# @return [Boolean] true if successful, false otherwise
|
|
62
|
-
def retryable(job_name, &block)
|
|
63
|
-
stud_try = ::LogStash::Helpers::LoggableTry.new(logger, job_name)
|
|
64
|
-
stud_try.try((@retries + 1).times) { yield }
|
|
65
|
-
rescue => e
|
|
66
|
-
error_details = {:message => e.message, :cause => e.cause}
|
|
67
|
-
error_details[:backtrace] = e.backtrace if logger.debug?
|
|
68
|
-
logger.error("#{job_name} failed with ", error_details)
|
|
69
|
-
false
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
private
|
|
73
|
-
|
|
74
|
-
# Process the ESQL response and push events to the output queue
|
|
75
|
-
# @param columns [Array[Hash]] The ESQL query response columns
|
|
76
|
-
# @param values [Array[Array]] The ESQL query response hits
|
|
77
|
-
# @param output_queue [Queue] The queue to push processed events to
|
|
78
|
-
def process_response(columns, values, output_queue)
|
|
79
|
-
column_specs = columns.map { |column| ColumnSpec.new(column) }
|
|
80
|
-
sub_element_mark_map = mark_sub_elements(column_specs)
|
|
81
|
-
multi_fields = sub_element_mark_map.filter_map { |key, val| key.name if val == true }
|
|
82
|
-
logger.warn("Multi-fields found in ES|QL result and they will not be available in the event. Please use `RENAME` command if you want to include them.", { :detected_multi_fields => multi_fields }) if multi_fields.any?
|
|
83
|
-
|
|
84
|
-
values.each do |row|
|
|
85
|
-
event = column_specs.zip(row).each_with_object(LogStash::Event.new) do |(column, value), event|
|
|
86
|
-
# `unless value.nil?` is a part of `drop_null_columns` that if some of columns' values are not `nil`, `nil` values appear
|
|
87
|
-
# we should continuously filter out them to achieve full `drop_null_columns` on each individual row (ideal `LIMIT 1` result)
|
|
88
|
-
# we also exclude sub-elements of main field
|
|
89
|
-
if value && sub_element_mark_map[column] == false
|
|
90
|
-
field_reference = apply_target(column.field_reference)
|
|
91
|
-
event.set(field_reference, ESQL_PARSERS_BY_TYPE[column.type].call(value))
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
@event_decorator.call(event)
|
|
95
|
-
output_queue << event
|
|
96
|
-
rescue => e
|
|
97
|
-
# if event creation fails with whatever reason, inform user and tag with failure and return entry as it is
|
|
98
|
-
logger.warn("Event creation error, ", message: e.message, exception: e.class, data: { "columns" => columns, "values" => [row] })
|
|
99
|
-
failed_event = LogStash::Event.new("columns" => columns, "values" => [row], "tags" => ['_elasticsearch_input_failure'])
|
|
100
|
-
output_queue << failed_event
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# Determines whether each column in a collection is a nested sub-element (example "user.age")
|
|
105
|
-
# of another column in the same collection (example "user").
|
|
106
|
-
#
|
|
107
|
-
# @param columns [Array<ColumnSpec>] An array of objects with a `name` attribute representing field paths.
|
|
108
|
-
# @return [Hash<ColumnSpec, Boolean>] A hash mapping each column to `true` if it is a sub-element of another field, `false` otherwise.
|
|
109
|
-
# Time complexity: (O(NlogN+N*K)) where K is the number of conflict depth
|
|
110
|
-
# without (`prefix_set`) memoization, it would be O(N^2)
|
|
111
|
-
def mark_sub_elements(columns)
|
|
112
|
-
# Sort columns by name length (ascending)
|
|
113
|
-
sorted_columns = columns.sort_by { |c| c.name.length }
|
|
114
|
-
prefix_set = Set.new # memoization set
|
|
115
|
-
|
|
116
|
-
sorted_columns.each_with_object({}) do |column, memo|
|
|
117
|
-
# Split the column name into parts (e.g., "user.profile.age" → ["user", "profile", "age"])
|
|
118
|
-
parts = column.name.split('.')
|
|
119
|
-
|
|
120
|
-
# Generate all possible parent prefixes (e.g., "user", "user.profile")
|
|
121
|
-
# and check if any parent prefix exists in the set
|
|
122
|
-
parent_prefixes = (0...parts.size - 1).map { |i| parts[0..i].join('.') }
|
|
123
|
-
memo[column] = parent_prefixes.any? { |prefix| prefix_set.include?(prefix) }
|
|
124
|
-
prefix_set.add(column.name)
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# Class representing a column specification in the ESQL response['columns']
|
|
130
|
-
# The class's main purpose is to provide a structure for the event key
|
|
131
|
-
# columns is an array with `name` and `type` pair (example: `{"name"=>"@timestamp", "type"=>"date"}`)
|
|
132
|
-
# @attr_reader :name [String] The name of the column
|
|
133
|
-
# @attr_reader :type [String] The type of the column
|
|
134
|
-
class ColumnSpec
|
|
135
|
-
attr_reader :name, :type
|
|
136
|
-
|
|
137
|
-
def initialize(spec)
|
|
138
|
-
@name = isolate(spec.fetch('name'))
|
|
139
|
-
@type = isolate(spec.fetch('type'))
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def field_reference
|
|
143
|
-
@_field_reference ||= '[' + name.gsub('.', '][') + ']'
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
private
|
|
147
|
-
def isolate(value)
|
|
148
|
-
value.frozen? ? value : value.clone.freeze
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
end
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2026-01-23T17:38:25+01:00
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
-----BEGIN CERTIFICATE-----
|
|
2
|
-
MIIDIzCCAgugAwIBAgIBATANBgkqhkiG9w0BAQsFADA0MTIwMAYDVQQDEylFbGFz
|
|
3
|
-
dGljIENlcnRpZmljYXRlIFRvb2wgQXV0b2dlbmVyYXRlZCBDQTAeFw0yNjAxMjMx
|
|
4
|
-
NjM4MjVaFw0yNzAxMjMxNjM4MjVaMA0xCzAJBgNVBAMTAmVzMIIBIjANBgkqhkiG
|
|
5
|
-
9w0BAQEFAAOCAQ8AMIIBCgKCAQEArZLZvLSWDK7Ul+AaBnjU81dsfaow8zOjCC5V
|
|
6
|
-
V21nXpYzQJoQbuWcvGYxwL7ZDs2ca4Wc8BVCj1NDduHuP7U+QIlUdQpl8kh5a0Zz
|
|
7
|
-
36pcFw7UyF51/AzWixJrht/Azzkb5cpZtE22ZK0KhS4oCsjJmTN0EABAsGhDI9/c
|
|
8
|
-
MjNrUC7iP0dvfOuzAPp7ufY83h98jKKXUYV24snbbvmqoWI6GQQNSG/sEo1+1UGH
|
|
9
|
-
/z07/mVKoBAa5DVoNGvxN0fCE7vW7hkhT8+frJcsYFatAbnf6ql0KzEa8lN9u0gR
|
|
10
|
-
hQNM3zcKKsjEMomBzVBc4SV3KXO0d/jGdDtlqsm2oXqlTMdtGwIDAQABo2cwZTAY
|
|
11
|
-
BgNVHREEETAPgg1lbGFzdGljc2VhcmNoMAkGA1UdEwQCMAAwHQYDVR0OBBYEFFQU
|
|
12
|
-
K+6Cg2kExRj1xSDzEi4kkgKXMB8GA1UdIwQYMBaAFMgkye5+2l+TE0I6RsXRHjGB
|
|
13
|
-
wpBGMA0GCSqGSIb3DQEBCwUAA4IBAQCDFHoe8ntx/D69gb8uVQhUDI2ERb0Hocbh
|
|
14
|
-
BpQVidsKEv220zmZW3O9PX0aN4j3e4HUeRNSdLmtN+sXJ1xCMjHlzA5z5wYJOk2y
|
|
15
|
-
4B/eCOzHoHfvR+l5NUSemZ5mk8V+gThGydPmLZBN3afjzd3NJ7NVaCA8cyKwe2jr
|
|
16
|
-
AMrIdGL5usoBSdbd7Ww0nWhLhqxmmV79+orCwAPJxkzsgFDK+vfIw4VHERXNoIPe
|
|
17
|
-
WKVBGpUCFPSfosLo9fSziNDXL71CrWZlPXt3Otip4QKQV5sZI2zxm1JtHdHX6Ul1
|
|
18
|
-
X7xkcUR5cOF0InxefGl9VHIZTYC5kSzAz95WV068j2Jxgu28GRP5
|
|
19
|
-
-----END CERTIFICATE-----
|
|
20
|
-
-----BEGIN CERTIFICATE-----
|
|
21
|
-
MIIDFTCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQsFADA0MTIwMAYDVQQDEylFbGFz
|
|
22
|
-
dGljIENlcnRpZmljYXRlIFRvb2wgQXV0b2dlbmVyYXRlZCBDQTAeFw0yNjAxMjMx
|
|
23
|
-
NjM4MjVaFw0yNzAxMjMxNjM4MjVaMDQxMjAwBgNVBAMTKUVsYXN0aWMgQ2VydGlm
|
|
24
|
-
aWNhdGUgVG9vbCBBdXRvZ2VuZXJhdGVkIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
|
25
|
-
AQ8AMIIBCgKCAQEArUe66xG4Y2zO13gRC+rBwyvxe+c01pqV6ukw6isIbJIQWs1/
|
|
26
|
-
QfEMhUwYwKs6/UXxK+VwardcA2zYwngXbGGEtms+mpUfH5CdJnrqW7lHz1BVK4yH
|
|
27
|
-
90IzGE0GU4D90OW/L4QkGX0fv3VQbL8KGFKBoF04pXIaSGMStFN4wirutHtQboYv
|
|
28
|
-
99X4kbLjVSIuubUpA/v9dUP1TNl8ar+HKUWRM96ijHkFTF3FR0NnZyt44gP5qC0h
|
|
29
|
-
i4lUiR6Uo9D6WMFjeRYFF7GolCy/I1SzWBmmOnNhQLO5VxcNG4ldhBcapZeGwE98
|
|
30
|
-
m/5lxLIwgFR9ZP8bXdxZTWLC58/LQ2NqOjA9mwIDAQABozIwMDAPBgNVHRMBAf8E
|
|
31
|
-
BTADAQH/MB0GA1UdDgQWBBTIJMnuftpfkxNCOkbF0R4xgcKQRjANBgkqhkiG9w0B
|
|
32
|
-
AQsFAAOCAQEAgZPz/e29AepHZu8uuO6+75b6Uf88m8x5m7XL13m9+vTTSqLvCiBw
|
|
33
|
-
JCf5caO73iOx8AlTT2iSlKPAS/7ogik5anZziqweHmBakb+HNQcUB4Vv4VBo5Ai3
|
|
34
|
-
SdOm28uu4EznVLUILPUHl4FQGxhn6ba1tLEWgxlD/ynBtfcmh9UFtBqIawrpAArv
|
|
35
|
-
TKwGBSkcSxh/yOIo+qbyBh9EyxxlGW/Z08m9qjNqHATDxCelcTsQLhCHkvNShR4j
|
|
36
|
-
v1sFw5XQ8H3zQU6w4WzV81A3K8erIU8WnvdisXLk0T1VB5KURy4+UDyw44tOAPD2
|
|
37
|
-
vBq3gYLgcVEMYVfOWCzDy7LS49leXRzAOA==
|
|
38
|
-
-----END CERTIFICATE-----
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
|
|
3
|
-
set -e
|
|
4
|
-
cd "$(dirname "$0")"
|
|
5
|
-
|
|
6
|
-
openssl x509 -x509toreq -in ca.crt -copy_extensions copyall -signkey ca.key -out ca.csr
|
|
7
|
-
openssl x509 -req -copy_extensions copyall -days 365 -in ca.csr -set_serial 0x01 -signkey ca.key -out ca.crt && rm ca.csr
|
|
8
|
-
openssl x509 -in ca.crt -outform der | sha256sum | awk '{print $1}' > ca.der.sha256
|
|
9
|
-
|
|
10
|
-
openssl x509 -x509toreq -in es.crt -copy_extensions copyall -signkey es.key -out es.csr
|
|
11
|
-
openssl x509 -req -copy_extensions copyall -days 365 -in es.csr -set_serial 0x01 -CA ca.crt -CAkey ca.key -out es.crt && rm es.csr
|
|
12
|
-
cat es.crt ca.crt > es.chain.crt
|
|
13
|
-
|
|
14
|
-
# output ISO8601 timestamp to file
|
|
15
|
-
date -Iseconds > GENERATED_AT
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
require "logstash/devutils/rspec/spec_helper"
|
|
3
|
-
require "logstash/devutils/rspec/shared_examples"
|
|
4
|
-
require "logstash/inputs/elasticsearch"
|
|
5
|
-
require "logstash/inputs/elasticsearch/cursor_tracker"
|
|
6
|
-
|
|
7
|
-
describe LogStash::Inputs::Elasticsearch::CursorTracker do
|
|
8
|
-
|
|
9
|
-
let(:last_run_metadata_path) { Tempfile.new('cursor_tracker_testing').path }
|
|
10
|
-
let(:tracking_field_seed) { "1980-01-01T23:59:59.999999999Z" }
|
|
11
|
-
let(:options) do
|
|
12
|
-
{
|
|
13
|
-
:last_run_metadata_path => last_run_metadata_path,
|
|
14
|
-
:tracking_field => "my_field",
|
|
15
|
-
:tracking_field_seed => tracking_field_seed
|
|
16
|
-
}
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
subject { described_class.new(**options) }
|
|
20
|
-
|
|
21
|
-
it "creating a class works" do
|
|
22
|
-
expect(subject).to be_a described_class
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
describe "checkpoint_cursor" do
|
|
26
|
-
before(:each) do
|
|
27
|
-
subject.checkpoint_cursor(intermediate: false) # store seed value
|
|
28
|
-
[
|
|
29
|
-
Thread.new(subject) {|subject| subject.record_last_value(LogStash::Event.new("my_field" => "2025-01-03T23:59:59.999999999Z")) },
|
|
30
|
-
Thread.new(subject) {|subject| subject.record_last_value(LogStash::Event.new("my_field" => "2025-01-01T23:59:59.999999999Z")) },
|
|
31
|
-
Thread.new(subject) {|subject| subject.record_last_value(LogStash::Event.new("my_field" => "2025-01-02T23:59:59.999999999Z")) },
|
|
32
|
-
].each(&:join)
|
|
33
|
-
end
|
|
34
|
-
context "when doing intermediate checkpoint" do
|
|
35
|
-
it "persists the smallest value" do
|
|
36
|
-
subject.checkpoint_cursor(intermediate: true)
|
|
37
|
-
expect(IO.read(last_run_metadata_path)).to eq("2025-01-01T23:59:59.999999999Z")
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
context "when doing non-intermediate checkpoint" do
|
|
41
|
-
it "persists the largest value" do
|
|
42
|
-
subject.checkpoint_cursor(intermediate: false)
|
|
43
|
-
expect(IO.read(last_run_metadata_path)).to eq("2025-01-03T23:59:59.999999999Z")
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
describe "inject_cursor" do
|
|
49
|
-
let(:new_value) { "2025-01-03T23:59:59.999999999Z" }
|
|
50
|
-
let(:fake_now) { "2026-09-19T23:59:59.999999999Z" }
|
|
51
|
-
|
|
52
|
-
let(:query) do
|
|
53
|
-
%q[
|
|
54
|
-
{ "query": { "range": { "event.ingested": { "gt": :last_value, "lt": :present}}}, "sort": [ { "event.ingested": {"order": "asc", "format": "strict_date_optional_time_nanos", "numeric_type" : "date_nanos" } } ] }
|
|
55
|
-
]
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
before(:each) do
|
|
59
|
-
subject.record_last_value(LogStash::Event.new("my_field" => new_value))
|
|
60
|
-
subject.checkpoint_cursor(intermediate: false)
|
|
61
|
-
allow(subject).to receive(:now_minus_30s).and_return(fake_now)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
it "injects the value of the cursor into json query if it contains :last_value" do
|
|
65
|
-
expect(subject.inject_cursor(query)).to match(/#{new_value}/)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
it "injects current time into json query if it contains :present" do
|
|
69
|
-
expect(subject.inject_cursor(query)).to match(/#{fake_now}/)
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|