Логирование и отладка
Winter Thread предлагает гибкую систему обработки вывода фоновых процессов, управляемую
двумя параметрами start(): $debugMode и $outputTarget.
Почему по умолчанию /dev/null
Когда родитель открывает pipe ($outputTarget = null), но никогда его не читает, буфер ОС
(~64 КБ) заполняется, дочерний процесс блокируется на write() и в итоге получает
Broken pipe — это молча убивает фоновые задачи. Значение по умолчанию '/dev/null'
полностью это предотвращает: вывод отбрасывается ОС без буферизации. Передавайте null
только тогда, когда активно читаете через readOutput() / readError().
Стратегия 1 — Fire and forget (по умолчанию)
Самый безопасный режим для продакшн-задач. Вывод отбрасывается; pipe не открывается, поэтому
родитель может запустить задачу и сразу освободить объект Thread.
start()— эквивалентstart([], false, '/dev/null')$debugMode: false— ошибки PHP в дочернем процессе подавляются.$outputTarget: '/dev/null'— вывод отбрасывается ОС.
<?php
require 'vendor/autoload.php';
use Flytachi\Winter\Thread\Runnable;
use Flytachi\Winter\Thread\Thread;
$thread = new Thread(new class implements Runnable {
public function run(array $args): void {
// Тяжёлая задача — вывод по умолчанию отбрасывается
file_put_contents('/tmp/result.json', json_encode(['done' => true]));
}
});
$pid = $thread->start(); // безопасный fire-and-forget
echo "Started background process PID: $pid\n";
// Объект Thread можно освободить здесь; риска Broken pipe нетСтратегия 2 — Лог в файл
Рекомендуется для staging и продакшна, когда нужна запись. Весь вывод (echo, var_dump,
ошибки PHP) дописывается в файл.
start(true, '/path/to/file.log')$debugMode: true— ошибки PHP включены и видны в логе.$outputTarget: '/path/file.log'— вывод дописывается.
<?php
require 'vendor/autoload.php';
use Flytachi\Winter\Thread\Runnable;
use Flytachi\Winter\Thread\Thread;
$thread = new Thread(new class implements Runnable {
public function run(array $args): void {
echo "Task started at: " . date('Y-m-d H:i:s') . PHP_EOL;
echo "Processing..." . PHP_EOL;
// Предупреждение — видно в логе, потому что debugMode = true
$result = 10 / 0;
}
});
$logFile = __DIR__ . '/worker.log';
$pid = $thread->start(debugMode: true, outputTarget: $logFile);
echo "PID: $pid — logging to {$logFile}\n";Читаем лог:
tail -f worker.logОжидаемое содержимое:
Task started at: 2025-12-19 10:30:00
Processing...
Warning: Division by zero in ... on line XXСтратегия 3 — Интерактивный режим (pipe)
Для локальной разработки и отладки. Родитель читает вывод дочернего процесса в реальном
времени. Вы обязаны явно передать null и обязаны активно опрашивать
readOutput() / readError(), пока процесс жив — иначе буфер pipe заполнится и вызовет
Broken pipe.
start(true, null)$debugMode: true— ошибки PHP включены.$outputTarget: null— вывод передаётся родителю через pipe.
<?php
require 'vendor/autoload.php';
use Flytachi\Winter\Thread\Runnable;
use Flytachi\Winter\Thread\Thread;
$thread = new Thread(new class implements Runnable {
public function run(array $args): void {
for ($i = 1; $i <= 3; $i++) {
echo "Step {$i}..." . PHP_EOL;
sleep(1);
}
trigger_error("Custom warning for demo", E_USER_WARNING);
}
});
$pid = $thread->start(debugMode: true, outputTarget: null);
echo "Interactive session started, PID: $pid\n\n";
// ВАЖНО: активно вычитывайте pipe, пока процесс работает
while ($thread->isAlive()) {
$out = $thread->readOutput();
if ($out !== '') {
echo '[STDOUT] ' . rtrim($out) . "\n";
}
$err = $thread->readError();
if ($err !== '') {
echo '[STDERR] ' . rtrim($err) . "\n";
}
usleep(250_000);
}
// Дочитываем остаток вывода после завершения процесса
$out = $thread->readOutput();
if ($out !== '') {
echo '[STDOUT] ' . rtrim($out) . "\n";
}
$err = $thread->readError();
if ($err !== '') {
echo '[STDERR] ' . rtrim($err) . "\n";
}
$exitCode = $thread->join();
echo "\nProcess $pid finished with exit code: $exitCode\n";Ожидаемый вывод (появляется постепенно):
Interactive session started, PID: 12345
[STDOUT] Step 1...
[STDOUT] Step 2...
[STDOUT] Step 3...
[STDERR] Warning: Custom warning for demo in ... on line XX
Process 12345 finished with exit code: 0Итог
| Режим | $outputTarget |
$debugMode |
Когда использовать |
|---|---|---|---|
| Fire and forget | '/dev/null' (по умолчанию) |
false |
Продакшн-задачи в фоне |
| Лог в файл | '/path/file.log' |
true |
Staging / продакшн с журналом |
| Интерактивный pipe | null (явно) |
true |
Локальная разработка, чтение вывода в реальном времени |