Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що містить stack trace». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Stack trace, це текстовий «знімок» стека викликів на момент створення помилки: тип помилки, її повідомлення і ланцюжок функцій, що привів до збою.** Він лежить у властивості `error.stack`, де перший рядок, це `Name: message`, а кожен наступний рядок `at ...` описує один рівень виклику: ім'я функції, файл, номер рядка і стовпця. Читають його згори вниз: угорі місце помилки, нижче той, хто його викликав. ```javascript const err = new Error("Network failure"); console.log(err.stack); // Error: Network failure // at <anonymous>:1:13 ``` **Ключове:** `stack` формується у момент створення об'єкта помилки, а не у момент `throw`, і асинхронні межі (`setTimeout`, промиси) розривають ланцюжок.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Stack trace, це рядок у властивості `error.stack`, що містить тип помилки, її повідомлення і ланцюжок викликів функцій, який привів до збою.** Кожен рядок `at ...` показує одну функцію, її файл, рядок і стовпець. ## Теорія ### TL;DR - Стек викликів (call stack), це структура, де рушій тримає перелік функцій, що зараз виконуються. - Коли створюється помилка, рушій робить «знімок» цього стека і кладе його в `error.stack`. - Перший рядок `stack`, це `Name: message`, далі йдуть рядки `at функція (файл:рядок:стовпець)`. - Порядок читання: згори місце помилки, нижче ті, хто його викликав. - Асинхронні виклики (`setTimeout`, промиси, `await`) можуть «розірвати» стек, бо виконуються в іншій ітерації event loop. ### Швидкий приклад ```javascript function a() { b(); } function b() { c(); } function c() { throw new Error("Something went wrong!"); } try { a(); } catch (err) { console.log(err.stack); } ``` Вивід у консолі: ```text Error: Something went wrong! at c (<anonymous>:8:9) at b (<anonymous>:4:3) at a (<anonymous>:1:3) at <anonymous>:12:3 ``` ### Що таке стек викликів **Стек викликів (call stack)**, це структура даних, у якій JavaScript **зберігає інформацію про те, які функції зараз виконуються** і **в якому порядку їх було викликано**. Коли виникає помилка, рушій створює «знімок» цього стека, той самий **stack trace**, що показує, *де саме і в якій послідовності було викликано код, який привів до помилки*. ### Як читати рядки stack trace Кожен рядок у `stack`, це **один рівень виклику функції**. Розберемо приклад вище: ```text Error: Something went wrong! <- тип помилки і повідомлення at c (<anonymous>:8:9) <- функція c() кинула помилку at b (<anonymous>:4:3) <- c() було викликано з b() at a (<anonymous>:1:3) <- b() було викликано з a() at <anonymous>:12:3 <- a() викликано в глобальній області ``` Кожен рядок вказує: - ім'я функції (`at c`, `at b`, `at a`); - файл (або `<anonymous>`, якщо коду немає імені файлу); - номер рядка і стовпця, де стався виклик. ### Де живе стек: властивість .stack Коли створюється об'єкт помилки (`new Error()`), у нього автоматично з'являється властивість `.stack`, яка містить: - назву помилки (`Error`, `TypeError` тощо); - повідомлення (`message`); - трасування викликів (stack trace). ```javascript const err = new Error("Network failure"); console.log(err.stack); ``` Вивід: ```text Error: Network failure at <anonymous>:1:13 ``` У реальній обробці помилок зручно логувати обидві частини окремо: ```javascript try { throw new Error("Unexpected failure"); } catch (e) { console.error("Error:", e.message); console.error("Call stack:\n", e.stack); } ``` Власні помилки теж мають стек. Якщо ви створюєте свій клас через `class ... extends Error`, властивість `.stack` буде доступна так само: ```javascript class ValidationError extends Error { constructor(message) { super(message); this.name = "ValidationError"; } } try { throw new ValidationError("Invalid email"); } catch (e) { console.log(e.stack); } ``` Вивід: ```text ValidationError: Invalid email at <anonymous>:8:9 at ... ``` ### Стек при асинхронних помилках Асинхронні виклики (через `setTimeout`, `Promise`, `await`) можуть «розірвати» стек, бо виконуються пізніше, в іншій ітерації event loop. ```javascript function a() { setTimeout(() => { throw new Error("Timer failure"); }, 0); } a(); ``` Вивід: ```text Uncaught Error: Timer failure at Timeout._onTimeout (<anonymous>:3:11) ``` > Тут видно лише частину стека, бо помилка сталася вже **в іншому контексті виконання**. Функції `a()` в трасуванні немає: на момент спрацювання таймера її кадр зі стека вже зник. ### Практичне застосування Stack trace потрібен, щоб: - швидко знаходити місце збою в коді; - розуміти, які функції привели до помилки; - зберігати трасування в логах і системах моніторингу (Sentry, GlitchTip, Logtail); - будувати власний бекенд для аналізу помилок користувачів. **Складові помилки в одному місці:** | Елемент | Що це означає | | --- | --- | | `Error.message` | Текст помилки | | `Error.name` | Тип помилки (`TypeError`, `ReferenceError`, ...) | | `Error.stack` | Повний шлях викликів до місця помилки | | Кожен рядок `at ...` | Одна функція в ланцюжку викликів | | Порядок | Згори місце помилки, нижче той, хто її викликав | ### Типові помилки - Вважати `error.stack` частиною стандарту з гарантованим форматом. Це де-факто угода, і текст відрізняється між рушіями, тож парсити його ненадійно. - Думати, що стек фіксується під час `throw`. Насправді він записується у момент `new Error(...)`, тому створювати помилку заздалегідь і кидати пізніше, погана ідея. - Логувати лише `e.message` і втрачати `e.stack`, після чого шукати причину збою немає за чим. - Кидати рядок або об'єкт замість `Error`, у такого значення стека немає взагалі. - Дивуватися короткому стеку в асинхронному коді. Передавайте початкову помилку далі через `cause`, щоб не втратити контекст.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.