# 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