logstash-core 5.3.3-java → 5.4.0-java
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/gemspec_jars.rb +2 -0
- data/lib/logstash-core/logstash-core.jar +0 -0
- data/lib/logstash-core/version.rb +1 -1
- data/lib/logstash-core_jars.rb +4 -0
- data/lib/logstash/agent.rb +15 -6
- data/lib/logstash/api/modules/base.rb +1 -1
- data/lib/logstash/api/rack_app.rb +1 -1
- data/lib/logstash/config/config_ast.rb +13 -13
- data/lib/logstash/config/mixin.rb +33 -28
- data/lib/logstash/environment.rb +11 -0
- data/lib/logstash/event.rb +56 -0
- data/lib/logstash/event_dispatcher.rb +2 -2
- data/lib/logstash/execution_context.rb +10 -0
- data/lib/logstash/filter_delegator.rb +3 -2
- data/lib/logstash/inputs/base.rb +15 -1
- data/lib/logstash/instrument/collector.rb +1 -1
- data/lib/logstash/instrument/metric.rb +4 -2
- data/lib/logstash/instrument/metric_store.rb +9 -5
- data/lib/logstash/instrument/null_metric.rb +1 -0
- data/lib/logstash/instrument/periodic_poller/cgroup.rb +3 -3
- data/lib/logstash/instrument/periodic_poller/jvm.rb +11 -8
- data/lib/logstash/instrument/periodic_poller/load_average.rb +4 -2
- data/lib/logstash/instrument/wrapped_write_client.rb +59 -0
- data/lib/logstash/java_integration.rb +2 -2
- data/lib/logstash/output_delegator.rb +2 -2
- data/lib/logstash/output_delegator_strategies/legacy.rb +5 -2
- data/lib/logstash/output_delegator_strategies/shared.rb +2 -1
- data/lib/logstash/output_delegator_strategies/single.rb +2 -1
- data/lib/logstash/outputs/base.rb +8 -0
- data/lib/logstash/patches/cabin.rb +1 -1
- data/lib/logstash/patches/stronger_openssl_defaults.rb +1 -1
- data/lib/logstash/pipeline.rb +47 -19
- data/lib/logstash/plugin.rb +3 -1
- data/lib/logstash/plugins/hooks_registry.rb +6 -6
- data/lib/logstash/plugins/registry.rb +2 -2
- data/lib/logstash/queue_factory.rb +7 -5
- data/lib/logstash/runner.rb +15 -1
- data/lib/logstash/settings.rb +14 -2
- data/lib/logstash/string_interpolation.rb +18 -0
- data/lib/logstash/timestamp.rb +27 -0
- data/lib/logstash/util.rb +1 -1
- data/lib/logstash/util/prctl.rb +1 -1
- data/lib/logstash/util/retryable.rb +1 -1
- data/lib/logstash/util/wrapped_acked_queue.rb +53 -22
- data/lib/logstash/util/wrapped_synchronous_queue.rb +51 -33
- data/lib/logstash/version.rb +1 -1
- data/locales/en.yml +4 -2
- data/logstash-core.gemspec +0 -3
- data/spec/api/lib/api/node_stats_spec.rb +2 -1
- data/spec/api/spec_helper.rb +1 -1
- data/spec/logstash/acked_queue_concurrent_stress_spec.rb +291 -0
- data/spec/logstash/agent_spec.rb +24 -0
- data/spec/logstash/config/mixin_spec.rb +11 -2
- data/spec/logstash/event_dispatcher_spec.rb +8 -1
- data/spec/logstash/event_spec.rb +346 -0
- data/spec/logstash/execution_context_spec.rb +13 -0
- data/spec/logstash/filter_delegator_spec.rb +4 -2
- data/spec/logstash/inputs/base_spec.rb +41 -0
- data/spec/logstash/instrument/metric_spec.rb +2 -1
- data/spec/logstash/instrument/metric_store_spec.rb +14 -0
- data/spec/logstash/instrument/namespaced_metric_spec.rb +2 -1
- data/spec/logstash/instrument/periodic_poller/cgroup_spec.rb +1 -1
- data/spec/logstash/instrument/periodic_poller/jvm_spec.rb +35 -0
- data/spec/logstash/instrument/periodic_poller/load_average_spec.rb +1 -5
- data/spec/logstash/instrument/wrapped_write_client_spec.rb +113 -0
- data/spec/logstash/json_spec.rb +1 -1
- data/spec/logstash/legacy_ruby_event_spec.rb +636 -0
- data/spec/logstash/legacy_ruby_timestamp_spec.rb +170 -0
- data/spec/logstash/output_delegator_spec.rb +6 -3
- data/spec/logstash/outputs/base_spec.rb +23 -0
- data/spec/logstash/pipeline_pq_file_spec.rb +18 -8
- data/spec/logstash/pipeline_spec.rb +41 -5
- data/spec/logstash/plugin_spec.rb +15 -3
- data/spec/logstash/plugins/hooks_registry_spec.rb +2 -2
- data/spec/logstash/runner_spec.rb +33 -2
- data/spec/logstash/settings/port_range_spec.rb +1 -1
- data/spec/logstash/settings_spec.rb +21 -0
- data/spec/logstash/timestamp_spec.rb +29 -0
- data/spec/logstash/util/accessors_spec.rb +179 -0
- data/spec/logstash/util/wrapped_synchronous_queue_spec.rb +4 -11
- data/spec/logstash/util_spec.rb +1 -1
- data/spec/logstash/webserver_spec.rb +1 -1
- data/spec/support/mocks_classes.rb +65 -53
- metadata +25 -30
data/lib/logstash/version.rb
CHANGED
data/locales/en.yml
CHANGED
@@ -71,9 +71,9 @@ en:
|
|
71
71
|
Logstash is not able to start since configuration auto reloading was enabled but the configuration contains plugins that don't support it. Quitting...
|
72
72
|
web_api:
|
73
73
|
cant_bind_to_port: |-
|
74
|
-
Logstash tried to bind to port %{port}, but the port is already in use. You can specify a new port by launching
|
74
|
+
Logstash tried to bind to port %{port}, but the port is already in use. You can specify a new port by launching logstash with the --http.port option."
|
75
75
|
cant_bind_to_port_in_range: |-
|
76
|
-
Logstash tried to bind to port range %{http_ports}, but all the ports are already in use. You can specify a new port by launching
|
76
|
+
Logstash tried to bind to port range %{http_ports}, but all the ports are already in use. You can specify a new port by launching logstash with the --http.port option."
|
77
77
|
hot_threads:
|
78
78
|
title: |-
|
79
79
|
::: {%{hostname}}
|
@@ -98,6 +98,8 @@ en:
|
|
98
98
|
the '-f yourlogstash.conf' flag?
|
99
99
|
reload-without-config-path: >-
|
100
100
|
Configuration reloading also requires passing a configuration path with '-f yourlogstash.conf'
|
101
|
+
locked-data-path: >-
|
102
|
+
Logstash could not be started because there is already another instance using the configured data directory. If you wish to run multiple instances, you must change the "path.data" setting.
|
101
103
|
invalid-shell: >-
|
102
104
|
Invalid option for interactive Ruby shell. Use either "irb" or "pry"
|
103
105
|
configtest-flag-information: |-
|
data/logstash-core.gemspec
CHANGED
@@ -19,9 +19,6 @@ Gem::Specification.new do |gem|
|
|
19
19
|
|
20
20
|
gem.platform = "java"
|
21
21
|
|
22
|
-
gem.add_runtime_dependency "logstash-core-event-java", LOGSTASH_CORE_VERSION
|
23
|
-
gem.add_runtime_dependency "logstash-core-queue-jruby", LOGSTASH_CORE_VERSION
|
24
|
-
|
25
22
|
gem.add_runtime_dependency "pry", "~> 0.10.1" #(Ruby license)
|
26
23
|
gem.add_runtime_dependency "stud", "~> 0.0.19" #(Apache 2.0 license)
|
27
24
|
gem.add_runtime_dependency "clamp", "~> 0.6.5" #(MIT license) for command line args/flags
|
data/spec/api/spec_helper.rb
CHANGED
@@ -31,7 +31,7 @@ end
|
|
31
31
|
##
|
32
32
|
# Class used to wrap and manage the execution of an agent for test,
|
33
33
|
# this helps a lot in order to have a more integrated test for the
|
34
|
-
# web api, could be also used for other use cases if generalized
|
34
|
+
# web api, could be also used for other use cases if generalized enough
|
35
35
|
##
|
36
36
|
class LogStashRunner
|
37
37
|
|
@@ -0,0 +1,291 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/util/wrapped_acked_queue"
|
3
|
+
require "logstash/event"
|
4
|
+
require "logstash/instrument/namespaced_metric"
|
5
|
+
|
6
|
+
describe LogStash::Util::WrappedAckedQueue, :stress_test => true do
|
7
|
+
let(:path) { Stud::Temporary.directory }
|
8
|
+
|
9
|
+
context "with multiple writers" do
|
10
|
+
let(:items) { expected_count / writers }
|
11
|
+
let(:page_capacity) { 1 << page_capacity_multiplier }
|
12
|
+
let(:queue_capacity) { page_capacity * queue_capacity_multiplier }
|
13
|
+
|
14
|
+
let(:output_strings) { [] }
|
15
|
+
let(:reject_memo_keys) { [:reject_memo_keys, :path, :queue, :writer_threads, :collector, :metric, :reader_threads, :output_strings] }
|
16
|
+
|
17
|
+
let(:queue) do
|
18
|
+
described_class.create_file_based(path, page_capacity, 0, queue_checkpoint_acks, queue_checkpoint_writes, queue_checkpoint_interval, queue_capacity)
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:writer_threads) do
|
22
|
+
writer = queue.write_client
|
23
|
+
writers.times.map do |i|
|
24
|
+
Thread.new(i, items, writer) do |_i, _items, _writer|
|
25
|
+
publisher(_items, _writer)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:writers_finished) { Concurrent::AtomicBoolean.new(false) }
|
31
|
+
|
32
|
+
let(:reader_threads) do
|
33
|
+
reader = queue.read_client
|
34
|
+
reader.set_batch_dimensions(batch_size, batch_wait)
|
35
|
+
reader.set_events_metric(metric.namespace([:stats, :events]))
|
36
|
+
reader.set_pipeline_metric(metric.namespace([:stats, :pipelines, :main, :events]))
|
37
|
+
|
38
|
+
readers.times.map do |i|
|
39
|
+
Thread.new(i, reader, counts) do |_i, _reader, _counts|
|
40
|
+
begin
|
41
|
+
tally = 0
|
42
|
+
while true
|
43
|
+
batch = _reader.read_batch
|
44
|
+
break if batch.size.zero? && writers_finished.value == true && queue.queue.is_fully_acked?
|
45
|
+
sleep(rand * 0.01) if simulate_work
|
46
|
+
tally += batch.size
|
47
|
+
batch.close
|
48
|
+
end
|
49
|
+
_counts[_i] = tally
|
50
|
+
# puts("reader #{_i}, tally=#{tally}, _counts=#{_counts.inspect}")
|
51
|
+
rescue => e
|
52
|
+
p :reader_error => e
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def publisher(items, writer)
|
59
|
+
items.times.each do |i|
|
60
|
+
event = LogStash::Event.new("sequence" => "#{i}".ljust(string_size))
|
61
|
+
writer.push(event)
|
62
|
+
end
|
63
|
+
rescue => e
|
64
|
+
p :publisher_error => e
|
65
|
+
end
|
66
|
+
|
67
|
+
let(:collector) { LogStash::Instrument::Collector.new }
|
68
|
+
let(:metric) { LogStash::Instrument::Metric.new(collector) }
|
69
|
+
|
70
|
+
shared_examples "a well behaved queue" do
|
71
|
+
it "writes, reads, closes and reopens" do
|
72
|
+
Thread.abort_on_exception = true
|
73
|
+
|
74
|
+
# force lazy initialization to avoid concurency issues within threads
|
75
|
+
counts
|
76
|
+
queue
|
77
|
+
|
78
|
+
# Start the threads
|
79
|
+
writer_threads
|
80
|
+
reader_threads
|
81
|
+
|
82
|
+
writer_threads.each(&:join)
|
83
|
+
writers_finished.make_true
|
84
|
+
|
85
|
+
reader_threads.each(&:join)
|
86
|
+
|
87
|
+
enqueued = queue.queue.unread_count
|
88
|
+
|
89
|
+
if enqueued != 0
|
90
|
+
output_strings << "unread events in queue: #{enqueued}"
|
91
|
+
end
|
92
|
+
|
93
|
+
got = counts.reduce(&:+)
|
94
|
+
|
95
|
+
if got != expected_count
|
96
|
+
# puts("count=#{counts.inspect}")
|
97
|
+
output_strings << "events read: #{got}"
|
98
|
+
end
|
99
|
+
|
100
|
+
sleep 0.1
|
101
|
+
expect { queue.close }.not_to raise_error
|
102
|
+
sleep 0.1
|
103
|
+
files = Dir.glob(path + '/*').map{|f| f.sub("#{path}/", '')}
|
104
|
+
if files.count != 2
|
105
|
+
output_strings << "File count after close mismatch expected: 2 got: #{files.count}"
|
106
|
+
output_strings.concat files
|
107
|
+
end
|
108
|
+
|
109
|
+
begin
|
110
|
+
queue.queue.open
|
111
|
+
rescue Exception => e
|
112
|
+
output_strings << e.message
|
113
|
+
end
|
114
|
+
|
115
|
+
queue.queue.close
|
116
|
+
|
117
|
+
if output_strings.any?
|
118
|
+
output_strings << __memoized.reject{|k,v| reject_memo_keys.include?(k)}.inspect
|
119
|
+
end
|
120
|
+
|
121
|
+
expect(output_strings).to eq([])
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
let(:writers) { 3 }
|
126
|
+
let(:readers) { 3 }
|
127
|
+
let(:simulate_work) { true }
|
128
|
+
let(:counts) { Concurrent::Array.new([0, 0, 0, 0, 0, 0, 0, 0]) }
|
129
|
+
let(:page_capacity_multiplier) { 20 }
|
130
|
+
let(:queue_capacity_multiplier) { 128 }
|
131
|
+
let(:queue_checkpoint_acks) { 1024 }
|
132
|
+
let(:queue_checkpoint_writes) { 1024 }
|
133
|
+
let(:queue_checkpoint_interval) { 1000 }
|
134
|
+
let(:batch_size) { 500 }
|
135
|
+
let(:batch_wait) { 1000 }
|
136
|
+
let(:expected_count) { 60000 }
|
137
|
+
let(:string_size) { 256 }
|
138
|
+
|
139
|
+
describe "with simulate_work ON" do
|
140
|
+
let(:simulate_work) { true }
|
141
|
+
|
142
|
+
context "> more writers than readers <" do
|
143
|
+
let(:writers) { 4 }
|
144
|
+
let(:readers) { 2 }
|
145
|
+
it_behaves_like "a well behaved queue"
|
146
|
+
end
|
147
|
+
|
148
|
+
context "> less writers than readers <" do
|
149
|
+
let(:writers) { 2 }
|
150
|
+
let(:readers) { 4 }
|
151
|
+
it_behaves_like "a well behaved queue"
|
152
|
+
end
|
153
|
+
|
154
|
+
context "> larger checkpoint acks <" do
|
155
|
+
let(:queue_checkpoint_acks) { 3000 }
|
156
|
+
it_behaves_like "a well behaved queue"
|
157
|
+
end
|
158
|
+
|
159
|
+
context "> smaller checkpoint acks <" do
|
160
|
+
let(:queue_checkpoint_acks) { 500 }
|
161
|
+
it_behaves_like "a well behaved queue"
|
162
|
+
end
|
163
|
+
|
164
|
+
context "> larger checkpoint writes <" do
|
165
|
+
let(:queue_checkpoint_writes) { 3000 }
|
166
|
+
it_behaves_like "a well behaved queue"
|
167
|
+
end
|
168
|
+
|
169
|
+
context "> smaller checkpoint writes <" do
|
170
|
+
let(:queue_checkpoint_writes) { 500 }
|
171
|
+
it_behaves_like "a well behaved queue"
|
172
|
+
end
|
173
|
+
|
174
|
+
context "> larger checkpoint interval <" do
|
175
|
+
let(:queue_checkpoint_interval) { 3000 }
|
176
|
+
it_behaves_like "a well behaved queue"
|
177
|
+
end
|
178
|
+
|
179
|
+
context "> smaller checkpoint interval <" do
|
180
|
+
let(:queue_checkpoint_interval) { 500 }
|
181
|
+
it_behaves_like "a well behaved queue"
|
182
|
+
end
|
183
|
+
|
184
|
+
context "> smaller batch wait <" do
|
185
|
+
let(:batch_wait) { 125 }
|
186
|
+
it_behaves_like "a well behaved queue"
|
187
|
+
end
|
188
|
+
|
189
|
+
context "> larger batch wait <" do
|
190
|
+
let(:batch_wait) { 5000 }
|
191
|
+
it_behaves_like "a well behaved queue"
|
192
|
+
end
|
193
|
+
|
194
|
+
context "> smaller event size <" do
|
195
|
+
let(:string_size) { 8 }
|
196
|
+
it_behaves_like "a well behaved queue"
|
197
|
+
end
|
198
|
+
|
199
|
+
context "> larger event size <" do
|
200
|
+
let(:string_size) { 8192 }
|
201
|
+
it_behaves_like "a well behaved queue"
|
202
|
+
end
|
203
|
+
|
204
|
+
context "> small queue size limit <" do
|
205
|
+
let(:queue_capacity_multiplier) { 10 }
|
206
|
+
it_behaves_like "a well behaved queue"
|
207
|
+
end
|
208
|
+
|
209
|
+
context "> very large queue size limit <" do
|
210
|
+
let(:queue_capacity_multiplier) { 512 }
|
211
|
+
it_behaves_like "a well behaved queue"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe "with simulate_work OFF" do
|
216
|
+
let(:simulate_work) { false }
|
217
|
+
|
218
|
+
context "> more writers than readers <" do
|
219
|
+
let(:writers) { 4 }
|
220
|
+
let(:readers) { 2 }
|
221
|
+
it_behaves_like "a well behaved queue"
|
222
|
+
end
|
223
|
+
|
224
|
+
context "> less writers than readers <" do
|
225
|
+
let(:writers) { 2 }
|
226
|
+
let(:readers) { 4 }
|
227
|
+
it_behaves_like "a well behaved queue"
|
228
|
+
end
|
229
|
+
|
230
|
+
context "> larger checkpoint acks <" do
|
231
|
+
let(:queue_checkpoint_acks) { 3000 }
|
232
|
+
it_behaves_like "a well behaved queue"
|
233
|
+
end
|
234
|
+
|
235
|
+
context "> smaller checkpoint acks <" do
|
236
|
+
let(:queue_checkpoint_acks) { 500 }
|
237
|
+
it_behaves_like "a well behaved queue"
|
238
|
+
end
|
239
|
+
|
240
|
+
context "> larger checkpoint writes <" do
|
241
|
+
let(:queue_checkpoint_writes) { 3000 }
|
242
|
+
it_behaves_like "a well behaved queue"
|
243
|
+
end
|
244
|
+
|
245
|
+
context "> smaller checkpoint writes <" do
|
246
|
+
let(:queue_checkpoint_writes) { 500 }
|
247
|
+
it_behaves_like "a well behaved queue"
|
248
|
+
end
|
249
|
+
|
250
|
+
context "> larger checkpoint interval <" do
|
251
|
+
let(:queue_checkpoint_interval) { 3000 }
|
252
|
+
it_behaves_like "a well behaved queue"
|
253
|
+
end
|
254
|
+
|
255
|
+
context "> smaller checkpoint interval <" do
|
256
|
+
let(:queue_checkpoint_interval) { 500 }
|
257
|
+
it_behaves_like "a well behaved queue"
|
258
|
+
end
|
259
|
+
|
260
|
+
context "> smaller batch wait <" do
|
261
|
+
let(:batch_wait) { 125 }
|
262
|
+
it_behaves_like "a well behaved queue"
|
263
|
+
end
|
264
|
+
|
265
|
+
context "> larger batch wait <" do
|
266
|
+
let(:batch_wait) { 5000 }
|
267
|
+
it_behaves_like "a well behaved queue"
|
268
|
+
end
|
269
|
+
|
270
|
+
context "> smaller event size <" do
|
271
|
+
let(:string_size) { 8 }
|
272
|
+
it_behaves_like "a well behaved queue"
|
273
|
+
end
|
274
|
+
|
275
|
+
context "> larger event size <" do
|
276
|
+
let(:string_size) { 8192 }
|
277
|
+
it_behaves_like "a well behaved queue"
|
278
|
+
end
|
279
|
+
|
280
|
+
context "> small queue size limit <" do
|
281
|
+
let(:queue_capacity_multiplier) { 10 }
|
282
|
+
it_behaves_like "a well behaved queue"
|
283
|
+
end
|
284
|
+
|
285
|
+
context "> very large queue size limit <" do
|
286
|
+
let(:queue_capacity_multiplier) { 512 }
|
287
|
+
it_behaves_like "a well behaved queue"
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
data/spec/logstash/agent_spec.rb
CHANGED
@@ -99,6 +99,30 @@ describe LogStash::Agent do
|
|
99
99
|
subject.register_pipeline(pipeline_settings)
|
100
100
|
end
|
101
101
|
|
102
|
+
context "when a system pipeline is running" do
|
103
|
+
context "when one pipeline is finite" do
|
104
|
+
let(:pipeline_args) {
|
105
|
+
{
|
106
|
+
"path.config" => "a",
|
107
|
+
"config.string" => "input { generator { count => 1000 }} output { null {} }"
|
108
|
+
}
|
109
|
+
}
|
110
|
+
let(:system_pipeline_settings) do
|
111
|
+
s = agent_settings.clone
|
112
|
+
s.set("path.config", "")
|
113
|
+
s.set("config.string", "input { generator {}} output { null {} }")
|
114
|
+
s.set("pipeline.id", ".monitoring")
|
115
|
+
s.set("pipeline.system", true)
|
116
|
+
s
|
117
|
+
end
|
118
|
+
|
119
|
+
it "stops logstash at the end of the execution of the finite pipeline" do
|
120
|
+
subject.register_pipeline(system_pipeline_settings)
|
121
|
+
expect(subject.execute).to be_nil
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
102
126
|
context "if state is clean" do
|
103
127
|
before :each do
|
104
128
|
allow(subject).to receive(:running_pipelines?).and_return(true)
|
@@ -192,7 +192,7 @@ describe LogStash::Config::Mixin do
|
|
192
192
|
expect(clone.uri.to_s).to eql(uri_hidden)
|
193
193
|
end
|
194
194
|
|
195
|
-
it "should make the real URI object
|
195
|
+
it "should make the real URI object available under #uri" do
|
196
196
|
expect(subject.uri.uri).to be_a(::URI)
|
197
197
|
end
|
198
198
|
|
@@ -322,6 +322,9 @@ describe LogStash::Config::Mixin do
|
|
322
322
|
config :oneNumber, :validate => :number, :required => false
|
323
323
|
config :oneArray, :validate => :array, :required => false
|
324
324
|
config :oneHash, :validate => :hash, :required => false
|
325
|
+
config :nestedHash, :validate => :hash, :required => false
|
326
|
+
config :nestedArray, :validate => :hash, :required => false
|
327
|
+
config :deepHash, :validate => :hash, :required => false
|
325
328
|
|
326
329
|
def initialize(params)
|
327
330
|
super(params)
|
@@ -378,7 +381,10 @@ describe LogStash::Config::Mixin do
|
|
378
381
|
"oneString" => "${FunString:foo}",
|
379
382
|
"oneBoolean" => "${FunBool:false}",
|
380
383
|
"oneArray" => [ "first array value", "${FunString:foo}" ],
|
381
|
-
"oneHash" => { "key1" => "${FunString:foo}", "key2" => "${FunString} is ${FunBool}", "key3" => "${FunBool:false} or ${funbool:false}" }
|
384
|
+
"oneHash" => { "key1" => "${FunString:foo}", "key2" => "${FunString} is ${FunBool}", "key3" => "${FunBool:false} or ${funbool:false}" },
|
385
|
+
"nestedHash" => { "level1" => { "key1" => "http://${FunString}:8080/blah.txt" } },
|
386
|
+
"nestedArray" => { "level1" => [{ "key1" => "http://${FunString}:8080/blah.txt" }, { "key2" => "http://${FunString}:8080/foo.txt" }] },
|
387
|
+
"deepHash" => { "level1" => { "level2" => {"level3" => { "key1" => "http://${FunString}:8080/blah.txt" } } } }
|
382
388
|
)
|
383
389
|
end
|
384
390
|
|
@@ -387,6 +393,9 @@ describe LogStash::Config::Mixin do
|
|
387
393
|
expect(subject.oneBoolean).to(be_truthy)
|
388
394
|
expect(subject.oneArray).to(be == [ "first array value", "fancy" ])
|
389
395
|
expect(subject.oneHash).to(be == { "key1" => "fancy", "key2" => "fancy is true", "key3" => "true or false" })
|
396
|
+
expect(subject.nestedHash).to(be == { "level1" => { "key1" => "http://fancy:8080/blah.txt" } })
|
397
|
+
expect(subject.nestedArray).to(be == { "level1" => [{ "key1" => "http://fancy:8080/blah.txt" }, { "key2" => "http://fancy:8080/foo.txt" }] })
|
398
|
+
expect(subject.deepHash).to(be == { "level1" => { "level2" => { "level3" => { "key1" => "http://fancy:8080/blah.txt" } } } })
|
390
399
|
end
|
391
400
|
end
|
392
401
|
|
@@ -34,6 +34,13 @@ describe LogStash::EventDispatcher do
|
|
34
34
|
let(:listener) { CustomSpy }
|
35
35
|
subject(:emitter) { DummyEmitter.new }
|
36
36
|
|
37
|
+
it "ignores duplicate listener" do
|
38
|
+
emitter.dispatcher.add_listener(listener)
|
39
|
+
emitter.dispatcher.add_listener(listener)
|
40
|
+
expect(listener).to receive(:method_exists).with(emitter).once
|
41
|
+
emitter.method_exists
|
42
|
+
end
|
43
|
+
|
37
44
|
describe "Emits events" do
|
38
45
|
before do
|
39
46
|
emitter.dispatcher.add_listener(listener)
|
@@ -65,7 +72,7 @@ describe LogStash::EventDispatcher do
|
|
65
72
|
emitter.method_exists
|
66
73
|
end
|
67
74
|
|
68
|
-
it "allows to remove a
|
75
|
+
it "allows to remove a listener to an emitter" do
|
69
76
|
expect(listener).to receive(:method_exists).with(emitter).once
|
70
77
|
emitter.dispatcher.add_listener(listener)
|
71
78
|
emitter.method_exists
|
@@ -0,0 +1,346 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require "logstash/util"
|
5
|
+
require "logstash/event"
|
6
|
+
require "json"
|
7
|
+
require "java"
|
8
|
+
|
9
|
+
TIMESTAMP = "@timestamp"
|
10
|
+
|
11
|
+
describe LogStash::Event do
|
12
|
+
context "to_json" do
|
13
|
+
it "should serialize simple values" do
|
14
|
+
e = LogStash::Event.new({"foo" => "bar", "bar" => 1, "baz" => 1.0, TIMESTAMP => "2015-05-28T23:02:05.350Z"})
|
15
|
+
expect(JSON.parse(e.to_json)).to eq(JSON.parse("{\"foo\":\"bar\",\"bar\":1,\"baz\":1.0,\"@timestamp\":\"2015-05-28T23:02:05.350Z\",\"@version\":\"1\"}"))
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should serialize deep hash values" do
|
19
|
+
e = LogStash::Event.new({"foo" => {"bar" => 1, "baz" => 1.0, "biz" => "boz"}, TIMESTAMP => "2015-05-28T23:02:05.350Z"})
|
20
|
+
expect(JSON.parse(e.to_json)).to eq(JSON.parse("{\"foo\":{\"bar\":1,\"baz\":1.0,\"biz\":\"boz\"},\"@timestamp\":\"2015-05-28T23:02:05.350Z\",\"@version\":\"1\"}"))
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should serialize deep array values" do
|
24
|
+
e = LogStash::Event.new({"foo" => ["bar", 1, 1.0], TIMESTAMP => "2015-05-28T23:02:05.350Z"})
|
25
|
+
expect(JSON.parse(e.to_json)).to eq(JSON.parse("{\"foo\":[\"bar\",1,1.0],\"@timestamp\":\"2015-05-28T23:02:05.350Z\",\"@version\":\"1\"}"))
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should serialize deep hash from field reference assignments" do
|
29
|
+
e = LogStash::Event.new({TIMESTAMP => "2015-05-28T23:02:05.350Z"})
|
30
|
+
e.set("foo", "bar")
|
31
|
+
e.set("bar", 1)
|
32
|
+
e.set("baz", 1.0)
|
33
|
+
e.set("[fancy][pants][socks]", "shoes")
|
34
|
+
expect(JSON.parse(e.to_json)).to eq(JSON.parse("{\"@timestamp\":\"2015-05-28T23:02:05.350Z\",\"@version\":\"1\",\"foo\":\"bar\",\"bar\":1,\"baz\":1.0,\"fancy\":{\"pants\":{\"socks\":\"shoes\"}}}"))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "#get" do
|
39
|
+
it "should get simple values" do
|
40
|
+
e = LogStash::Event.new({"foo" => "bar", "bar" => 1, "baz" => 1.0, TIMESTAMP => "2015-05-28T23:02:05.350Z"})
|
41
|
+
expect(e.get("foo")).to eq("bar")
|
42
|
+
expect(e.get("[foo]")).to eq("bar")
|
43
|
+
expect(e.get("bar")).to eq(1)
|
44
|
+
expect(e.get("[bar]")).to eq(1)
|
45
|
+
expect(e.get("baz")).to eq(1.0)
|
46
|
+
expect(e.get("[baz]")).to eq(1.0)
|
47
|
+
expect(e.get(TIMESTAMP).to_s).to eq("2015-05-28T23:02:05.350Z")
|
48
|
+
expect(e.get("[#{TIMESTAMP}]").to_s).to eq("2015-05-28T23:02:05.350Z")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should get deep hash values" do
|
52
|
+
e = LogStash::Event.new({"foo" => {"bar" => 1, "baz" => 1.0}})
|
53
|
+
expect(e.get("[foo][bar]")).to eq(1)
|
54
|
+
expect(e.get("[foo][baz]")).to eq(1.0)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should get deep array values" do
|
58
|
+
e = LogStash::Event.new({"foo" => ["bar", 1, 1.0]})
|
59
|
+
expect(e.get("[foo][0]")).to eq("bar")
|
60
|
+
expect(e.get("[foo][1]")).to eq(1)
|
61
|
+
expect(e.get("[foo][2]")).to eq(1.0)
|
62
|
+
expect(e.get("[foo][3]")).to be_nil
|
63
|
+
end
|
64
|
+
|
65
|
+
context "negative array values" do
|
66
|
+
it "should index from the end of the array" do
|
67
|
+
list = ["bar", 1, 1.0]
|
68
|
+
e = LogStash::Event.new({"foo" => list})
|
69
|
+
expect(e.get("[foo][-3]")).to eq(list[-3])
|
70
|
+
expect(e.get("[foo][-2]")).to eq(list[-2])
|
71
|
+
expect(e.get("[foo][-1]")).to eq(list[-1])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "#set" do
|
77
|
+
it "should set simple values" do
|
78
|
+
e = LogStash::Event.new()
|
79
|
+
expect(e.set("foo", "bar")).to eq("bar")
|
80
|
+
expect(e.get("foo")).to eq("bar")
|
81
|
+
|
82
|
+
e = LogStash::Event.new({"foo" => "test"})
|
83
|
+
expect(e.set("foo", "bar")).to eq("bar")
|
84
|
+
expect(e.get("foo")).to eq("bar")
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should set deep hash values" do
|
88
|
+
e = LogStash::Event.new()
|
89
|
+
expect(e.set("[foo][bar]", "baz")).to eq("baz")
|
90
|
+
expect(e.get("[foo][bar]")).to eq("baz")
|
91
|
+
expect(e.get("[foo][baz]")).to be_nil
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should set deep array values" do
|
95
|
+
e = LogStash::Event.new()
|
96
|
+
expect(e.set("[foo][0]", "bar")).to eq("bar")
|
97
|
+
expect(e.get("[foo][0]")).to eq("bar")
|
98
|
+
expect(e.set("[foo][1]", 1)).to eq(1)
|
99
|
+
expect(e.get("[foo][1]")).to eq(1)
|
100
|
+
expect(e.set("[foo][2]", 1.0)).to eq(1.0)
|
101
|
+
expect(e.get("[foo][2]")).to eq(1.0)
|
102
|
+
expect(e.get("[foo][3]")).to be_nil
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should add key when setting nil value" do
|
106
|
+
e = LogStash::Event.new()
|
107
|
+
e.set("[foo]", nil)
|
108
|
+
expect(e.to_hash).to include("foo" => nil)
|
109
|
+
end
|
110
|
+
|
111
|
+
# BigDecimal is now natively converted by JRuby, see https://github.com/elastic/logstash/pull/4838
|
112
|
+
it "should set BigDecimal" do
|
113
|
+
e = LogStash::Event.new()
|
114
|
+
e.set("[foo]", BigDecimal.new(1))
|
115
|
+
expect(e.get("foo")).to be_kind_of(BigDecimal)
|
116
|
+
expect(e.get("foo")).to eq(BigDecimal.new(1))
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should set RubyBignum" do
|
120
|
+
e = LogStash::Event.new()
|
121
|
+
e.set("[foo]", -9223372036854776000)
|
122
|
+
expect(e.get("foo")).to be_kind_of(Bignum)
|
123
|
+
expect(e.get("foo")).to eq(-9223372036854776000)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should convert Time to Timestamp" do
|
127
|
+
e = LogStash::Event.new()
|
128
|
+
time = Time.now
|
129
|
+
e.set("[foo]", Time.at(time.to_f))
|
130
|
+
expect(e.get("foo")).to be_kind_of(LogStash::Timestamp)
|
131
|
+
expect(e.get("foo").to_f).to be_within(0.1).of(time.to_f)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should set XXJavaProxy Jackson crafted" do
|
135
|
+
proxy = org.logstash.Util.getMapFixtureJackson()
|
136
|
+
# proxy is {"string": "foo", "int": 42, "float": 42.42, "array": ["bar","baz"], "hash": {"string":"quux"} }
|
137
|
+
e = LogStash::Event.new()
|
138
|
+
e.set("[proxy]", proxy)
|
139
|
+
expect(e.get("[proxy][string]")).to eql("foo")
|
140
|
+
expect(e.get("[proxy][int]")).to eql(42)
|
141
|
+
expect(e.get("[proxy][float]")).to eql(42.42)
|
142
|
+
expect(e.get("[proxy][array][0]")).to eql("bar")
|
143
|
+
expect(e.get("[proxy][array][1]")).to eql("baz")
|
144
|
+
expect(e.get("[proxy][hash][string]")).to eql("quux")
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should set XXJavaProxy hand crafted" do
|
148
|
+
proxy = org.logstash.Util.getMapFixtureHandcrafted()
|
149
|
+
# proxy is {"string": "foo", "int": 42, "float": 42.42, "array": ["bar","baz"], "hash": {"string":"quux"} }
|
150
|
+
e = LogStash::Event.new()
|
151
|
+
e.set("[proxy]", proxy)
|
152
|
+
expect(e.get("[proxy][string]")).to eql("foo")
|
153
|
+
expect(e.get("[proxy][int]")).to eql(42)
|
154
|
+
expect(e.get("[proxy][float]")).to eql(42.42)
|
155
|
+
expect(e.get("[proxy][array][0]")).to eql("bar")
|
156
|
+
expect(e.get("[proxy][array][1]")).to eql("baz")
|
157
|
+
expect(e.get("[proxy][hash][string]")).to eql("quux")
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should fail on non UTF-8 encoding" do
|
161
|
+
# e = LogStash::Event.new
|
162
|
+
# s1 = "\xE0 Montr\xE9al".force_encoding("ISO-8859-1")
|
163
|
+
# expect(s1.encoding.name).to eq("ISO-8859-1")
|
164
|
+
# expect(s1.valid_encoding?).to eq(true)
|
165
|
+
# e.set("test", s1)
|
166
|
+
# s2 = e.get("test")
|
167
|
+
# expect(s2.encoding.name).to eq("UTF-8")
|
168
|
+
# expect(s2.valid_encoding?).to eq(true)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "timestamp" do
|
173
|
+
it "getters should present a Ruby LogStash::Timestamp" do
|
174
|
+
e = LogStash::Event.new()
|
175
|
+
expect(e.timestamp.class).to eq(LogStash::Timestamp)
|
176
|
+
expect(e.get(TIMESTAMP).class).to eq(LogStash::Timestamp)
|
177
|
+
end
|
178
|
+
|
179
|
+
it "to_hash should inject a Ruby LogStash::Timestamp" do
|
180
|
+
e = LogStash::Event.new()
|
181
|
+
|
182
|
+
expect(e.to_java).to be_kind_of(Java::OrgLogstash::Event)
|
183
|
+
expect(e.to_java.get_field(TIMESTAMP)).to be_kind_of(Java::OrgLogstash::Timestamp)
|
184
|
+
|
185
|
+
expect(e.to_hash[TIMESTAMP]).to be_kind_of(LogStash::Timestamp)
|
186
|
+
# now make sure the original map was not touched
|
187
|
+
expect(e.to_java.get_field(TIMESTAMP)).to be_kind_of(Java::OrgLogstash::Timestamp)
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should set timestamp" do
|
191
|
+
e = LogStash::Event.new
|
192
|
+
now = Time.now
|
193
|
+
e.set("@timestamp", LogStash::Timestamp.at(now.to_i))
|
194
|
+
expect(e.timestamp.to_i).to eq(now.to_i)
|
195
|
+
expect(e.get("@timestamp").to_i).to eq(now.to_i)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
context "append" do
|
200
|
+
it "should append" do
|
201
|
+
event = LogStash::Event.new("message" => "hello world")
|
202
|
+
event.append(LogStash::Event.new("message" => "another thing"))
|
203
|
+
expect(event.get("message")).to eq(["hello world", "another thing"])
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context "tags" do
|
208
|
+
it "should tag" do
|
209
|
+
event = LogStash::Event.new("message" => "hello world")
|
210
|
+
expect(event.get("tags")).to be_nil
|
211
|
+
event.tag("foo")
|
212
|
+
expect(event.get("tags")).to eq(["foo"])
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
# TODO(talevy): migrate tests to Java. no reason to test logging logic in ruby when it is being
|
218
|
+
# done in java land.
|
219
|
+
|
220
|
+
# context "logger" do
|
221
|
+
|
222
|
+
# let(:logger) { double("Logger") }
|
223
|
+
|
224
|
+
# before(:each) do
|
225
|
+
# allow(LogStash::Event).to receive(:logger).and_return(logger)
|
226
|
+
# end
|
227
|
+
|
228
|
+
# it "should set logger using a module" do
|
229
|
+
# expect(logger).to receive(:warn).once
|
230
|
+
# LogStash::Event.new(TIMESTAMP => "invalid timestamp")
|
231
|
+
# end
|
232
|
+
|
233
|
+
# it "should warn on invalid timestamp object" do
|
234
|
+
# expect(logger).to receive(:warn).once.with(/^Unrecognized/)
|
235
|
+
# LogStash::Event.new(TIMESTAMP => Array.new)
|
236
|
+
# end
|
237
|
+
# end
|
238
|
+
|
239
|
+
context "to_hash" do
|
240
|
+
let (:source_hash) { {"a" => 1, "b" => [1, 2, 3, {"h" => 1, "i" => "baz"}], "c" => {"d" => "foo", "e" => "bar", "f" => [4, 5, "six"]}} }
|
241
|
+
let (:source_hash_with_metadata) { source_hash.merge({"@metadata" => {"a" => 1, "b" => 2}}) }
|
242
|
+
subject { LogStash::Event.new(source_hash_with_metadata) }
|
243
|
+
|
244
|
+
it "should include @timestamp and @version" do
|
245
|
+
h = subject.to_hash
|
246
|
+
expect(h).to include("@timestamp")
|
247
|
+
expect(h).to include("@version")
|
248
|
+
expect(h).not_to include("@metadata")
|
249
|
+
end
|
250
|
+
|
251
|
+
it "should include @timestamp and @version and @metadata" do
|
252
|
+
h = subject.to_hash_with_metadata
|
253
|
+
expect(h).to include("@timestamp")
|
254
|
+
expect(h).to include("@version")
|
255
|
+
expect(h).to include("@metadata")
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should produce valid deep Ruby hash without metadata" do
|
259
|
+
h = subject.to_hash
|
260
|
+
h.delete("@timestamp")
|
261
|
+
h.delete("@version")
|
262
|
+
expect(h).to eq(source_hash)
|
263
|
+
end
|
264
|
+
|
265
|
+
it "should produce valid deep Ruby hash with metadata" do
|
266
|
+
h = subject.to_hash_with_metadata
|
267
|
+
h.delete("@timestamp")
|
268
|
+
h.delete("@version")
|
269
|
+
expect(h).to eq(source_hash_with_metadata)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
context "from_json" do
|
274
|
+
let (:source_json) { "{\"foo\":1, \"bar\":\"baz\"}" }
|
275
|
+
let (:blank_strings) {["", " ", " "]}
|
276
|
+
let (:bare_strings) {["aa", " aa", "aa "]}
|
277
|
+
|
278
|
+
it "should produce a new event from json" do
|
279
|
+
expect(LogStash::Event.from_json(source_json).size).to eq(1)
|
280
|
+
|
281
|
+
event = LogStash::Event.from_json(source_json)[0]
|
282
|
+
expect(event.get("[foo]")).to eq(1)
|
283
|
+
expect(event.get("[bar]")).to eq("baz")
|
284
|
+
end
|
285
|
+
|
286
|
+
it "should ignore blank strings" do
|
287
|
+
blank_strings.each do |s|
|
288
|
+
expect(LogStash::Event.from_json(s).size).to eq(0)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
it "should raise TypeError on nil string" do
|
293
|
+
expect{LogStash::Event.from_json(nil)}.to raise_error TypeError
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should consistently handle nil" do
|
297
|
+
blank_strings.each do |s|
|
298
|
+
expect{LogStash::Event.from_json(nil)}.to raise_error
|
299
|
+
expect{LogStash::Event.new(LogStash::Json.load(nil))}.to raise_error
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
it "should consistently handle bare string" do
|
304
|
+
bare_strings.each do |s|
|
305
|
+
expect{LogStash::Event.from_json(s)}.to raise_error LogStash::Json::ParserError
|
306
|
+
expect{LogStash::Event.new(LogStash::Json.load(s))}.to raise_error LogStash::Json::ParserError
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
context "initialize" do
|
312
|
+
|
313
|
+
it "should accept Ruby Hash" do
|
314
|
+
e = LogStash::Event.new({"foo" => 1, TIMESTAMP => "2015-05-28T23:02:05.350Z"})
|
315
|
+
expect(e.get("foo")).to eq(1)
|
316
|
+
expect(e.timestamp.to_iso8601).to eq("2015-05-28T23:02:05.350Z")
|
317
|
+
end
|
318
|
+
|
319
|
+
it "should accept Java Map" do
|
320
|
+
h = Java::JavaUtil::HashMap.new
|
321
|
+
h.put("foo", 2);
|
322
|
+
h.put(TIMESTAMP, "2016-05-28T23:02:05.350Z");
|
323
|
+
e = LogStash::Event.new(h)
|
324
|
+
|
325
|
+
expect(e.get("foo")).to eq(2)
|
326
|
+
expect(e.timestamp.to_iso8601).to eq("2016-05-28T23:02:05.350Z")
|
327
|
+
end
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
context "method missing exception messages" do
|
332
|
+
subject { LogStash::Event.new({"foo" => "bar"}) }
|
333
|
+
|
334
|
+
it "#[] method raises a better exception message" do
|
335
|
+
expect { subject["foo"] }.to raise_error(NoMethodError, /Direct event field references \(i\.e\. event\['field'\]\)/)
|
336
|
+
end
|
337
|
+
|
338
|
+
it "#[]= method raises a better exception message" do
|
339
|
+
expect { subject["foo"] = "baz" }.to raise_error(NoMethodError, /Direct event field references \(i\.e\. event\['field'\] = 'value'\)/)
|
340
|
+
end
|
341
|
+
|
342
|
+
it "other missing method raises normal exception message" do
|
343
|
+
expect { subject.baz() }.to raise_error(NoMethodError, /undefined method `baz' for/)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|