Skip to main content

Що містить stack trace

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, щоб не втратити контекст.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.