logstash-core 2.0.1.snapshot1-java → 2.1.0-java
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of logstash-core might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/logstash/agent.rb +21 -6
- data/lib/logstash/config/mixin.rb +0 -6
- data/lib/logstash/event.rb +1 -1
- data/lib/logstash/filters/base.rb +1 -2
- data/lib/logstash/inputs/base.rb +0 -5
- data/lib/logstash/outputs/base.rb +7 -5
- data/lib/logstash/pipeline.rb +85 -15
- data/lib/logstash/plugin.rb +5 -0
- data/lib/logstash/shutdown_controller.rb +127 -0
- data/lib/logstash/util.rb +35 -0
- data/lib/logstash/util/defaults_printer.rb +1 -1
- data/lib/logstash/util/worker_threads_default_printer.rb +14 -2
- data/lib/logstash/version.rb +1 -1
- data/locales/en.yml +5 -0
- data/logstash-core.gemspec +2 -1
- data/spec/core/event_spec.rb +19 -0
- data/spec/core/pipeline_spec.rb +113 -7
- data/spec/core/shutdown_controller_spec.rb +107 -0
- data/spec/license_spec.rb +1 -0
- data/spec/plugin_manager/install_spec.rb +28 -0
- data/spec/plugin_manager/update_spec.rb +39 -0
- data/spec/plugin_manager/util_spec.rb +71 -0
- data/spec/util/compress_spec.rb +121 -0
- data/spec/util/defaults_printer_spec.rb +3 -2
- data/spec/util/worker_threads_default_printer_spec.rb +30 -11
- metadata +76 -54
- data/lib/logstash/util/reporter.rb +0 -28
- data/spec/pluginmanager/util_spec.rb +0 -42
data/lib/logstash/util.rb
CHANGED
@@ -24,6 +24,41 @@ module LogStash::Util
|
|
24
24
|
end
|
25
25
|
end # def set_thread_name
|
26
26
|
|
27
|
+
def self.set_thread_plugin(plugin)
|
28
|
+
Thread.current[:plugin] = plugin
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.get_thread_id(thread)
|
32
|
+
if RUBY_ENGINE == "jruby"
|
33
|
+
JRuby.reference(thread).native_thread.id
|
34
|
+
else
|
35
|
+
raise Exception.new("Native thread IDs aren't supported outside of JRuby")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.thread_info(thread)
|
40
|
+
backtrace = thread.backtrace.map do |line|
|
41
|
+
line.gsub(LogStash::Environment::LOGSTASH_HOME, "[...]")
|
42
|
+
end
|
43
|
+
|
44
|
+
blocked_on = case backtrace.first
|
45
|
+
when /in `push'/ then "blocked_on_push"
|
46
|
+
when /(?:pipeline|base).*pop/ then "waiting_for_events"
|
47
|
+
else nil
|
48
|
+
end
|
49
|
+
|
50
|
+
{
|
51
|
+
"thread_id" => get_thread_id(thread),
|
52
|
+
"name" => thread[:name],
|
53
|
+
"plugin" => (thread[:plugin] ? thread[:plugin].debug_info : nil),
|
54
|
+
"backtrace" => backtrace,
|
55
|
+
"blocked_on" => blocked_on,
|
56
|
+
"status" => thread.status,
|
57
|
+
"current_call" => backtrace.first
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
|
27
62
|
# Merge hash 'src' into 'dst' nondestructively
|
28
63
|
#
|
29
64
|
# Duplicate keys will become array values
|
@@ -6,11 +6,23 @@ require "logstash/util"
|
|
6
6
|
module LogStash module Util class WorkerThreadsDefaultPrinter
|
7
7
|
|
8
8
|
def initialize(settings)
|
9
|
-
@setting = settings.fetch('filter-workers',
|
9
|
+
@setting = settings.fetch('filter-workers', 0)
|
10
|
+
@default = settings.fetch('default-filter-workers', 0)
|
10
11
|
end
|
11
12
|
|
12
13
|
def visit(collector)
|
13
|
-
collector
|
14
|
+
visit_setting(collector)
|
15
|
+
visit_default(collector)
|
16
|
+
end
|
17
|
+
|
18
|
+
def visit_setting(collector)
|
19
|
+
return if @setting == 0
|
20
|
+
collector.push("User set filter workers: #{@setting}")
|
21
|
+
end
|
22
|
+
|
23
|
+
def visit_default(collector)
|
24
|
+
return if @default == 0
|
25
|
+
collector.push "Default filter workers: #{@default}"
|
14
26
|
end
|
15
27
|
|
16
28
|
end end end
|
data/lib/logstash/version.rb
CHANGED
data/locales/en.yml
CHANGED
@@ -187,3 +187,8 @@ en:
|
|
187
187
|
debug: |+
|
188
188
|
Most verbose logging. This causes 'debug'
|
189
189
|
level logs to be emitted.
|
190
|
+
unsafe_shutdown: |+
|
191
|
+
Force logstash to exit during shutdown even
|
192
|
+
if there are still inflight events in memory.
|
193
|
+
By default, logstash will refuse to quit until all
|
194
|
+
received events have been pushed to the outputs.
|
data/logstash-core.gemspec
CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |gem|
|
|
23
23
|
gem.add_runtime_dependency "clamp", "~> 0.6.5" #(MIT license) for command line args/flags
|
24
24
|
gem.add_runtime_dependency "filesize", "0.0.4" #(MIT license) for :bytes config validator
|
25
25
|
gem.add_runtime_dependency "gems", "~> 0.8.3" #(MIT license)
|
26
|
-
gem.add_runtime_dependency "concurrent-ruby", "
|
26
|
+
gem.add_runtime_dependency "concurrent-ruby", "0.9.2"
|
27
27
|
gem.add_runtime_dependency "jruby-openssl", ">= 0.9.11" # Required to support TLSv1.2
|
28
28
|
|
29
29
|
# TODO(sissel): Treetop 1.5.x doesn't seem to work well, but I haven't
|
@@ -35,6 +35,7 @@ Gem::Specification.new do |gem|
|
|
35
35
|
|
36
36
|
# filetools and rakelib
|
37
37
|
gem.add_runtime_dependency "minitar", "~> 0.5.4"
|
38
|
+
gem.add_runtime_dependency "rubyzip", "~> 1.1.7"
|
38
39
|
gem.add_runtime_dependency "thread_safe", "~> 0.3.5" #(Apache 2.0 license)
|
39
40
|
|
40
41
|
if RUBY_PLATFORM == 'java'
|
data/spec/core/event_spec.rb
CHANGED
@@ -496,4 +496,23 @@ describe LogStash::Event do
|
|
496
496
|
subject{LogStash::Event.new(LogStash::Json.load(LogStash::Json.dump(event_hash)))}
|
497
497
|
end
|
498
498
|
end
|
499
|
+
|
500
|
+
|
501
|
+
describe "#to_s" do
|
502
|
+
let(:timestamp) { LogStash::Timestamp.new }
|
503
|
+
let(:event1) { LogStash::Event.new({ "@timestamp" => timestamp, "host" => "foo", "message" => "bar"}) }
|
504
|
+
let(:event2) { LogStash::Event.new({ "host" => "bar", "message" => "foo"}) }
|
505
|
+
|
506
|
+
it "should cache only one template" do
|
507
|
+
LogStash::StringInterpolation::CACHE.clear
|
508
|
+
expect {
|
509
|
+
event1.to_s
|
510
|
+
event2.to_s
|
511
|
+
}.to change { LogStash::StringInterpolation::CACHE.size }.by(1)
|
512
|
+
end
|
513
|
+
|
514
|
+
it "return the string containing the timestamp, the host and the message" do
|
515
|
+
expect(event1.to_s).to eq("#{timestamp.to_iso8601} #{event1["host"]} #{event1["message"]}")
|
516
|
+
end
|
517
|
+
end
|
499
518
|
end
|
data/spec/core/pipeline_spec.rb
CHANGED
@@ -53,20 +53,127 @@ class DummyOutput < LogStash::Outputs::Base
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
+
class DummyFilter < LogStash::Filters::Base
|
57
|
+
config_name "dummyfilter"
|
58
|
+
milestone 2
|
59
|
+
|
60
|
+
def register() end
|
61
|
+
|
62
|
+
def filter(event) end
|
63
|
+
|
64
|
+
def threadsafe?() false; end
|
65
|
+
|
66
|
+
def close() end
|
67
|
+
end
|
68
|
+
|
69
|
+
class DummySafeFilter < LogStash::Filters::Base
|
70
|
+
config_name "dummysafefilter"
|
71
|
+
milestone 2
|
72
|
+
|
73
|
+
def register() end
|
74
|
+
|
75
|
+
def filter(event) end
|
76
|
+
|
77
|
+
def threadsafe?() true; end
|
78
|
+
|
79
|
+
def close() end
|
80
|
+
end
|
81
|
+
|
56
82
|
class TestPipeline < LogStash::Pipeline
|
57
|
-
attr_reader :outputs
|
83
|
+
attr_reader :outputs, :filter_threads, :settings, :logger
|
58
84
|
end
|
59
85
|
|
60
86
|
describe LogStash::Pipeline do
|
87
|
+
let(:worker_thread_count) { 8 }
|
88
|
+
let(:safe_thread_count) { 1 }
|
89
|
+
let(:override_thread_count) { 42 }
|
90
|
+
|
91
|
+
describe "defaulting the filter workers based on thread safety" do
|
92
|
+
before(:each) do
|
93
|
+
allow(LogStash::Plugin).to receive(:lookup).with("input", "dummyinput").and_return(DummyInput)
|
94
|
+
allow(LogStash::Plugin).to receive(:lookup).with("codec", "plain").and_return(DummyCodec)
|
95
|
+
allow(LogStash::Plugin).to receive(:lookup).with("output", "dummyoutput").and_return(DummyOutput)
|
96
|
+
allow(LogStash::Plugin).to receive(:lookup).with("filter", "dummyfilter").and_return(DummyFilter)
|
97
|
+
allow(LogStash::Plugin).to receive(:lookup).with("filter", "dummysafefilter").and_return(DummySafeFilter)
|
98
|
+
allow(LogStash::Config::CpuCoreStrategy).to receive(:fifty_percent).and_return(worker_thread_count)
|
99
|
+
end
|
100
|
+
|
101
|
+
context "when there are some not threadsafe filters" do
|
102
|
+
let(:test_config_with_filters) {
|
103
|
+
<<-eos
|
104
|
+
input {
|
105
|
+
dummyinput {}
|
106
|
+
}
|
107
|
+
|
108
|
+
filter {
|
109
|
+
dummyfilter {}
|
110
|
+
}
|
111
|
+
|
112
|
+
output {
|
113
|
+
dummyoutput {}
|
114
|
+
}
|
115
|
+
eos
|
116
|
+
}
|
117
|
+
|
118
|
+
context "when there is no command line -w N set" do
|
119
|
+
it "starts one filter thread" do
|
120
|
+
msg = "Defaulting filter worker threads to 1 because there are some" +
|
121
|
+
" filters that might not work with multiple worker threads"
|
122
|
+
pipeline = TestPipeline.new(test_config_with_filters)
|
123
|
+
expect(pipeline.logger).to receive(:warn).with(msg,
|
124
|
+
{:count_was=>worker_thread_count, :filters=>["dummyfilter"]})
|
125
|
+
pipeline.run
|
126
|
+
expect(pipeline.filter_threads.size).to eq(safe_thread_count)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context "when there is command line -w N set" do
|
131
|
+
it "starts multiple filter thread" do
|
132
|
+
msg = "Warning: Manual override - there are filters that might" +
|
133
|
+
" not work with multiple worker threads"
|
134
|
+
pipeline = TestPipeline.new(test_config_with_filters)
|
135
|
+
expect(pipeline.logger).to receive(:warn).with(msg,
|
136
|
+
{:worker_threads=> override_thread_count, :filters=>["dummyfilter"]})
|
137
|
+
pipeline.configure("filter-workers", override_thread_count)
|
138
|
+
pipeline.run
|
139
|
+
expect(pipeline.filter_threads.size).to eq(override_thread_count)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "when there are threadsafe filters only" do
|
145
|
+
let(:test_config_with_filters) {
|
146
|
+
<<-eos
|
147
|
+
input {
|
148
|
+
dummyinput {}
|
149
|
+
}
|
150
|
+
|
151
|
+
filter {
|
152
|
+
dummysafefilter {}
|
153
|
+
}
|
61
154
|
|
62
|
-
|
155
|
+
output {
|
156
|
+
dummyoutput {}
|
157
|
+
}
|
158
|
+
eos
|
159
|
+
}
|
63
160
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
161
|
+
it "starts multiple filter threads" do
|
162
|
+
pipeline = TestPipeline.new(test_config_with_filters)
|
163
|
+
pipeline.run
|
164
|
+
expect(pipeline.filter_threads.size).to eq(worker_thread_count)
|
165
|
+
end
|
166
|
+
end
|
68
167
|
end
|
69
168
|
|
169
|
+
context "close" do
|
170
|
+
before(:each) do
|
171
|
+
allow(LogStash::Plugin).to receive(:lookup).with("input", "dummyinput").and_return(DummyInput)
|
172
|
+
allow(LogStash::Plugin).to receive(:lookup).with("codec", "plain").and_return(DummyCodec)
|
173
|
+
allow(LogStash::Plugin).to receive(:lookup).with("output", "dummyoutput").and_return(DummyOutput)
|
174
|
+
end
|
175
|
+
|
176
|
+
|
70
177
|
let(:test_config_without_output_workers) {
|
71
178
|
<<-eos
|
72
179
|
input {
|
@@ -191,6 +298,5 @@ context "close" do
|
|
191
298
|
expect(subject[2]["foo"]).to eq("bar")
|
192
299
|
end
|
193
300
|
end
|
194
|
-
|
195
301
|
end
|
196
302
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "spec_helper"
|
3
|
+
require "logstash/shutdown_controller"
|
4
|
+
|
5
|
+
describe LogStash::ShutdownController do
|
6
|
+
|
7
|
+
let(:check_every) { 0.01 }
|
8
|
+
let(:check_threshold) { 100 }
|
9
|
+
subject { LogStash::ShutdownController.new(pipeline, check_every) }
|
10
|
+
let(:pipeline) { double("pipeline") }
|
11
|
+
report_count = 0
|
12
|
+
|
13
|
+
before :each do
|
14
|
+
allow(LogStash::Report).to receive(:from_pipeline).and_wrap_original do |m, *args|
|
15
|
+
report_count += 1
|
16
|
+
m.call(*args)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
after :each do
|
21
|
+
report_count = 0
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when pipeline is stalled" do
|
25
|
+
let(:increasing_count) { (1..5000).to_a.map {|i| { "total" => i } } }
|
26
|
+
before :each do
|
27
|
+
allow(pipeline).to receive(:inflight_count).and_return(*increasing_count)
|
28
|
+
allow(pipeline).to receive(:stalling_threads) { { } }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe ".unsafe_shutdown = true" do
|
32
|
+
let(:abort_threshold) { subject.abort_threshold }
|
33
|
+
let(:report_every) { subject.report_every }
|
34
|
+
|
35
|
+
before :each do
|
36
|
+
subject.class.unsafe_shutdown = true
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should force the shutdown" do
|
40
|
+
expect(subject).to receive(:force_exit).once
|
41
|
+
subject.start
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should do exactly \"abort_threshold\" stall checks" do
|
45
|
+
allow(subject).to receive(:force_exit)
|
46
|
+
expect(subject).to receive(:shutdown_stalled?).exactly(abort_threshold).times.and_call_original
|
47
|
+
subject.start
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should do exactly \"abort_threshold\"*\"report_every\" stall checks" do
|
51
|
+
allow(subject).to receive(:force_exit)
|
52
|
+
expect(LogStash::Report).to receive(:from_pipeline).exactly(abort_threshold*report_every).times.and_call_original
|
53
|
+
subject.start
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe ".unsafe_shutdown = false" do
|
58
|
+
|
59
|
+
before :each do
|
60
|
+
subject.class.unsafe_shutdown = false
|
61
|
+
end
|
62
|
+
|
63
|
+
it "shouldn't force the shutdown" do
|
64
|
+
expect(subject).to_not receive(:force_exit)
|
65
|
+
thread = Thread.new(subject) {|subject| subject.start }
|
66
|
+
sleep 0.1 until report_count > check_threshold
|
67
|
+
thread.kill
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when pipeline is not stalled" do
|
73
|
+
let(:decreasing_count) { (1..5000).to_a.reverse.map {|i| { "total" => i } } }
|
74
|
+
before :each do
|
75
|
+
allow(pipeline).to receive(:inflight_count).and_return(*decreasing_count)
|
76
|
+
allow(pipeline).to receive(:stalling_threads) { { } }
|
77
|
+
end
|
78
|
+
|
79
|
+
describe ".unsafe_shutdown = true" do
|
80
|
+
|
81
|
+
before :each do
|
82
|
+
subject.class.unsafe_shutdown = true
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should force the shutdown" do
|
86
|
+
expect(subject).to_not receive(:force_exit)
|
87
|
+
thread = Thread.new(subject) {|subject| subject.start }
|
88
|
+
sleep 0.1 until report_count > check_threshold
|
89
|
+
thread.kill
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe ".unsafe_shutdown = false" do
|
94
|
+
|
95
|
+
before :each do
|
96
|
+
subject.class.unsafe_shutdown = false
|
97
|
+
end
|
98
|
+
|
99
|
+
it "shouldn't force the shutdown" do
|
100
|
+
expect(subject).to_not receive(:force_exit)
|
101
|
+
thread = Thread.new(subject) {|subject| subject.start }
|
102
|
+
sleep 0.1 until report_count > check_threshold
|
103
|
+
thread.kill
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/spec/license_spec.rb
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'pluginmanager/main'
|
4
|
+
|
5
|
+
describe LogStash::PluginManager::Install do
|
6
|
+
let(:cmd) { LogStash::PluginManager::Install.new("install") }
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
expect(cmd).to receive(:validate_cli_options!).and_return(nil)
|
10
|
+
end
|
11
|
+
|
12
|
+
context "when validating plugins" do
|
13
|
+
let(:sources) { ["https://rubygems.org", "http://localhost:9292"] }
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
expect(cmd).to receive(:plugins_gems).and_return([["dummy", nil]])
|
17
|
+
expect(cmd).to receive(:install_gems_list!).and_return(nil)
|
18
|
+
expect(cmd).to receive(:remove_unused_locally_installed_gems!).and_return(nil)
|
19
|
+
cmd.verify = true
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should load all the sources defined in the Gemfile" do
|
23
|
+
expect(cmd.gemfile.gemset).to receive(:sources).and_return(sources)
|
24
|
+
expect(LogStash::PluginManager).to receive(:logstash_plugin?).with("dummy", nil, {:rubygems_source => sources}).and_return(true)
|
25
|
+
cmd.execute
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'pluginmanager/main'
|
4
|
+
|
5
|
+
describe LogStash::PluginManager::Update do
|
6
|
+
let(:cmd) { LogStash::PluginManager::Update.new("update") }
|
7
|
+
let(:sources) { cmd.gemfile.gemset.sources }
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
expect(cmd).to receive(:find_latest_gem_specs).and_return({})
|
11
|
+
allow(cmd).to receive(:warn_local_gems).and_return(nil)
|
12
|
+
expect(cmd).to receive(:display_updated_plugins).and_return(nil)
|
13
|
+
expect_any_instance_of(LogStash::Bundler).to receive(:invoke!).with(:clean => true)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "pass all gem sources to the bundle update command" do
|
17
|
+
sources = cmd.gemfile.gemset.sources
|
18
|
+
expect_any_instance_of(LogStash::Bundler).to receive(:invoke!).with(:update => [], :rubygems_source => sources)
|
19
|
+
cmd.execute
|
20
|
+
end
|
21
|
+
|
22
|
+
context "when skipping validation" do
|
23
|
+
let(:cmd) { LogStash::PluginManager::Update.new("update") }
|
24
|
+
let(:plugin) { OpenStruct.new(:name => "dummy", :options => {} ) }
|
25
|
+
|
26
|
+
before(:each) do
|
27
|
+
expect(cmd.gemfile).to receive(:find).with(plugin).and_return(plugin)
|
28
|
+
expect(cmd.gemfile).to receive(:save).and_return(nil)
|
29
|
+
expect(cmd).to receive(:plugins_to_update).and_return([plugin])
|
30
|
+
expect_any_instance_of(LogStash::Bundler).to receive(:invoke!).with(:update => [plugin], :rubygems_source => sources).and_return(nil)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "skips version verification when ask for it" do
|
34
|
+
cmd.verify = false
|
35
|
+
expect(cmd).to_not receive(:validates_version)
|
36
|
+
cmd.execute
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|