Почему нужно делать return await внутри асинхронных функций и методов а не возвращать промис?nodejs-65

Логирование — критически важная часть любого Node.js приложения. Рассмотрим основные подходы, их преимущества и недостатки.

1. Консольное логирование

Простейший подход:

console.log('Запрос получен:', req.url);
console.error('Ошибка:', err);

Плюсы:

  • Простота использования (встроено в Node.js)
  • Не требует дополнительных зависимостей
  • Подходит для быстрого прототипирования

Минусы:

  • Нет уровней логирования
  • Нет ротации логов
  • Плохая производительность в production
  • Нет структурированного формата

2. Использование Winston

Популярная библиотека логирования:

const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});

logger.info('Запрос', { method: req.method, url: req.url });

Плюсы:

  • Поддержка разных уровней (error, warn, info, debug)
  • Несколько транспортов (файл, консоль, базы данных)
  • Структурированные логи (JSON)
  • Плагины и кастомизация

Минусы:

  • Требует настройки
  • Дополнительная зависимость

3. Использование Pino

Высокопроизводительный логгер:

const pino = require('pino');

const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  formatters: {
    level: (label) => ({ level: label })
  }
});

logger.info({ req }, 'Входящий запрос');

Плюсы:

  • Очень высокая производительность
  • Поддержка асинхронного логирования
  • Автоматическое определение контекста
  • Поддержка HTTP-транспорта

Минусы:

  • Менее гибкий, чем Winston
  • Сложнее конфигурировать

4. Bunyan

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

const bunyan = require('bunyan');

const log = bunyan.createLogger({
  name: 'myapp',
  streams: [
    { level: 'error', path: '/var/log/myapp-error.log' }
  ]
});

log.info({ req }, 'Запрос начат');

Плюсы:

  • Четкая структура логов
  • Встроенная сериализация
  • Поддержка ротации логов

Минусы:

  • Менее популярен, чем Winston/Pino
  • Ограниченная кастомизация

5. Системное логирование

Интеграция с системным логгером:

const syslog = require('modern-syslog');

syslog.init('myapp', syslog.LOG_PID, syslog.LOG_LOCAL0);
syslog.log(syslog.LOG_INFO, 'Запуск приложения');

Плюсы:

  • Интеграция с инфраструктурой мониторинга
  • Централизованное хранение логов
  • Поддержка ротации и архивирования

Минусы:

  • Сложность настройки
  • Зависит от ОС
  • Менее гибкий формат

6. Логирование в базу данных

Пример с MongoDB:

const { MongoClient } = require('mongodb');
const winston = require('winston');
require('winston-mongodb');

const logger = winston.createLogger({
  transports: [
    new winston.transports.MongoDB({
      db: 'mongodb://localhost/logs',
      collection: 'app_logs'
    })
  ]
});

Плюсы:

  • Возможность сложных запросов
  • Долгосрочное хранение
  • Интеграция с аналитикой

Минусы:

  • Производительность
  • Сложность масштабирования
  • Дополнительные затраты

7. Распределенное логирование

Пример с ELK Stack:

const { Client } = require('@elastic/elasticsearch');
const client = new Client({ node: 'http://localhost:9200' });

async function logToElastic(data) {
  await client.index({
    index: 'app-logs',
    body: {
      timestamp: new Date(),
      level: 'info',
      message: 'Запрос обработан',
      ...data
    }
  });
}

Плюсы:

  • Централизованное хранение
  • Мощные возможности поиска
  • Визуализация (Kibana, Grafana)

Минусы:

  • Сложность настройки инфраструктуры
  • Требует ресурсов

Сравнение производительности

Характеристика Stateless Stateful
МасштабируемостьЛегко горизонтально масштабируетсяТребует синхронизации состояния
ОтказоустойчивостьВыше (нет критичного состояния)Ниже (потеря состояния = проблема)
ПроизводительностьПредсказуемаяМожет быть выше за счет кэша
СложностьПрощеСложнее (нужно управлять состоянием)
Использование памятиМинимальноеЗависит от объема состояния

Лучшие практики

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

    • error: Критические ошибки
    • warn: Предупреждения
    • info: Важная информация
    • debug: Отладочная информация
    • trace: Трассировка
  2. Контекст важнее сообщений:

// Плохо
logger.error('Ошибка валидации');

// Хорошо
logger.error('Ошибка валидации', {
  input: req.body,
  rules: validationRules,
  userId: req.user.id
});
  1. Разделяйте логи по назначению:

    • Ошибки приложения
    • Бизнес-события
    • Аудиторские логи
  2. Не логируйте конфиденциальные данные:

    • Пароли
    • Токены
    • Персональные данные

Резюмируем:

Выбор подхода к логированию зависит от масштаба приложения. Для небольших проектов достаточно Winston, для высоконагруженных систем лучше подойдет Pino. В production обязательно используйте структурированные логи (JSON) и централизованное хранение (ELK, Loki). Помните: хорошие логи — это не просто записанные сообщения, а структурированные данные для последующего анализа.