logstash-core 7.1.1-java → 7.2.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/lib/logstash/api/commands/default_metadata.rb +9 -2
- data/lib/logstash/api/commands/hot_threads_reporter.rb +2 -2
- data/lib/logstash/api/commands/node.rb +18 -5
- data/lib/logstash/api/commands/stats.rb +1 -0
- data/lib/logstash/api/modules/node.rb +11 -2
- data/lib/logstash/codecs/delegator.rb +52 -0
- data/lib/logstash/compiler/lscl.rb +4 -3
- data/lib/logstash/config/lir_serializer.rb +126 -0
- data/lib/logstash/config/mixin.rb +2 -1
- data/lib/logstash/environment.rb +7 -2
- data/lib/logstash/inputs/base.rb +7 -0
- data/lib/logstash/instrument/periodic_poller/jvm.rb +3 -0
- data/lib/logstash/java_pipeline.rb +20 -3
- data/lib/logstash/outputs/base.rb +7 -0
- data/lib/logstash/pipeline.rb +15 -2
- data/lib/logstash/pipeline_settings.rb +1 -0
- data/lib/logstash/plugin.rb +25 -1
- data/lib/logstash/plugin_metadata.rb +139 -0
- data/lib/logstash/plugins/registry.rb +29 -1
- data/lib/logstash/runner.rb +5 -0
- data/locales/en.yml +2 -0
- data/logstash-core.gemspec +1 -1
- data/spec/logstash/codecs/delegator_spec.rb +85 -0
- data/spec/logstash/compiler/compiler_spec.rb +28 -0
- data/spec/logstash/config/config_ast_spec.rb +15 -0
- data/spec/logstash/environment_spec.rb +4 -4
- data/spec/logstash/plugin_spec.rb +75 -0
- data/spec/plugin_metadata_spec.rb +151 -0
- data/versions-gem-copy.yml +2 -2
- metadata +23 -16
@@ -102,6 +102,13 @@ class LogStash::Outputs::Base < LogStash::Plugin
|
|
102
102
|
self.class.concurrency
|
103
103
|
end
|
104
104
|
|
105
|
+
def metric=(metric)
|
106
|
+
super
|
107
|
+
# Hack to create a new metric namespace using 'plugins' as the root
|
108
|
+
@codec.metric = metric.root.namespace(metric.namespace_name[0...-2].push(:codecs, codec.id))
|
109
|
+
metric
|
110
|
+
end
|
111
|
+
|
105
112
|
def execution_context=(context)
|
106
113
|
super
|
107
114
|
# There is no easy way to propage an instance variable into the codec, because the codec
|
data/lib/logstash/pipeline.rb
CHANGED
@@ -394,6 +394,14 @@ module LogStash; class Pipeline < BasePipeline
|
|
394
394
|
filter_queue_client.add_output_metrics(batch.filtered_size)
|
395
395
|
end
|
396
396
|
|
397
|
+
def resolve_cluster_uuids
|
398
|
+
outputs.each_with_object(Set.new) do |output, cluster_uuids|
|
399
|
+
if LogStash::PluginMetadata.exists?(output.id)
|
400
|
+
cluster_uuids << LogStash::PluginMetadata.for_plugin(output.id).get(:cluster_uuid)
|
401
|
+
end
|
402
|
+
end.to_a.compact
|
403
|
+
end
|
404
|
+
|
397
405
|
def wait_inputs
|
398
406
|
@input_threads.each(&:join)
|
399
407
|
end
|
@@ -442,9 +450,14 @@ module LogStash; class Pipeline < BasePipeline
|
|
442
450
|
# Assuming the failure that caused this exception is transient,
|
443
451
|
# let's sleep for a bit and execute #run again
|
444
452
|
sleep(1)
|
453
|
+
begin
|
454
|
+
plugin.do_close
|
455
|
+
rescue => close_exception
|
456
|
+
@logger.debug("Input plugin raised exception while closing, ignoring",
|
457
|
+
default_logging_keys(:plugin => plugin.class.config_name, :exception => close_exception.message,
|
458
|
+
:backtrace => close_exception.backtrace))
|
459
|
+
end
|
445
460
|
retry
|
446
|
-
ensure
|
447
|
-
plugin.do_close
|
448
461
|
end
|
449
462
|
end # def inputworker
|
450
463
|
|
data/lib/logstash/plugin.rb
CHANGED
@@ -3,6 +3,8 @@ require "logstash/config/mixin"
|
|
3
3
|
require "concurrent"
|
4
4
|
require "securerandom"
|
5
5
|
|
6
|
+
require_relative 'plugin_metadata'
|
7
|
+
|
6
8
|
class LogStash::Plugin
|
7
9
|
include LogStash::Util::Loggable
|
8
10
|
|
@@ -70,7 +72,11 @@ class LogStash::Plugin
|
|
70
72
|
# main task terminates
|
71
73
|
def do_close
|
72
74
|
@logger.debug("Closing", :plugin => self.class.name)
|
73
|
-
|
75
|
+
begin
|
76
|
+
close
|
77
|
+
ensure
|
78
|
+
LogStash::PluginMetadata.delete_for_plugin(self.id)
|
79
|
+
end
|
74
80
|
end
|
75
81
|
|
76
82
|
# Subclasses should implement this close method if you need to perform any
|
@@ -136,4 +142,22 @@ class LogStash::Plugin
|
|
136
142
|
require "logstash/plugins/registry"
|
137
143
|
LogStash::PLUGIN_REGISTRY.lookup_pipeline_plugin(type, name)
|
138
144
|
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Returns this plugin's metadata key/value store.
|
148
|
+
#
|
149
|
+
# @see LogStash::PluginMetadata for restrictions and caveats.
|
150
|
+
# @since 7.1
|
151
|
+
#
|
152
|
+
# @usage:
|
153
|
+
# ~~~
|
154
|
+
# if defined?(plugin_metadata)
|
155
|
+
# plugin_metadata.set(:foo, 'value')
|
156
|
+
# end
|
157
|
+
# ~~~
|
158
|
+
#
|
159
|
+
# @return [LogStash::PluginMetadata]
|
160
|
+
def plugin_metadata
|
161
|
+
LogStash::PluginMetadata.for_plugin(self.id)
|
162
|
+
end
|
139
163
|
end # class LogStash::Plugin
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'thread_safe/cache'
|
4
|
+
|
5
|
+
module LogStash
|
6
|
+
##
|
7
|
+
# `PluginMetadata` provides a space to store key/value metadata about a plugin, typically metadata about
|
8
|
+
# external resources that can be gleaned during plugin registration.
|
9
|
+
#
|
10
|
+
# Data should not be persisted across pipeline reloads, and should be cleaned up after a pipeline reload
|
11
|
+
#
|
12
|
+
# - It MUST NOT be used to store processing state
|
13
|
+
# - It SHOULD NOT be updated frequently.
|
14
|
+
# - Individual metadata keys MUST be Symbols and SHOULD NOT be dynamically generated
|
15
|
+
#
|
16
|
+
# USAGE FROM PLUGINS
|
17
|
+
# ------------------
|
18
|
+
#
|
19
|
+
# Since we allow plugins to be updated, it is important to introduce bindings to new Logstash features in a way
|
20
|
+
# that doesn't break when installed onto a Logstash that doesn't have those features, e.g.:
|
21
|
+
#
|
22
|
+
# ~~~
|
23
|
+
#
|
24
|
+
# plugin_metadata.set(:foo, bar) if defined?(plugin_metadata?)
|
25
|
+
#
|
26
|
+
# ~~~
|
27
|
+
#
|
28
|
+
# @since 7.1
|
29
|
+
class PluginMetadata
|
30
|
+
include LogStash::Util::Loggable
|
31
|
+
|
32
|
+
Thread.exclusive do
|
33
|
+
@registry = ThreadSafe::Cache.new unless defined?(@registry)
|
34
|
+
end
|
35
|
+
|
36
|
+
class << self
|
37
|
+
##
|
38
|
+
# Get the PluginMetadata object corresponding to the given plugin id
|
39
|
+
#
|
40
|
+
# @param plugin_id [String]
|
41
|
+
#
|
42
|
+
# @return [PluginMetadata]: the metadata object for the provided `plugin_id`; if no
|
43
|
+
# metadata object exists, it will be created.
|
44
|
+
def for_plugin(plugin_id)
|
45
|
+
@registry.compute_if_absent(plugin_id) { PluginMetadata.new }
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Determine if we have an existing PluginMetadata object for the given plugin id
|
50
|
+
# This allows us to avoid creating a metadata object if we don't already have one.
|
51
|
+
#
|
52
|
+
# @param plugin_id [String]
|
53
|
+
#
|
54
|
+
# @return [Boolean]
|
55
|
+
def exists?(plugin_id)
|
56
|
+
@registry.key?(plugin_id)
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Deletes, and then clears the contents of an existing PluginMetadata object for the given plugin id if one exists
|
61
|
+
#
|
62
|
+
# @param plugin_id [String]
|
63
|
+
#
|
64
|
+
# @return [Boolean]
|
65
|
+
def delete_for_plugin(plugin_id)
|
66
|
+
logger.debug("Removing metadata for plugin #{plugin_id}")
|
67
|
+
old_registry = @registry.delete(plugin_id)
|
68
|
+
old_registry.clear unless old_registry.nil?
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# @api private
|
73
|
+
def reset!
|
74
|
+
@registry.clear
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# @see [LogStash::PluginMetadata#for_plugin(String)]
|
80
|
+
# @api private
|
81
|
+
def initialize
|
82
|
+
@datastore = ThreadSafe::Cache.new
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Set the metadata key for this plugin, returning the previous value (if any)
|
87
|
+
#
|
88
|
+
# @param key [Symbol]
|
89
|
+
# @param value [Object]
|
90
|
+
#
|
91
|
+
# @return [Object]
|
92
|
+
def set(key, value)
|
93
|
+
if value.nil?
|
94
|
+
@datastore.delete(key)
|
95
|
+
else
|
96
|
+
@datastore.get_and_set(key, value)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# Get the metadata value for the given key on this plugin
|
102
|
+
#
|
103
|
+
# @param key [Symbol]
|
104
|
+
#
|
105
|
+
# @return [Object]: the value object associated with the given key on this
|
106
|
+
# plugin, or nil if no value is associated
|
107
|
+
def get(key)
|
108
|
+
@datastore.get(key)
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Determine whether specific key/value metadata exists for this plugin
|
113
|
+
#
|
114
|
+
# @param key [Symbol]: the key
|
115
|
+
#
|
116
|
+
# @return [Boolean]: true if the plugin includes metadata for the key
|
117
|
+
def set?(key)
|
118
|
+
@datastore.key?(key)
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# Delete the metadata key for this plugin, returning the previous value (if any)
|
123
|
+
#
|
124
|
+
# @param key [Symbol]
|
125
|
+
#
|
126
|
+
# @return [Object]
|
127
|
+
def delete(key)
|
128
|
+
@datastore.delete(key)
|
129
|
+
end
|
130
|
+
|
131
|
+
##
|
132
|
+
# Clear all metadata keys for this plugin
|
133
|
+
#
|
134
|
+
# @return [Object]
|
135
|
+
def clear
|
136
|
+
@datastore.clear
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -105,6 +105,7 @@ module LogStash module Plugins
|
|
105
105
|
# but it is the case that we can call lookups from multiple threads,
|
106
106
|
# when multiple pipelines are in play, and that a lookup may modify the registry.
|
107
107
|
@registry = java.util.concurrent.ConcurrentHashMap.new
|
108
|
+
@java_plugins = java.util.concurrent.ConcurrentHashMap.new
|
108
109
|
@hooks = HooksRegistry.new
|
109
110
|
end
|
110
111
|
|
@@ -133,6 +134,15 @@ module LogStash module Plugins
|
|
133
134
|
require "logstash/plugins/builtin"
|
134
135
|
|
135
136
|
GemRegistry.logstash_plugins.each do |plugin_context|
|
137
|
+
if plugin_context.spec.metadata.key?('java_plugin')
|
138
|
+
jar_files = plugin_context.spec.files.select {|f| f =~ /.*\.jar/}
|
139
|
+
expected_jar_name = plugin_context.spec.name + "-" + plugin_context.spec.version.to_s + ".jar"
|
140
|
+
if (jar_files.length != 1 || !jar_files[0].end_with?(expected_jar_name))
|
141
|
+
raise LoadError, "Java plugin '#{plugin_context.spec.name}' does not contain a single jar file with the plugin's name and version"
|
142
|
+
end
|
143
|
+
@java_plugins[plugin_context.spec.name] = [plugin_context.spec.loaded_from, jar_files[0]]
|
144
|
+
end
|
145
|
+
|
136
146
|
# When a plugin has a HOOK_FILE defined, its the responsibility of the plugin
|
137
147
|
# to register itself to the registry of available plugins.
|
138
148
|
#
|
@@ -262,13 +272,31 @@ module LogStash module Plugins
|
|
262
272
|
# @param name [String] plugin name
|
263
273
|
# @return [Boolean] true if klass is a valid plugin for name
|
264
274
|
def is_a_plugin?(klass, name)
|
275
|
+
(klass.class == Java::JavaLang::Class && klass.simple_name.downcase == name.gsub('_','')) ||
|
265
276
|
(klass.class == Java::JavaClass && klass.simple_name.downcase == name.gsub('_','')) ||
|
266
277
|
(klass.ancestors.include?(LogStash::Plugin) && klass.respond_to?(:config_name) && klass.config_name == name)
|
267
278
|
end
|
268
279
|
|
269
280
|
def add_plugin(type, name, klass)
|
270
281
|
if klass.respond_to?("javaClass", true)
|
271
|
-
|
282
|
+
if LogStash::SETTINGS.get_value('pipeline.plugin_classloaders')
|
283
|
+
full_name = 'logstash-' + key_for(type, name)
|
284
|
+
if @java_plugins.key?(full_name)
|
285
|
+
plugin_paths = @java_plugins[full_name]
|
286
|
+
else
|
287
|
+
raise LoadError, "Could not find metadata for Java plugin: #{full_name}"
|
288
|
+
end
|
289
|
+
|
290
|
+
java_import org.logstash.plugins.PluginClassLoader
|
291
|
+
java_import org.logstash.Logstash
|
292
|
+
|
293
|
+
classloader = PluginClassLoader.create(plugin_paths[0], plugin_paths[1], Logstash.java_class.class_loader)
|
294
|
+
klazz = classloader.load_class(klass.javaClass.name)
|
295
|
+
|
296
|
+
@registry[key_for(type, name)] = PluginSpecification.new(type, name, klazz.ruby_class.java_class)
|
297
|
+
else
|
298
|
+
@registry[key_for(type, name)] = PluginSpecification.new(type, name, klass.javaClass)
|
299
|
+
end
|
272
300
|
elsif !exists?(type, name)
|
273
301
|
specification_klass = type == :universal ? UniversalPluginSpecification : PluginSpecification
|
274
302
|
@registry[key_for(type, name)] = specification_klass.new(type, name, klass)
|
data/lib/logstash/runner.rb
CHANGED
@@ -113,6 +113,11 @@ class LogStash::Runner < Clamp::StrictCommand
|
|
113
113
|
:attribute_name => "pipeline.java_execution",
|
114
114
|
:default => LogStash::SETTINGS.get_default("pipeline.java_execution")
|
115
115
|
|
116
|
+
option ["--plugin-classloaders"], :flag,
|
117
|
+
I18n.t("logstash.runner.flag.plugin-classloaders"),
|
118
|
+
:attribute_name => "pipeline.plugin_classloaders",
|
119
|
+
:default => LogStash::SETTINGS.get_default("pipeline.plugin_classloaders")
|
120
|
+
|
116
121
|
option ["-b", "--pipeline.batch.size"], "SIZE",
|
117
122
|
I18n.t("logstash.runner.flag.pipeline-batch-size"),
|
118
123
|
:attribute_name => "pipeline.batch.size",
|
data/locales/en.yml
CHANGED
@@ -290,6 +290,8 @@ en:
|
|
290
290
|
Sets the number of pipeline workers to run.
|
291
291
|
java-execution: |+
|
292
292
|
Use Java execution engine.
|
293
|
+
plugin-classloaders: |+
|
294
|
+
(Beta) Load Java plugins in independent classloaders to isolate their dependencies.
|
293
295
|
pipeline-batch-size: |+
|
294
296
|
Size of batches the pipeline is to work in.
|
295
297
|
pipeline-batch-delay: |+
|
data/logstash-core.gemspec
CHANGED
@@ -57,7 +57,6 @@ Gem::Specification.new do |gem|
|
|
57
57
|
gem.add_runtime_dependency "sinatra", '~> 1', '>= 1.4.6'
|
58
58
|
gem.add_runtime_dependency 'puma', '~> 2'
|
59
59
|
gem.add_runtime_dependency "jruby-openssl", "~> 0.10" # >= 0.9.13 Required to support TLSv1.2
|
60
|
-
gem.add_runtime_dependency "faraday", "~> 0.9.0" # because of conflicting dependencies between twitter input and octokit
|
61
60
|
gem.add_runtime_dependency "chronic_duration", "~> 0.10"
|
62
61
|
|
63
62
|
gem.add_runtime_dependency "treetop", "~> 1" #(MIT license)
|
@@ -73,4 +72,5 @@ Gem::Specification.new do |gem|
|
|
73
72
|
|
74
73
|
gem.add_runtime_dependency "elasticsearch", "~> 5"
|
75
74
|
gem.add_runtime_dependency "manticore", '~> 0.6'
|
75
|
+
gem.add_runtime_dependency "faraday", '~> 0.9.0'
|
76
76
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
class LogStash::Codecs::MockCodec < LogStash::Codecs::Base
|
5
|
+
config_name "my_name"
|
6
|
+
|
7
|
+
def multi_encode(e)
|
8
|
+
end
|
9
|
+
|
10
|
+
def encode(e)
|
11
|
+
end
|
12
|
+
|
13
|
+
def decode(e)
|
14
|
+
for i in e.split('|')
|
15
|
+
yield i
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe LogStash::Codecs::Delegator do
|
21
|
+
let(:collector) { LogStash::Instrument::Collector.new }
|
22
|
+
let(:metric) { LogStash::Instrument::Metric.new(collector) }
|
23
|
+
let(:codec) { LogStash::Codecs::MockCodec.new }
|
24
|
+
|
25
|
+
subject do
|
26
|
+
delegator = described_class.new(codec)
|
27
|
+
delegator.metric = metric.namespace([:stats, :pipelines, :main, :plugins, :codecs, :my_id])
|
28
|
+
delegator
|
29
|
+
end
|
30
|
+
|
31
|
+
let(:snapshot_store) { collector.snapshot_metric.metric_store }
|
32
|
+
|
33
|
+
let(:snapshot_metric) { snapshot_store.get_shallow(:stats) }
|
34
|
+
|
35
|
+
describe "#encode" do
|
36
|
+
it "should delegate call to codec" do
|
37
|
+
expect(codec).to receive(:encode).with("abcdef")
|
38
|
+
subject.encode("abcdef")
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should increment metrics" do
|
42
|
+
subject.encode("test")
|
43
|
+
expect(snapshot_metric[:pipelines][:main][:plugins][:codecs][:my_id][:encode][:writes_in].value).to eq(1)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#multi_encode" do
|
48
|
+
it "should delegate call to codec" do
|
49
|
+
expect(codec).to receive(:multi_encode).with(%w(ay laa))
|
50
|
+
subject.multi_encode(%w(ay laa))
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should increment metrics" do
|
54
|
+
subject.multi_encode(%w(ay test))
|
55
|
+
expect(snapshot_metric[:pipelines][:main][:plugins][:codecs][:my_id][:encode][:writes_in].value).to eq(2)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#decode" do
|
60
|
+
it "should delegate call to codec" do
|
61
|
+
expect(codec).to receive(:decode).with("ayooooo")
|
62
|
+
subject.decode("ayooooo")
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should increment metrics" do
|
66
|
+
subject.decode("bird|law") {}
|
67
|
+
expect(snapshot_metric[:pipelines][:main][:plugins][:codecs][:my_id][:decode][:writes_in].value).to eq(1)
|
68
|
+
expect(snapshot_metric[:pipelines][:main][:plugins][:codecs][:my_id][:decode][:out].value).to eq(2)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#close" do
|
73
|
+
it "should delegate call to codec" do
|
74
|
+
expect(codec).to receive(:close)
|
75
|
+
subject.close
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#plugin_type" do
|
80
|
+
it "should delegate call to codec" do
|
81
|
+
expect(codec).to receive(:plugin_type)
|
82
|
+
subject.plugin_type
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -252,6 +252,34 @@ describe LogStash::Compiler do
|
|
252
252
|
expect(c_plugin).to ir_eql(j.iPlugin(rand_meta, FILTER, "grok", expected_plugin_args))
|
253
253
|
end
|
254
254
|
|
255
|
+
describe "a filter plugin with a repeated hash directive with duplicated keys" do
|
256
|
+
let(:source) { "input { } filter { #{plugin_source} } output { } " }
|
257
|
+
let(:plugin_source) do
|
258
|
+
%q[
|
259
|
+
grok {
|
260
|
+
match => { "message" => "foo" }
|
261
|
+
match => { "message" => "bar" }
|
262
|
+
break_on_match => false
|
263
|
+
}
|
264
|
+
]
|
265
|
+
end
|
266
|
+
subject(:c_plugin) { compiled[:filter] }
|
267
|
+
|
268
|
+
let(:expected_plugin_args) do
|
269
|
+
{
|
270
|
+
"match" => {
|
271
|
+
"message" => ["foo", "bar"]
|
272
|
+
},
|
273
|
+
"break_on_match" => "false"
|
274
|
+
}
|
275
|
+
end
|
276
|
+
|
277
|
+
it "should merge the values of the duplicate keys into an array" do
|
278
|
+
expect(c_plugin).to ir_eql(j.iPlugin(rand_meta, FILTER, "grok", expected_plugin_args))
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|
282
|
+
|
255
283
|
describe "a filter plugin that has nested Hash directives" do
|
256
284
|
let(:source) { "input { } filter { #{plugin_source} } output { } " }
|
257
285
|
let(:plugin_source) do
|