From 4ec2e760724b48989cb077a9a11764b0e4273fda Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Przemys=C5=82aw=20=C5=81ada?= <przlada@gmail.com>
Date: Mon, 14 Oct 2024 19:27:02 +0200
Subject: [PATCH 1/2] Extend the "Child process died" log to include `exitcode`
 and `signal_name`

---
 uvicorn/supervisors/multiprocess.py | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/uvicorn/supervisors/multiprocess.py b/uvicorn/supervisors/multiprocess.py
index e198fe780..2b2835363 100644
--- a/uvicorn/supervisors/multiprocess.py
+++ b/uvicorn/supervisors/multiprocess.py
@@ -98,6 +98,10 @@ def join(self) -> None:
     def pid(self) -> int | None:
         return self.process.pid
 
+    @property
+    def exitcode(self) -> int | None:
+        return self.process.exitcode
+
 
 class Multiprocess:
     def __init__(
@@ -167,13 +171,20 @@ def keep_subprocess_alive(self) -> None:
             if process.is_alive():
                 continue
 
+            exitcode = process.exitcode
             process.kill()  # process is hung, kill it
             process.join()
 
             if self.should_exit.is_set():
                 return  # pragma: full coverage
 
-            logger.info(f"Child process [{process.pid}] died")
+            signal_name = None
+            if exitcode is not None and exitcode < 0:
+                try:
+                    signal_name = signal.Signals(-exitcode).name
+                except ValueError:
+                    pass
+            logger.info(f"Child process [{process.pid}] died", extra={"exitcode": exitcode, "signal_name": signal_name})
             process = Process(self.config, self.target, self.sockets)
             process.start()
             self.processes[idx] = process

From 9eef487beda1fdd4f70f7b98967f22c1aea3b4be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Przemys=C5=82aw=20=C5=81ada?= <przlada@gmail.com>
Date: Mon, 14 Oct 2024 20:39:05 +0200
Subject: [PATCH 2/2] Add # pragma: no cover

---
 uvicorn/supervisors/multiprocess.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/uvicorn/supervisors/multiprocess.py b/uvicorn/supervisors/multiprocess.py
index 2b2835363..0cb38bfd1 100644
--- a/uvicorn/supervisors/multiprocess.py
+++ b/uvicorn/supervisors/multiprocess.py
@@ -182,7 +182,7 @@ def keep_subprocess_alive(self) -> None:
             if exitcode is not None and exitcode < 0:
                 try:
                     signal_name = signal.Signals(-exitcode).name
-                except ValueError:
+                except ValueError:  # pragma: no cover
                     pass
             logger.info(f"Child process [{process.pid}] died", extra={"exitcode": exitcode, "signal_name": signal_name})
             process = Process(self.config, self.target, self.sockets)