Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions testdata/crashing-worker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

// Boots successfully, then terminates with a non-zero exit status while
// handling its second request, simulating a worker crash after boot.
$requests = 0;
$handler = static function () use (&$requests) {
if (++$requests >= 2) {
exit(1);
}

echo 'ok';
};

while (frankenphp_handle_request($handler)) {
}
8 changes: 5 additions & 3 deletions threadworker.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,10 @@ func tearDownWorkerScript(handler *workerThread, exitStatus int) {

if !handler.isBootingScript {
// fatal error (could be due to exit(1), timeouts, etc.)
if globalLogger.Enabled(globalCtx, slog.LevelDebug) {
globalLogger.LogAttrs(globalCtx, slog.LevelDebug, "restarting", slog.String("worker", worker.name), slog.Int("thread", handler.thread.threadIndex), slog.Int("exit_status", exitStatus))
// unlike a clean restart, this took down any in-flight request, so
// surface it above debug level, with the exit status needed to triage it
if globalLogger.Enabled(globalCtx, slog.LevelWarn) {
globalLogger.LogAttrs(globalCtx, slog.LevelWarn, "unexpected termination, restarting", slog.String("worker", worker.name), slog.Int("thread", handler.thread.threadIndex), slog.Int("exit_status", exitStatus))
}

return
Expand All @@ -175,7 +177,7 @@ func tearDownWorkerScript(handler *workerThread, exitStatus int) {

if watcherIsEnabled {
// worker script has probably failed due to script changes while watcher is enabled
if globalLogger.Enabled(globalCtx, slog.LevelError) {
if globalLogger.Enabled(globalCtx, slog.LevelWarn) {
globalLogger.LogAttrs(globalCtx, slog.LevelWarn, "(watcher enabled) worker script has not reached frankenphp_handle_request()", slog.String("worker", worker.name), slog.Int("thread", handler.thread.threadIndex))
}
} else {
Expand Down
12 changes: 12 additions & 0 deletions worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,18 @@ func TestWorkerGetOpt(t *testing.T) {
assert.NotRegexp(t, buf.String(), "exit_status=[1-9]")
}

func TestWorkerCrashIsLoggedAboveDebugLevel(t *testing.T) {
logger, buf := newTestLogger(t)

runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) {
req := httptest.NewRequest("GET", "http://example.com/crashing-worker.php", nil)
w := httptest.NewRecorder()
handler(w, req)
}, &testOptions{logger: logger, workerScript: "crashing-worker.php", nbWorkers: 1, nbParallelRequests: 4})

assert.Regexp(t, `level=WARN msg="unexpected termination, restarting" worker=\S+ thread=\d+ exit_status=1`, buf.String())
}

func ExampleServeHTTP_workers() {
if err := frankenphp.Init(
frankenphp.WithWorkers("worker1", "worker1.php", 4,
Expand Down
Loading