Логирование и отслеживание ошибок

Логирование и отслеживание ошибок

Технический фундамент наблюдаемости

Логирование это часть наблюдаемости системы. Технически полезный лог должен быть:

  • структурированным (объект с фиксированными полями);
  • консистентным по формату во всех модулях;
  • пригодным для фильтрации по level, operation, requestId;
  • безопасным по данным (без секретов и персональных полей в открытом виде).

Инженерный стандарт: сначала определи схему лога, потом пиши сообщения.

Почему без логов отладка превращается в угадайку

Ошибка без контекста почти бесполезна. Сообщение "Something went wrong" не говорит, где упало, при каких данных и как часто это повторяется. Логирование превращает хаос в наблюдаемую систему: ты видишь причину, место и последствия сбоя.

Ключевой момент: лог это не просто console.log, а структурированная запись события, которую можно анализировать.

Проверь себя: какая информация в ошибке помогает исправить баг быстрее всего?

Уровни логов: что и когда писать

На практике удобно думать уровнями:

  • debug - детали для локальной отладки;
  • info - важные шаги сценария;
  • warn - подозрительные, но не критичные ситуации;
  • error - сбои, влияющие на функциональность.

В учебных примерах часто хватает console.error, console.warn, console.log, но привычка разделять уровни нужна уже сейчас.

function parseProfile(raw) {
  try {
    return JSON.parse(raw);
  } catch (error) {
    console.error('parseProfile failed', {
      message: error.message,
      raw,
    });
    return null;
  }
}

Смотри, что важно: мы логируем не только текст ошибки, но и контекст (raw). Иначе потом трудно воспроизвести проблему.

Что должно быть в хорошем логе

Минимальный полезный набор полей:

  • название операции (operation);
  • сообщение ошибки (message);
  • входные данные или их безопасная часть;
  • время события;
  • идентификатор пользователя/запроса (если есть).
function logError(operation, error, context = {}) {
  console.error({
    level: 'error',
    operation,
    name: error.name,
    message: error.message,
    stack: error.stack,
    timestamp: new Date().toISOString(),
    context,
  });
}

Смотри, что важно: Error плохо сериализуется через JSON.stringify (у него многие поля не перечислимые), поэтому лучше логировать name/message/stack явно.

const e = new Error('boom');
console.log(JSON.stringify(e)); // {}

Новый термин: корреляция логов - связывание записей по общему requestId/traceId, чтобы видеть полный путь запроса через систему.

Проверь себя: почему один и тот же requestId в нескольких логах сильно ускоряет анализ инцидента?

Локальная отладка против продакшен-логирования

В локальной среде ты можешь писать подробные логи и быстро экспериментировать. В проде нужны ограничения:

  • не логировать чувствительные данные (password, токены, полный номер карты);
  • не спамить логами в циклах без необходимости;
  • сохранять единый формат записи.

Здесь часто путаются: "чем больше логов, тем лучше". На практике шумные логи ухудшают диагностику, потому что полезные записи тонут.

Мини-сценарий: ошибка при сохранении профиля

  • Пользователь нажал "Сохранить".
  • API вернул ошибку валидации.
  • UI показывает сообщение "Проверь данные".
  • В лог уходит событие saveProfileFailed с userId, полем и причиной.
function handleSaveError(error, userId) {
  logError('saveProfile', error, { userId });
  return 'Не удалось сохранить профиль. Проверь данные и попробуй еще раз.';
}

Дополнительный пример: маскирование чувствительных полей перед логированием.

function maskEmail(email) {
  if (typeof email !== 'string' || !email.includes('@')) return '[invalid-email]';
  const [name, domain] = email.split('@');
  return `${name.slice(0, 2)}***@${domain}`;
}

Мы не показываем внутреннюю техническую ошибку пользователю, но сохраняем достаточно данных для команды.

Отслеживание ошибок во времени

Логирование отвечает на вопрос "что случилось". Отслеживание (monitoring/tracking) добавляет "как часто и где".

Практически это значит:

  • считать частоту ошибок по типам;
  • смотреть, после какого релиза выросли падения;
  • видеть топ проблемных экранов/операций.

Даже простая метрика вроде "ошибок парсинга за час" может раньше заметить деградацию, чем жалобы пользователей.

Проверь себя: почему единичный TypeError может быть случайностью, а 500 таких же за 10 минут - инцидентом?

Частые ошибки новичков

  • Логировать только error.message без контекста.
  • Ловить ошибку и не логировать вообще.
  • Логировать слишком чувствительные данные.
  • Использовать разный формат логов в каждом файле.

Анти-провал: заранее определи маленький стандарт лога для проекта и придерживайся его везде.

Что будет, если изменить входные данные

Если в parseProfile передать корректный JSON, error-лог не появится. Если передать поломанную строку, получишь одну понятную запись с контекстом, и это поможет воспроизвести баг в пару шагов.

Так ты переходишь от "иногда падает" к "падает при таком входе, в таком месте, с такой причиной".

Краткий итог

  • Логирование нужно, чтобы ошибка была наблюдаемой и воспроизводимой.
  • Полезный лог содержит контекст, а не только текст исключения.
  • Разделяй уровни логов и не создавай шум.
  • В проде соблюдай безопасность данных и единый формат.
  • Отслеживание динамики ошибок помогает находить проблемы до массовых жалоб.