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

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

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

Начать курс

Введение в логирование TypeScript приложений

Логирование — это процесс записи событий, происходящих во время работы программы. Для TypeScript-приложений, особенно работающих в среде Node.js, грамотно настроенное логирование является не просто хорошим тоном, а критически важным инструментом для отладки, мониторинга и анализа ошибок.

Многие начинающие разработчики ограничиваются использованием console.log(). Однако в production-среде такой подход неэффективен: он не позволяет гибко управлять уровнями важности, фильтровать сообщения или сохранять их в файлы. В этой статье мы разберём, как создать профессиональную систему логирования в TypeScript с нуля.

Почему не стоит использовать console.log в production?

Взглянем на типичный пример:

// Плохой подход — используем console.log напрямую
function processOrder(orderId: string): void {
  console.log(`Processing order ${orderId}`);
  try {
    // бизнес-логика
  } catch (error) {
    console.error(`Error processing order: ${error}`);
  }
}

Проблемы такого подхода:

  • Отсутствие уровней логирования: нельзя отделить debug-сообщения от критических ошибок.
  • Нет форматирования: сложно читать большие объёмы данных.
  • Нельзя перенаправить: все сообщения идут только в stdout/stderr.
  • Проблемы с производительностью: синхронная запись в консоль может замедлить приложение.

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

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

Для TypeScript рекомендуется использовать одну из двух популярных библиотек:

  • Winston — мощный и гибкий логгер с поддержкой транспортов (файл, консоль, базы данных).
  • Pino — сверхбыстрый логгер, идеальный для high-load приложений.

Установим Winston:

npm install winston
npm install @types/winston --save-dev

2. Создание конфигурируемого логгера

Создадим модуль logger.ts:

import winston from 'winston';

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.colorize(),
        winston.format.simple()
      )
    }),
    new winston.transports.File({ 
      filename: 'logs/error.log', 
      level: 'error' 
    }),
    new winston.transports.File({ 
      filename: 'logs/combined.log' 
    })
  ]
});

export default logger;

Теперь можно использовать логгер в любом модуле:

import logger from './logger';

function getUser(id: string) {
  logger.info(`Fetching user with ID: ${id}`);
  try {
    // логика получения пользователя
    logger.debug('User data retrieved successfully');
  } catch (error) {
    logger.error('Failed to fetch user', { userId: id, error });
  }
}

3. Использование уровней логирования

Стандартные уровни (от наимение важного к самому важному):

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

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

// В .env файле
LOG_LEVEL=debug  # для разработки
LOG_LEVEL=warn   # для production

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

Передавайте дополнительный контекст в виде объекта:

logger.info('Order processed', {
  orderId: '12345',
  userId: 'user_678',
  amount: 150.50,
  duration: 245 // ms
});

Это позволяет легко анализировать логи с помощью инструментов вроде ELK Stack или Grafana.

Продвинутые техники для TypeScript

Типизация логгера

Создайте типизированную обёртку:

interface LoggerContext {
  requestId?: string;
  userId?: string;
  service?: string;
}

class TypedLogger {
  private logger: winston.Logger;

  constructor(context?: LoggerContext) {
    this.logger = winston.createLogger({
      defaultMeta: context || {}
    });
  }

  info(message: string, meta?: Record<string, unknown>) {
    this.logger.info(message, meta);
  }

  error(message: string, error?: Error, meta?: Record<string, unknown>) {
    this.logger.error(message, { ...meta, error: error?.stack });
  }
}

export default TypedLogger;

Асинхронное логирование

Для высоконагруженных систем используйте Pino с асинхронным транспортом:

import pino from 'pino';

const logger = pino({
  transport: {
    target: 'pino/file',
    options: { destination: './logs/app.log', sync: false }
  }
});

Заключение

Грамотное логирование в TypeScript — это не просто замена console.log на библиотеку. Это архитектурное решение, которое включает:

  • Выбор подходящей библиотеки (Winston для гибкости, Pino для скорости).
  • Настройку уровней логирования через переменные окружения.
  • Структурированное логирование с контекстом.
  • Разделение транспортов для разных уровней.

Начните с внедрения Winston в ваш проект — это даст 80% необходимого функционала. По мере роста приложения вы сможете добавить централизованный сбор логов, мониторинг и алерты. Помните: хорошие логи экономят часы отладки и помогают быстрее находить причины проблем в production.

Блоги

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