50 lines
1.3 KiB
Ruby
50 lines
1.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# This class is used to log messages to a specified logger from within a `Signal.trap` context. Most loggers rely on
|
|
# methods that are prohibited within a `Signal.trap` context, so this class is used to queue up log messages and then
|
|
# log them from a separate thread outside of the `Signal.trap` context.
|
|
#
|
|
# Example:
|
|
# Signal.trap("USR1") do
|
|
# SignalTrapLogger.instance.log(Rails.logger, "Received USR1 signal")
|
|
# end
|
|
#
|
|
# Do note that you need to call `SignalTrapLogger.instance.after_fork` after forking a new process to ensure that the
|
|
# logging thread is running in the new process.
|
|
class SignalTrapLogger
|
|
include Singleton
|
|
|
|
def initialize
|
|
@queue = Queue.new
|
|
ensure_logging_thread_running
|
|
end
|
|
|
|
def log(logger, message, level: :info)
|
|
@queue << { logger:, message:, level: }
|
|
end
|
|
|
|
def after_fork
|
|
ensure_logging_thread_running
|
|
end
|
|
|
|
private
|
|
|
|
def ensure_logging_thread_running
|
|
return if @thread&.alive?
|
|
|
|
@thread =
|
|
Thread.new do
|
|
loop do
|
|
begin
|
|
log_entry = @queue.pop
|
|
log_entry[:logger].public_send(log_entry[:level], log_entry[:message])
|
|
rescue => error
|
|
Rails.logger.error(
|
|
"Error in SignalTrapLogger thread: #{error.message}\n#{error.backtrace.join("\n")}",
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|