From 2af91a1fef70d6546ee03760011c170a082db667 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Fri, 17 Feb 2017 16:12:33 -0800 Subject: Add after_worker_exit configuration option This option is executed in the master process following all worker process exits. It is most useful in the case where the worker process crashes the ruby interpreter, as the worker process may not be able to send error notifications appropriately. For example, let's say you have a specific request that crashes a worker process, which you expect to be due to a improperly programmed C extension. By modifying your worker to save request related data in a temporary file and using this option, you can get a record of what request is crashing the application, which will make debugging easier. Example: after_worker_exit do |server, worker, status| server.logger.info "worker #{status.success? ? 'exit' : 'crash'}: #{status}" file = "request.#{status.pid}.txt" if File.exist?(file) do_something_with(File.read(file)) unless status.success? File.delete(file) end end --- lib/unicorn/configurator.rb | 21 +++++++++++++++++++++ lib/unicorn/http_server.rb | 4 ++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb index 3329c10..5bad925 100644 --- a/lib/unicorn/configurator.rb +++ b/lib/unicorn/configurator.rb @@ -41,6 +41,14 @@ class Unicorn::Configurator :before_exec => lambda { |server| server.logger.info("forked child re-executing...") }, + :after_worker_exit => lambda { |server, worker, status| + m = "reaped #{status.inspect} worker=#{worker.nr rescue 'unknown'}" + if status.success? + server.logger.info(m) + else + server.logger.error(m) + end + }, :pid => nil, :preload_app => false, :check_client_connection => false, @@ -151,6 +159,19 @@ class Unicorn::Configurator set_hook(:after_fork, block_given? ? block : args[0]) end + # sets after_worker_exit hook to a given block. This block will be called + # by the master process after a worker exits: + # + # after_fork do |server,worker,status| + # # status is a Process::Status instance for the exited worker process + # unless status.success? + # server.logger.error("worker process failure: #{status.inspect}") + # end + # end + def after_worker_exit(*args, &block) + set_hook(:after_worker_exit, block_given? ? block : args[0], 3) + end + # sets before_fork got be a given Proc object. This Proc # object will be called by the master process before forking # each worker. diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb index 35bd100..c2086cb 100644 --- a/lib/unicorn/http_server.rb +++ b/lib/unicorn/http_server.rb @@ -15,6 +15,7 @@ class Unicorn::HttpServer :before_fork, :after_fork, :before_exec, :listener_opts, :preload_app, :orig_app, :config, :ready_pipe, :user + attr_writer :after_worker_exit attr_reader :pid, :logger include Unicorn::SocketHelper @@ -395,8 +396,7 @@ class Unicorn::HttpServer proc_name 'master' else worker = @workers.delete(wpid) and worker.close rescue nil - m = "reaped #{status.inspect} worker=#{worker.nr rescue 'unknown'}" - status.success? ? logger.info(m) : logger.error(m) + @after_worker_exit.call(self, worker, status) end rescue Errno::ECHILD break -- cgit v1.2.3-24-ge0c7