Логирование в TypeScript приложениях: лучшие практики

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Зачем нужно логирование в TypeScript?

Логирование — это не просто запись сообщений в консоль. В современных TypeScript приложениях (серверных на Node.js, клиентских на React/Angular или full-stack проектах) логирование становится критическим инструментом для отладки, мониторинга и анализа производительности. Без качественного логирования разработчики тратят часы на воспроизведение ошибок, а DevOps-инженеры не могут быстро определить причину сбоя в production.

TypeScript, как надстройка над JavaScript, добавляет строгую типизацию, что позволяет создавать более надёжные и предсказуемые системы логирования. В этой статье мы разберём лучшие практики, которые помогут вам организовать эффективное логирование в TypeScript-проектах.

Основные принципы логирования

Уровни логирования

Правильное использование уровней логирования — основа любой системы мониторинга. В TypeScript принято использовать следующие уровни (по возрастанию важности):

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

Пример настройки уровней с помощью библиотеки Winston:

import winston from 'winston';

const logger = winston.createLogger({
  level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
  format: winston.format.json(),
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
  ],
});

logger.info('Сервер запущен на порту 3000');
logger.warn('Память близка к лимиту');
logger.error('Ошибка подключения к БД');

Структурированные логи

Избегайте простых строковых сообщений. Используйте структурированные логи с контекстной информацией (JSON-формат). Это упрощает поиск и анализ в системах вроде ELK или Grafana.

// Плохо
logger.info(`Пользователь ${userId} выполнил действие ${action}`);

// Хорошо
logger.info('Действие пользователя', {
  userId: userId,
  action: action,
  timestamp: new Date().toISOString(),
  ip: request.ip,
});

Такой подход позволяет фильтровать логи по полям и строить графики.

Лучшие практики для TypeScript

Использование TypeScript для типизации логов

TypeScript позволяет определить интерфейсы для контекста логов, что предотвращает случайные ошибки в названиях полей:

interface LogContext {
  userId: string;
  action: string;
  durationMs: number;
  success: boolean;
}

function logUserAction(context: LogContext): void {
  logger.info('User action', context);
}

// Компилятор проверит, что все поля переданы корректно
logUserAction({
  userId: '123',
  action: 'login',
  durationMs: 150,
  success: true,
});

Избегайте логирования чувствительных данных

Никогда не логируйте пароли, токены, номера кредитных карт или персональные данные (GDPR). Используйте утилиты для маскировки:

function sanitizeLogData(data: Record<string, unknown>): Record<string, unknown> {
  const sensitiveFields = ['password', 'token', 'ssn'];
  const sanitized = { ...data };
  for (const field of sensitiveFields) {
    if (sanitized[field]) {
      sanitized[field] = '***';
    }
  }
  return sanitized;
}

logger.info('User registration', sanitizeLogData(userData));

Асинхронное логирование и производительность

В высоконагруженных приложениях синхронное логирование может замедлить работу. Используйте асинхронные транспорты (например, winston поддерживает буферизацию).

import winston from 'winston';
import 'winston-daily-rotate-file';

const transport = new winston.transports.DailyRotateFile({
  filename: 'app-%DATE%.log',
  datePattern: 'YYYY-MM-DD',
  maxSize: '20m',
  maxFiles: '14d',
  // Асинхронная запись
  zippedArchive: true,
});

const asyncLogger = winston.createLogger({
  transports: [transport],
});

Выбор библиотеки для логирования

Сравнение популярных решений

Для TypeScript-проектов на Node.js чаще всего используют:

  • Winston — самый популярный, гибкий, множество транспортов (файлы, консоль, внешние сервисы).
  • Pino — очень быстрый, минималистичный, отлично подходит для микросервисов.
  • Bunyan — старый, но надёжный, выводит логи в JSON.
  • Loglevel — лёгкая библиотека для клиентских приложений (браузер).

Пример использования Pino с TypeScript (очень высокая производительность):

import pino from 'pino';

const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  formatters: {
    level(label) {
      return { level: label };
    },
  },
  // Для production можно отключить красивое форматирование
  transport: process.env.NODE_ENV !== 'production'
    ? { target: 'pino-pretty' }
    : undefined,
});

logger.info({ userId: 42 }, 'User logged in');

Заключение

Логирование в TypeScript — это не просто техническая необходимость, а стратегический инструмент для поддержки качества кода и быстрого реагирования на инциденты. Следуя описанным практикам, вы сможете:

  • Быстро находить и исправлять ошибки;
  • Собирать метрики для оптимизации производительности;
  • Обеспечить безопасность данных (за счёт маскировки);
  • Интегрироваться с системами мониторинга (Grafana, ELK, Datadog).

Начните с малого: выберите библиотеку (Winston или Pino), определите уровни логирования и добавьте типизацию для контекста. Постепенно вы построите систему, которая станет основой для стабильной работы вашего TypeScript-приложения.

Не забывайте регулярно пересматривать

Блоги

Рекомендаций по книгам