---
title: "Laravel queue:work тепер показує причину зупинки воркера"
url: https://laravelukraine.com/blog/laravel-queuework-teper-pokazuje-pricinu-zupinki-vorkera
date: 2026-09-04
source: https://laravel-news.com/laravel-queue-worker-stop-reasons?utm_medium=feed&utm_source=feedpress.me&utm_campaign=Feed%3A+laravelnews
---

# Laravel queue:work тепер показує причину зупинки воркера

Раніше, коли воркер черги зупинявся і Supervisor запускав новий, логи показували лише обробку завдань, потім пропуск, а потім знову обробку. Незалежно від того, чи була це команда `queue:restart`, досягнення ліміту пам'яті або втрата з'єднання з базою даних - вивід виглядав однаково.

У Laravel 13.30 `queue:work` тепер записує причину зупинки:

```
2026-09-01 13:20:40 Worker STOPPED Memory limit exceeded
```

## Що вже було раніше

Воркер і раніше знав, чому він зупиняється. Подія `WorkerStopping` передає `WorkerStopReason` разом зі статусом виходу, кількістю оброблених завдань, часовою міткою останнього завдання та використаною пам'яттю:

```php
use Illuminate\Queue\Events\WorkerStopping;
Event::listen(function (WorkerStopping $event) {
    Log::info('Worker stopped', [
        'reason' => $event->reason?->value,
        'status' => $event->status,
        'jobs' => $event->jobsProcessed,
        'memory' => $event->memoryUsage,
    ]);
});
```

Це працює і досі залишається правильним інструментом, коли потрібно відправляти дані в систему метрик. Але це занадто багато коду для типового випадку - коли ви просто хочете побачити в терміналі або лозі, чому процес завершився.

## Новий вивід

Тепер `queue:work` сам слухає цю подію та виводить фінальний рядок. У терміналі:

```
2026-09-01 13:20:40 Worker STOPPED Queue empty
```

З опцією `--json` це виводиться як запис поряд із виводом для кожного завдання, тому pipeline логів, що вже парсить одне, зможе парсити й інше:

```json
{"level":"info","status":"stopped","reason":"empty","exit_code":0,"jobs_processed":12,"memory":34.0,"timestamp":"2026-09-01T13:20:40.118273+00:00"}
```

`reason` - це backing value enum, стабільний для порівняння. `memory` вказується в мегабайтах, округлених до одного знака після коми. `level` залежить від коду виходу: `info` для чистого виходу, `warning` для всього іншого - на практиці це означає перевищення ліміту пам'яті або таймаут завдання.

Нічого не виводиться з опціями `--quiet` або `--silent`, або коли причина null. Останній випадок варто знати: воркер, убитий ззовні (OOM killer або `SIGKILL`), ніколи не досягає коду, що відправляє подію. Тиша все ще означає, що процес завершився без запиту.

## Дев'ять причин зупинки

`WorkerStopReason` отримав метод `description()`, що містить людино-читабельні рядки. Тепер enum - це єдине місце, звідки і консольний вивід, і ваші власні слухачі можуть їх читати:

| Значення | Опис | Код виходу |
|----------|------|------------|
| `empty` | Queue empty | 0 |
| `empty_for` | Queue empty for the configured duration | 0 |
| `max_jobs` | Maximum jobs exceeded | 0 |
| `max_time` | Maximum run time exceeded | 0 |
| `restart_signal` | Received restart signal | 0 |
| `interrupted` | Interrupted | 0 |
| `lost_connection` | Lost connection | 0 |
| `memory` | Memory limit exceeded | 12 |
| `timed_out` | Job timed out | 1 |

Два ненульові коди - це ті, на які варто налаштувати алерти, і вони означають різні речі:

**`memory`** - це коли воркер помічає, що його власне використання пам'яті перевищило `--memory` між завданнями. Це механізм працює як задумано. Це стає сигналом, коли це відбувається після двох завдань замість двох тисяч - це вказує на завдання, що тримає великий result set, або на витік пам'яті в чомусь, що воркер завантажує один раз. Код виходу 12 - це `Worker::EXIT_MEMORY_LIMIT`, і його можна налаштувати через `Worker::$memoryExceededExitCode`, якщо ваш менеджер процесів обробляє конкретні коди по-різному.

**`timed_out`** відрізняється за своєю природою. Інші вісім причин - це коли воркер вирішує зупинитися між завданнями; ця - коли батьківський процес вбиває дочірній, що перевищив `--timeout` посеред завдання. Метод `failed()` цього завдання може не виконатися так, як при звичайній помилці, тому серія виходів `timed_out` зазвичай означає, що одне завдання потребує довшого `$timeout` або меншої одиниці роботи.

## Інші причини зупинки

Причини з нульовим статусом переважно інформаційні, з одним винятком. `lost_connection` завершується чисто, бо перепідключення - це робота менеджера процесів, але постійний потік таких виходів означає, що з'єднання з базою даних або Redis обривається під воркером, часто через idle timeout на файрволі або проксі, а не самою базою даних.

Інші описують намір: `restart_signal` - це коли хтось запускає `queue:restart`, зазвичай під час деплою. `empty` та `empty_for` - це `--stop-when-empty` та `--stop-when-empty-for`, що очікується на контейнері, який масштабується до нуля. `max_jobs` та `max_time` - це `--max-jobs` та `--max-time`, навмисне перезапуск, що запобігає накопиченню стану в довготривалому PHP-процесі. `interrupted` - це `SIGINT` або `SIGTERM`, що відправляє Ctrl-C та більшість завершень контейнерів.

## Читання в продакшені

Цінність JSON-формату в тому, що питання "чому воркер перезапустився" більше не потребує кореляції часових міток. Фільтруйте за причиною, і відповідь у одному полі:

```bash
php artisan queue:work --json --max-time=3600 2>&1 | \
  jq -c 'select(.status == "stopped")'
```

Під менеджером процесів, що пише у файл, той самий запит виконується проти логу постфактум. Воркер, що перезапускається на `max_time` кожну годину - це конфігурація працює. Той самий воркер, що перезапускається на `memory` кожні чотири хвилини - це витік пам'яті. Раніше обидва виглядали як перезапуск.

Слухач події все ще є для будь-чого, що виходить за межі читання, наприклад, інкрементування лічильника на причину, щоб дашборд показував розподіл у часі, а не один рядок на вихід.

Додано [@jackbayliss](https://github.com/jackbayliss) у [#61339](https://github.com/laravel/framework/pull/61339).
