mirror of
https://github.com/elastic/logstash.git
synced 2025-04-23 22:27:21 -04:00
Fix a concurrency issue with the Webserver when overrideing Puma STDERR and STDOUT.
This PR changes how we modify the STDOUT/STDERR in puma, instande of using `const_set`, we override the constants using a module. This operation should be thread safe and will make sure the STDERR are correctly visible. After when we receive the logger we can swap the null logger with the real thing in a thread safe way. Fixes: #5912 Fixes #5918
This commit is contained in:
parent
0daeec7e04
commit
f0ae34e25a
3 changed files with 50 additions and 23 deletions
44
logstash-core/lib/logstash/patches/puma.rb
Normal file
44
logstash-core/lib/logstash/patches/puma.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
# encoding: utf-8
|
||||
#
|
||||
# Patch to replace the usage of STDERR and STDOUT
|
||||
# see: https://github.com/elastic/logstash/issues/5912
|
||||
module LogStash
|
||||
class NullLogger
|
||||
def self.debug(message)
|
||||
end
|
||||
end
|
||||
|
||||
# Puma uses by default the STDERR an the STDOUT for all his error
|
||||
# handling, the server class accept custom a events object that can accept custom io object,
|
||||
# so I just wrap the logger into an IO like object.
|
||||
class IOWrappedLogger
|
||||
def initialize(new_logger)
|
||||
@logger_lock = Mutex.new
|
||||
@logger = new_logger
|
||||
end
|
||||
|
||||
def sync=(v)
|
||||
# noop
|
||||
end
|
||||
|
||||
def logger=(logger)
|
||||
@logger_lock.synchronize { @logger = logger }
|
||||
end
|
||||
|
||||
def puts(str)
|
||||
# The logger only accept a str as the first argument
|
||||
@logger_lock.synchronize { @logger.debug(str.to_s) }
|
||||
end
|
||||
alias_method :write, :puts
|
||||
alias_method :<<, :puts
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Reopen the puma class to create a scoped STDERR and STDOUT
|
||||
# This operation is thread safe since its done at the class level
|
||||
# and force JRUBY to flush his classes cache.
|
||||
module Puma
|
||||
STDERR = LogStash::IOWrappedLogger.new(LogStash::NullLogger)
|
||||
STDOUT = LogStash::IOWrappedLogger.new(LogStash::NullLogger)
|
||||
end
|
|
@ -2,7 +2,9 @@
|
|||
require "logstash/api/rack_app"
|
||||
require "puma"
|
||||
require "puma/server"
|
||||
require "logstash/patches/puma"
|
||||
require "concurrent"
|
||||
require "thread"
|
||||
|
||||
module LogStash
|
||||
class WebServer
|
||||
|
@ -70,32 +72,13 @@ module LogStash
|
|||
@server.stop(true) if @server
|
||||
end
|
||||
|
||||
# Puma uses by default the STDERR an the STDOUT for all his error
|
||||
# handling, the server class accept custom a events object that can accept custom io object,
|
||||
# so I just wrap the logger into an IO like object.
|
||||
class IOWrappedLogger
|
||||
def initialize(logger)
|
||||
@logger = logger
|
||||
end
|
||||
|
||||
def sync=(v)
|
||||
end
|
||||
|
||||
def puts(str)
|
||||
# The logger only accept a str as the first argument
|
||||
@logger.debug(str.to_s)
|
||||
end
|
||||
alias_method :write, :puts
|
||||
alias_method :<<, :puts
|
||||
end
|
||||
|
||||
def start_webserver(port)
|
||||
# wrap any output that puma could generate into a wrapped logger
|
||||
# use the puma namespace to override STDERR, STDOUT in that scope.
|
||||
io_wrapped_logger = IOWrappedLogger.new(@logger)
|
||||
Puma::STDERR.logger = logger
|
||||
Puma::STDOUT.logger = logger
|
||||
|
||||
::Puma.const_set("STDERR", io_wrapped_logger) unless ::Puma.const_defined?("STDERR")
|
||||
::Puma.const_set("STDOUT", io_wrapped_logger) unless ::Puma.const_defined?("STDOUT")
|
||||
io_wrapped_logger = LogStash::IOWrappedLogger.new(logger)
|
||||
|
||||
app = LogStash::Api::RackApp.app(logger, agent, http_environment)
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ describe LogStash::WebServer do
|
|||
end
|
||||
end
|
||||
|
||||
describe LogStash::WebServer::IOWrappedLogger do
|
||||
describe LogStash::IOWrappedLogger do
|
||||
let(:logger) { spy("logger") }
|
||||
let(:message) { "foobar" }
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue