logstash-core 1.5.0.beta2-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 +7 -0
- data/lib/logstash-event.rb +2 -0
- data/lib/logstash.rb +4 -0
- data/lib/logstash/JRUBY-PR1448.rb +32 -0
- data/lib/logstash/agent.rb +355 -0
- data/lib/logstash/bundler.rb +124 -0
- data/lib/logstash/codecs/base.rb +50 -0
- data/lib/logstash/config/config_ast.rb +508 -0
- data/lib/logstash/config/file.rb +39 -0
- data/lib/logstash/config/grammar.rb +3503 -0
- data/lib/logstash/config/mixin.rb +495 -0
- data/lib/logstash/config/registry.rb +13 -0
- data/lib/logstash/environment.rb +168 -0
- data/lib/logstash/errors.rb +12 -0
- data/lib/logstash/event.rb +310 -0
- data/lib/logstash/filters/base.rb +239 -0
- data/lib/logstash/gemfile.rb +175 -0
- data/lib/logstash/inputs/base.rb +137 -0
- data/lib/logstash/inputs/threadable.rb +18 -0
- data/lib/logstash/java_integration.rb +41 -0
- data/lib/logstash/json.rb +53 -0
- data/lib/logstash/logging.rb +91 -0
- data/lib/logstash/multiqueue.rb +53 -0
- data/lib/logstash/namespace.rb +17 -0
- data/lib/logstash/outputs/base.rb +124 -0
- data/lib/logstash/patches.rb +3 -0
- data/lib/logstash/patches/bugfix_jruby_2558.rb +50 -0
- data/lib/logstash/patches/cabin.rb +34 -0
- data/lib/logstash/patches/profile_require_calls.rb +47 -0
- data/lib/logstash/pipeline.rb +305 -0
- data/lib/logstash/plugin.rb +177 -0
- data/lib/logstash/pluginmanager.rb +17 -0
- data/lib/logstash/pluginmanager/install.rb +112 -0
- data/lib/logstash/pluginmanager/list.rb +38 -0
- data/lib/logstash/pluginmanager/main.rb +22 -0
- data/lib/logstash/pluginmanager/maven_tools_patch.rb +12 -0
- data/lib/logstash/pluginmanager/uninstall.rb +49 -0
- data/lib/logstash/pluginmanager/update.rb +50 -0
- data/lib/logstash/pluginmanager/util.rb +88 -0
- data/lib/logstash/program.rb +15 -0
- data/lib/logstash/runner.rb +167 -0
- data/lib/logstash/sized_queue.rb +8 -0
- data/lib/logstash/threadwatchdog.rb +37 -0
- data/lib/logstash/timestamp.rb +97 -0
- data/lib/logstash/util.rb +152 -0
- data/lib/logstash/util/accessors.rb +88 -0
- data/lib/logstash/util/buftok.rb +139 -0
- data/lib/logstash/util/charset.rb +35 -0
- data/lib/logstash/util/fieldreference.rb +68 -0
- data/lib/logstash/util/filetools.rb +185 -0
- data/lib/logstash/util/password.rb +25 -0
- data/lib/logstash/util/plugin_version.rb +43 -0
- data/lib/logstash/util/prctl.rb +11 -0
- data/lib/logstash/util/require-helper.rb +18 -0
- data/lib/logstash/util/retryable.rb +39 -0
- data/lib/logstash/util/socket_peer.rb +7 -0
- data/lib/logstash/version.rb +6 -0
- data/locales/en.yml +176 -0
- metadata +427 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
if ENV["PROFILE_BAD_LOG_CALLS"] || ($DEBUGLIST || []).include?("log")
|
2
|
+
# Set PROFILE_BAD_LOG_CALLS=1 in your environment if you want
|
3
|
+
# to track down logger calls that cause performance problems
|
4
|
+
#
|
5
|
+
# Related research here:
|
6
|
+
# https://github.com/jordansissel/experiments/tree/master/ruby/logger-string-vs-block
|
7
|
+
#
|
8
|
+
# Basically, the following is wastes tons of effort creating objects that are
|
9
|
+
# never used if the log level hides the log:
|
10
|
+
#
|
11
|
+
# logger.debug("something happend", :what => Happened)
|
12
|
+
#
|
13
|
+
# This is shown to be 4x faster:
|
14
|
+
#
|
15
|
+
# logger.debug(...) if logger.debug?
|
16
|
+
#
|
17
|
+
# I originally intended to use RubyParser and SexpProcessor to
|
18
|
+
# process all the logstash ruby code offline, but it was much
|
19
|
+
# faster to write this monkeypatch to warn as things are called.
|
20
|
+
require "cabin/mixins/logger"
|
21
|
+
module Cabin::Mixins::Logger
|
22
|
+
LEVELS.keys.each do |level|
|
23
|
+
m = "original_#{level}".to_sym
|
24
|
+
predicate = "#{level}?".to_sym
|
25
|
+
alias_method m, level
|
26
|
+
define_method(level) do |*args|
|
27
|
+
if !send(predicate)
|
28
|
+
warn("Unconditional log call", :location => caller[0])
|
29
|
+
end
|
30
|
+
send(m, *args)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end # PROFILE_BAD_LOG_CALLS
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
if ($DEBUGLIST || []).include?("require")
|
3
|
+
ROOT = File.dirname(__FILE__)
|
4
|
+
module Kernel
|
5
|
+
alias_method :require_debug, :require
|
6
|
+
|
7
|
+
def require(path)
|
8
|
+
start = Time.now
|
9
|
+
result = require_debug(path)
|
10
|
+
duration = Time.now - start
|
11
|
+
|
12
|
+
origin = caller[1]
|
13
|
+
if origin =~ /rubygems\/custom_require/
|
14
|
+
origin = caller[3]
|
15
|
+
if origin.nil?
|
16
|
+
STDERR.puts "Unknown origin"
|
17
|
+
STDERR.puts caller.join("\n")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
origin = origin.gsub(/:[0-9]+:in .*/, "") if origin
|
21
|
+
|
22
|
+
# Only print require() calls that did actual work.
|
23
|
+
# require() returns true on load, false if already loaded.
|
24
|
+
if result
|
25
|
+
source = caller[0]
|
26
|
+
#p source.include?("/lib/polyglot.rb:63:in `require'") => source
|
27
|
+
if source.include?("/lib/polyglot.rb:63:in `require'")
|
28
|
+
source = caller[1]
|
29
|
+
end
|
30
|
+
|
31
|
+
#target = $LOADED_FEATURES.grep(/#{path}/).first
|
32
|
+
#puts path
|
33
|
+
#puts caller.map { |c| " #{c}" }.join("\n")
|
34
|
+
#fontsize = [10, duration * 48].max
|
35
|
+
puts "#{duration},#{path},#{source}"
|
36
|
+
end
|
37
|
+
#puts caller.map { |c| " => #{c}" }.join("\n")
|
38
|
+
end
|
39
|
+
|
40
|
+
alias_method :load_debug, :load
|
41
|
+
|
42
|
+
def load(path)
|
43
|
+
puts "load(\"#{path}\")"
|
44
|
+
return load_debug(path)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,305 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "thread" #
|
3
|
+
require "stud/interval"
|
4
|
+
require "logstash/namespace"
|
5
|
+
require "logstash/errors"
|
6
|
+
require "logstash/event"
|
7
|
+
require "logstash/config/file"
|
8
|
+
require "logstash/filters/base"
|
9
|
+
require "logstash/inputs/base"
|
10
|
+
require "logstash/outputs/base"
|
11
|
+
|
12
|
+
class LogStash::Pipeline
|
13
|
+
|
14
|
+
FLUSH_EVENT = LogStash::FlushEvent.new
|
15
|
+
|
16
|
+
def initialize(configstr)
|
17
|
+
@logger = Cabin::Channel.get(LogStash)
|
18
|
+
grammar = LogStashConfigParser.new
|
19
|
+
@config = grammar.parse(configstr)
|
20
|
+
if @config.nil?
|
21
|
+
raise LogStash::ConfigurationError, grammar.failure_reason
|
22
|
+
end
|
23
|
+
|
24
|
+
# This will compile the config to ruby and evaluate the resulting code.
|
25
|
+
# The code will initialize all the plugins and define the
|
26
|
+
# filter and output methods.
|
27
|
+
code = @config.compile
|
28
|
+
# The config code is hard to represent as a log message...
|
29
|
+
# So just print it.
|
30
|
+
@logger.debug? && @logger.debug("Compiled pipeline code:\n#{code}")
|
31
|
+
begin
|
32
|
+
eval(code)
|
33
|
+
rescue => e
|
34
|
+
raise
|
35
|
+
end
|
36
|
+
|
37
|
+
@input_to_filter = SizedQueue.new(20)
|
38
|
+
|
39
|
+
# If no filters, pipe inputs directly to outputs
|
40
|
+
if !filters?
|
41
|
+
@filter_to_output = @input_to_filter
|
42
|
+
else
|
43
|
+
@filter_to_output = SizedQueue.new(20)
|
44
|
+
end
|
45
|
+
@settings = {
|
46
|
+
"filter-workers" => 1,
|
47
|
+
}
|
48
|
+
end # def initialize
|
49
|
+
|
50
|
+
def ready?
|
51
|
+
return @ready
|
52
|
+
end
|
53
|
+
|
54
|
+
def started?
|
55
|
+
return @started
|
56
|
+
end
|
57
|
+
|
58
|
+
def configure(setting, value)
|
59
|
+
if setting == "filter-workers"
|
60
|
+
# Abort if we have any filters that aren't threadsafe
|
61
|
+
if value > 1 && @filters.any? { |f| !f.threadsafe? }
|
62
|
+
plugins = @filters.select { |f| !f.threadsafe? }.collect { |f| f.class.config_name }
|
63
|
+
raise LogStash::ConfigurationError, "Cannot use more than 1 filter worker because the following plugins don't work with more than one worker: #{plugins.join(", ")}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
@settings[setting] = value
|
67
|
+
end
|
68
|
+
|
69
|
+
def filters?
|
70
|
+
return @filters.any?
|
71
|
+
end
|
72
|
+
|
73
|
+
def run
|
74
|
+
@started = true
|
75
|
+
@input_threads = []
|
76
|
+
|
77
|
+
start_inputs
|
78
|
+
start_filters if filters?
|
79
|
+
start_outputs
|
80
|
+
|
81
|
+
@ready = true
|
82
|
+
|
83
|
+
@logger.info("Pipeline started")
|
84
|
+
@logger.terminal("Logstash startup completed")
|
85
|
+
|
86
|
+
wait_inputs
|
87
|
+
|
88
|
+
if filters?
|
89
|
+
shutdown_filters
|
90
|
+
wait_filters
|
91
|
+
flush_filters_to_output!(:final => true)
|
92
|
+
end
|
93
|
+
|
94
|
+
shutdown_outputs
|
95
|
+
wait_outputs
|
96
|
+
|
97
|
+
@logger.info("Pipeline shutdown complete.")
|
98
|
+
@logger.terminal("Logstash shutdown completed")
|
99
|
+
|
100
|
+
# exit code
|
101
|
+
return 0
|
102
|
+
end # def run
|
103
|
+
|
104
|
+
def wait_inputs
|
105
|
+
@input_threads.each(&:join)
|
106
|
+
rescue Interrupt
|
107
|
+
# rbx does weird things during do SIGINT that I haven't debugged
|
108
|
+
# so we catch Interrupt here and signal a shutdown. For some reason the
|
109
|
+
# signal handler isn't invoked it seems? I dunno, haven't looked much into
|
110
|
+
# it.
|
111
|
+
shutdown
|
112
|
+
end
|
113
|
+
|
114
|
+
def shutdown_filters
|
115
|
+
@flusher_lock.synchronize { @flusher_thread.kill }
|
116
|
+
@input_to_filter.push(LogStash::ShutdownEvent.new)
|
117
|
+
end
|
118
|
+
|
119
|
+
def wait_filters
|
120
|
+
@filter_threads.each(&:join) if @filter_threads
|
121
|
+
end
|
122
|
+
|
123
|
+
def shutdown_outputs
|
124
|
+
# nothing, filters will do this
|
125
|
+
@filter_to_output.push(LogStash::ShutdownEvent.new)
|
126
|
+
end
|
127
|
+
|
128
|
+
def wait_outputs
|
129
|
+
# Wait for the outputs to stop
|
130
|
+
@output_threads.each(&:join)
|
131
|
+
end
|
132
|
+
|
133
|
+
def start_inputs
|
134
|
+
moreinputs = []
|
135
|
+
@inputs.each do |input|
|
136
|
+
if input.threadable && input.threads > 1
|
137
|
+
(input.threads-1).times do |i|
|
138
|
+
moreinputs << input.clone
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
@inputs += moreinputs
|
143
|
+
|
144
|
+
@inputs.each do |input|
|
145
|
+
input.register
|
146
|
+
start_input(input)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def start_filters
|
151
|
+
@filters.each(&:register)
|
152
|
+
@filter_threads = @settings["filter-workers"].times.collect do
|
153
|
+
Thread.new { filterworker }
|
154
|
+
end
|
155
|
+
|
156
|
+
@flusher_lock = Mutex.new
|
157
|
+
@flusher_thread = Thread.new { Stud.interval(5) { @flusher_lock.synchronize { @input_to_filter.push(FLUSH_EVENT) } } }
|
158
|
+
end
|
159
|
+
|
160
|
+
def start_outputs
|
161
|
+
@outputs.each(&:register)
|
162
|
+
@output_threads = [
|
163
|
+
Thread.new { outputworker }
|
164
|
+
]
|
165
|
+
end
|
166
|
+
|
167
|
+
def start_input(plugin)
|
168
|
+
@input_threads << Thread.new { inputworker(plugin) }
|
169
|
+
end
|
170
|
+
|
171
|
+
def inputworker(plugin)
|
172
|
+
LogStash::Util::set_thread_name("<#{plugin.class.config_name}")
|
173
|
+
begin
|
174
|
+
plugin.run(@input_to_filter)
|
175
|
+
rescue LogStash::ShutdownSignal
|
176
|
+
return
|
177
|
+
rescue => e
|
178
|
+
if @logger.debug?
|
179
|
+
@logger.error(I18n.t("logstash.pipeline.worker-error-debug",
|
180
|
+
:plugin => plugin.inspect, :error => e.to_s,
|
181
|
+
:exception => e.class,
|
182
|
+
:stacktrace => e.backtrace.join("\n")))
|
183
|
+
else
|
184
|
+
@logger.error(I18n.t("logstash.pipeline.worker-error",
|
185
|
+
:plugin => plugin.inspect, :error => e))
|
186
|
+
end
|
187
|
+
puts e.backtrace if @logger.debug?
|
188
|
+
plugin.teardown
|
189
|
+
sleep 1
|
190
|
+
retry
|
191
|
+
end
|
192
|
+
rescue LogStash::ShutdownSignal
|
193
|
+
# nothing
|
194
|
+
ensure
|
195
|
+
plugin.teardown
|
196
|
+
end # def inputworker
|
197
|
+
|
198
|
+
def filterworker
|
199
|
+
LogStash::Util::set_thread_name("|worker")
|
200
|
+
begin
|
201
|
+
while true
|
202
|
+
event = @input_to_filter.pop
|
203
|
+
|
204
|
+
case event
|
205
|
+
when LogStash::Event
|
206
|
+
# use events array to guarantee ordering of origin vs created events
|
207
|
+
# where created events are emitted by filters like split or metrics
|
208
|
+
events = []
|
209
|
+
filter(event) { |newevent| events << newevent }
|
210
|
+
events.each { |event| @filter_to_output.push(event) }
|
211
|
+
when LogStash::FlushEvent
|
212
|
+
# handle filter flushing here so that non threadsafe filters (thus only running one filterworker)
|
213
|
+
# don't have to deal with thread safety implementing the flush method
|
214
|
+
@flusher_lock.synchronize { flush_filters_to_output! }
|
215
|
+
when LogStash::ShutdownEvent
|
216
|
+
# pass it down to any other filterworker and stop this worker
|
217
|
+
@input_to_filter.push(event)
|
218
|
+
break
|
219
|
+
end
|
220
|
+
end
|
221
|
+
rescue => e
|
222
|
+
@logger.error("Exception in filterworker", "exception" => e, "backtrace" => e.backtrace)
|
223
|
+
end
|
224
|
+
|
225
|
+
@filters.each(&:teardown)
|
226
|
+
end # def filterworker
|
227
|
+
|
228
|
+
def outputworker
|
229
|
+
LogStash::Util::set_thread_name(">output")
|
230
|
+
@outputs.each(&:worker_setup)
|
231
|
+
|
232
|
+
while true
|
233
|
+
event = @filter_to_output.pop
|
234
|
+
break if event.is_a?(LogStash::ShutdownEvent)
|
235
|
+
output(event)
|
236
|
+
end # while true
|
237
|
+
|
238
|
+
@outputs.each do |output|
|
239
|
+
output.worker_plugins.each(&:teardown)
|
240
|
+
end
|
241
|
+
end # def outputworker
|
242
|
+
|
243
|
+
# Shutdown this pipeline.
|
244
|
+
#
|
245
|
+
# This method is intended to be called from another thread
|
246
|
+
def shutdown
|
247
|
+
@input_threads.each do |thread|
|
248
|
+
# Interrupt all inputs
|
249
|
+
@logger.info("Sending shutdown signal to input thread",
|
250
|
+
:thread => thread)
|
251
|
+
thread.raise(LogStash::ShutdownSignal)
|
252
|
+
begin
|
253
|
+
thread.wakeup # in case it's in blocked IO or sleeping
|
254
|
+
rescue ThreadError
|
255
|
+
end
|
256
|
+
|
257
|
+
# Sometimes an input is stuck in a blocking I/O
|
258
|
+
# so we need to tell it to teardown directly
|
259
|
+
@inputs.each do |input|
|
260
|
+
input.teardown
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# No need to send the ShutdownEvent to the filters/outputs nor to wait for
|
265
|
+
# the inputs to finish, because in the #run method we wait for that anyway.
|
266
|
+
end # def shutdown
|
267
|
+
|
268
|
+
def plugin(plugin_type, name, *args)
|
269
|
+
args << {} if args.empty?
|
270
|
+
klass = LogStash::Plugin.lookup(plugin_type, name)
|
271
|
+
return klass.new(*args)
|
272
|
+
end
|
273
|
+
|
274
|
+
def filter(event, &block)
|
275
|
+
@filter_func.call(event, &block)
|
276
|
+
end
|
277
|
+
|
278
|
+
def output(event)
|
279
|
+
@output_func.call(event)
|
280
|
+
end
|
281
|
+
|
282
|
+
# perform filters flush and yeild flushed event to the passed block
|
283
|
+
# @param options [Hash]
|
284
|
+
# @option options [Boolean] :final => true to signal a final shutdown flush
|
285
|
+
def flush_filters(options = {}, &block)
|
286
|
+
flushers = options[:final] ? @shutdown_flushers : @periodic_flushers
|
287
|
+
|
288
|
+
flushers.each do |flusher|
|
289
|
+
flusher.call(options, &block)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# perform filters flush into the output queue
|
294
|
+
# @param options [Hash]
|
295
|
+
# @option options [Boolean] :final => true to signal a final shutdown flush
|
296
|
+
def flush_filters_to_output!(options = {})
|
297
|
+
flush_filters(options) do |event|
|
298
|
+
unless event.cancelled?
|
299
|
+
@logger.debug? and @logger.debug("Pushing flushed events", :event => event)
|
300
|
+
@filter_to_output.push(event)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end # flush_filters_to_output!
|
304
|
+
|
305
|
+
end # class Pipeline
|
@@ -0,0 +1,177 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/namespace"
|
3
|
+
require "logstash/logging"
|
4
|
+
require "logstash/config/mixin"
|
5
|
+
require "cabin"
|
6
|
+
|
7
|
+
class LogStash::Plugin
|
8
|
+
attr_accessor :params
|
9
|
+
attr_accessor :logger
|
10
|
+
|
11
|
+
NL = "\n"
|
12
|
+
|
13
|
+
public
|
14
|
+
def hash
|
15
|
+
params.hash ^
|
16
|
+
self.class.name.hash
|
17
|
+
end
|
18
|
+
|
19
|
+
public
|
20
|
+
def eql?(other)
|
21
|
+
self.class.name == other.class.name && @params == other.params
|
22
|
+
end
|
23
|
+
|
24
|
+
public
|
25
|
+
def initialize(params=nil)
|
26
|
+
@params = params
|
27
|
+
@logger = Cabin::Channel.get(LogStash)
|
28
|
+
end
|
29
|
+
|
30
|
+
# This method is called when someone or something wants this plugin to shut
|
31
|
+
# down. When you successfully shutdown, you must call 'finished'
|
32
|
+
# You must also call 'super' in any subclasses.
|
33
|
+
public
|
34
|
+
def shutdown(queue)
|
35
|
+
# By default, shutdown is assumed a no-op for all plugins.
|
36
|
+
# If you need to take special efforts to shutdown (like waiting for
|
37
|
+
# an operation to complete, etc)
|
38
|
+
teardown
|
39
|
+
@logger.info("Received shutdown signal", :plugin => self)
|
40
|
+
|
41
|
+
@shutdown_queue = queue
|
42
|
+
if @plugin_state == :finished
|
43
|
+
finished
|
44
|
+
else
|
45
|
+
@plugin_state = :terminating
|
46
|
+
end
|
47
|
+
end # def shutdown
|
48
|
+
|
49
|
+
# You should call this method when you (the plugin) are done with work
|
50
|
+
# forever.
|
51
|
+
public
|
52
|
+
def finished
|
53
|
+
# TODO(sissel): I'm not sure what I had planned for this shutdown_queue
|
54
|
+
# thing
|
55
|
+
if @shutdown_queue
|
56
|
+
@logger.info("Sending shutdown event to agent queue", :plugin => self)
|
57
|
+
@shutdown_queue << self
|
58
|
+
end
|
59
|
+
|
60
|
+
if @plugin_state != :finished
|
61
|
+
@logger.info("Plugin is finished", :plugin => self)
|
62
|
+
@plugin_state = :finished
|
63
|
+
end
|
64
|
+
end # def finished
|
65
|
+
|
66
|
+
# Subclasses should implement this teardown method if you need to perform any
|
67
|
+
# special tasks during shutdown (like flushing, etc.)
|
68
|
+
public
|
69
|
+
def teardown
|
70
|
+
# nothing by default
|
71
|
+
finished
|
72
|
+
end
|
73
|
+
|
74
|
+
# This method is called when a SIGHUP triggers a reload operation
|
75
|
+
public
|
76
|
+
def reload
|
77
|
+
# Do nothing by default
|
78
|
+
end
|
79
|
+
|
80
|
+
public
|
81
|
+
def finished?
|
82
|
+
return @plugin_state == :finished
|
83
|
+
end # def finished?
|
84
|
+
|
85
|
+
public
|
86
|
+
def running?
|
87
|
+
return @plugin_state != :finished
|
88
|
+
end # def finished?
|
89
|
+
|
90
|
+
public
|
91
|
+
def terminating?
|
92
|
+
return @plugin_state == :terminating
|
93
|
+
end # def terminating?
|
94
|
+
|
95
|
+
public
|
96
|
+
def to_s
|
97
|
+
return "#{self.class.name}: #{@params}"
|
98
|
+
end
|
99
|
+
|
100
|
+
protected
|
101
|
+
def update_watchdog(state)
|
102
|
+
Thread.current[:watchdog] = Time.now
|
103
|
+
Thread.current[:watchdog_state] = state
|
104
|
+
end
|
105
|
+
|
106
|
+
protected
|
107
|
+
def clear_watchdog
|
108
|
+
Thread.current[:watchdog] = nil
|
109
|
+
Thread.current[:watchdog_state] = nil
|
110
|
+
end
|
111
|
+
|
112
|
+
public
|
113
|
+
def inspect
|
114
|
+
if !@config.nil?
|
115
|
+
description = @config \
|
116
|
+
.select { |k,v| !v.nil? && (v.respond_to?(:empty?) && !v.empty?) } \
|
117
|
+
.collect { |k,v| "#{k}=>#{v.inspect}" }
|
118
|
+
return "<#{self.class.name} #{description.join(", ")}>"
|
119
|
+
else
|
120
|
+
return "<#{self.class.name} --->"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Look up a plugin by type and name.
|
125
|
+
public
|
126
|
+
def self.lookup(type, name)
|
127
|
+
path = "logstash/#{type}s/#{name}"
|
128
|
+
|
129
|
+
# first check if plugin already exists in namespace and continue to next step if not
|
130
|
+
begin
|
131
|
+
return namespace_lookup(type, name)
|
132
|
+
rescue NameError
|
133
|
+
logger.debug("Plugin not defined in namespace, checking for plugin file", :type => type, :name => name, :path => path)
|
134
|
+
end
|
135
|
+
|
136
|
+
# try to load the plugin file. ex.: lookup("filter", "grok") will require logstash/filters/grok
|
137
|
+
require(path)
|
138
|
+
|
139
|
+
# check again if plugin is now defined in namespace after the require
|
140
|
+
namespace_lookup(type, name)
|
141
|
+
rescue LoadError, NameError => e
|
142
|
+
raise(LogStash::PluginLoadingError, I18n.t("logstash.pipeline.plugin-loading-error", :type => type, :name => name, :path => path, :error => e.to_s))
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
# lookup a plugin by type and name in the existing LogStash module namespace
|
148
|
+
# ex.: namespace_lookup("filter", "grok") looks for LogStash::Filters::Grok
|
149
|
+
# @param type [String] plugin type, "input", "ouput", "filter"
|
150
|
+
# @param name [String] plugin name, ex.: "grok"
|
151
|
+
# @return [Class] the plugin class or raises NameError
|
152
|
+
# @raise NameError if plugin class does not exist or is invalid
|
153
|
+
def self.namespace_lookup(type, name)
|
154
|
+
type_const = "#{type.capitalize}s"
|
155
|
+
namespace = LogStash.const_get(type_const)
|
156
|
+
# the namespace can contain constants which are not for plugins classes (do not respond to :config_name)
|
157
|
+
# namespace.constants is the shallow collection of all constants symbols in namespace
|
158
|
+
# note that below namespace.const_get(c) should never result in a NameError since c is from the constants collection
|
159
|
+
klass_sym = namespace.constants.find { |c| is_a_plugin?(namespace.const_get(c), name) }
|
160
|
+
klass = klass_sym && namespace.const_get(klass_sym)
|
161
|
+
raise(NameError) unless klass
|
162
|
+
klass
|
163
|
+
end
|
164
|
+
|
165
|
+
# check if klass is a valid plugin for name
|
166
|
+
# @param klass [Class] plugin class
|
167
|
+
# @param name [String] plugin name
|
168
|
+
# @return [Boolean] true if klass is a valid plugin for name
|
169
|
+
def self.is_a_plugin?(klass, name)
|
170
|
+
klass.ancestors.include?(LogStash::Plugin) && klass.respond_to?(:config_name) && klass.config_name == name
|
171
|
+
end
|
172
|
+
|
173
|
+
# @return [Cabin::Channel] logger channel for class methods
|
174
|
+
def self.logger
|
175
|
+
@logger ||= Cabin::Channel.get(LogStash)
|
176
|
+
end
|
177
|
+
end # class LogStash::Plugin
|