npm package: @kiz8/logger
Description: Универсальный логгер для системы управления репутацией
Author: Kiz8
Latest version: 0.1.0

# @reputation-management/logger

Универсальный логгер для системы управления репутацией, построенный на базе [Winston](https://github.com/winstonjs/winston).

## Архитектура и обзор

`@reputation-management/logger` представляет собой унифицированное решение для логирования в проектах системы управления репутацией. Пакет предоставляет обертку над библиотекой Winston, добавляя необходимую для проекта функциональность и интеграцию.

Основные архитектурные компоненты:

- **Ядро логгера**: Основано на Winston с настраиваемыми транспортами для консоли и файлов.
- **Форматирование**: Структурированные JSON-логи с метаданными и читаемое форматирование для консоли.
- **Интеграции**: Готовые интеграции с Hono (HTTP), Inngest (фоновые задачи) и Vercel Fluid Functions.
- **Утилиты**: Вспомогательные функции для логирования ошибок и создания дочерних логгеров.

Архитектура логгера разработана для обеспечения:

- **Гибкости**: Возможность настройки под различные сценарии использования
- **Расширяемости**: Простое добавление новых транспортов и форматеров
- **Консистентности**: Единый формат логов во всех частях системы
- **Производительности**: Оптимизированное логирование для снижения влияния на производительность

## Возможности

- Гибкая конфигурация уровней логирования
- Логирование в консоль и файлы
- Ротация файлов логов
- Форматирование логов с метаданными
- Интеграция с Hono для логирования HTTP запросов
- Интеграция с Inngest для логирования фоновых задач
- Интеграция с Vercel Fluid Functions
- Создание дочерних логгеров с предустановленными метаданными

## Установка

```bash
npm install @reputation-management/logger
```

или

```bash
yarn add @reputation-management/logger
```

## Использование

### Базовое использование

```typescript
import { defaultLogger, LogLevel } from "@reputation-management/logger";

// Использование логгера по умолчанию
defaultLogger.info("Приложение запущено");
defaultLogger.error("Произошла ошибка", { error: "Детали ошибки" });

// Создание собственного логгера
import { createLogger } from "@reputation-management/logger";

const logger = createLogger({
  level: LogLevel.DEBUG,
  console: true,
  file: true,
  logDir: "./logs",
  defaultMeta: {
    service: "my-service",
  },
});

logger.info("Сообщение с метаданными", { userId: "123", action: "login" });

// Установка уровня логирования динамически
logger.setLevel(LogLevel.WARN); // Теперь будут логгироваться только WARN и ERROR

// Получение текущего логгера из контекста
import { getLogger } from "@reputation-management/logger";
const contextLogger = getLogger();
```

### Логирование ошибок

```typescript
import {
  defaultLogger,
  createErrorMeta,
  formatError,
} from "@reputation-management/logger";

try {
  // Какой-то код, который может вызвать ошибку
  throw new Error("Пример ошибки");
} catch (error) {
  if (error instanceof Error) {
    // Вариант 1: Использование createErrorMeta
    defaultLogger.error("Произошла ошибка", createErrorMeta(error));

    // Вариант 2: Форматирование ошибки в строку
    defaultLogger.error(formatError(error));

    // Вариант 3: Полное логирование для критических ошибок
    defaultLogger.error("Критическая ошибка в системе", {
      ...createErrorMeta(error),
      component: "payment-service",
      operation: "processTransaction",
      transactionId: "12345",
      isRetryable: false,
    });
  }
}

// Логирование асинхронных ошибок
async function fetchData() {
  try {
    const response = await fetch("https://api.example.com/data");
    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    defaultLogger.error(
      "Ошибка при запросе данных",
      error instanceof Error
        ? createErrorMeta(error)
        : { error: String(error) },
    );
    throw error; // Переброс ошибки для обработки выше
  }
}
```

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

```typescript
import { defaultLogger } from "@reputation-management/logger";
import { v4 as uuidv4 } from "uuid";

// Использование трассировки запросов
function processOrder(order, user) {
  const traceId = uuidv4();
  const logger = defaultLogger.child({
    traceId,
    userId: user.id,
    orderId: order.id,
  });

  logger.info("Начало обработки заказа");

  // Логирование каждого шага с тем же traceId
  try {
    logger.info("Проверка наличия товаров", {
      products: order.products.map((p) => p.id),
    });

    logger.info("Проверка платежной информации", {
      paymentMethod: order.paymentMethod,
    });

    logger.info("Создание заказа завершено", {
      status: "completed",
      total: order.total,
    });

    return { success: true, orderId: order.id };
  } catch (error) {
    logger.error("Ошибка при обработке заказа", createErrorMeta(error));
    return { success: false, error: error.message };
  }
}
```

### Интеграция с Hono

```typescript
import { Hono } from "hono";
import {
  defaultLogger,
  honoLoggerMiddleware,
  honoErrorLoggerMiddleware,
} from "@reputation-management/logger";

const app = new Hono();

// Базовая интеграция
// ===================

// Добавление middleware для логирования запросов
app.use("*", honoLoggerMiddleware(defaultLogger));

// Добавление middleware для логирования ошибок
app.onError(honoErrorLoggerMiddleware(defaultLogger));

// Маршруты приложения
app.get("/", (c) => c.text("Hello World"));

// Расширенная интеграция
// ======================

// Настройка middleware с дополнительными опциями
app.use(
  "*",
  honoLoggerMiddleware(defaultLogger, {
    excludePaths: ["/health", "/metrics"], // Исключить пути из логирования
    logBody: true, // Логировать тело запроса
    maskHeaders: ["authorization", "x-api-key"], // Маскировать заголовки
    getRequestId: (c) => c.req.header("x-request-id") || uuidv4(), // Функция для получения ID запроса
  }),
);

// Создание middleware для конкретной группы маршрутов
const apiRoutes = new Hono();

// Логгер с предустановленными метаданными для API
const apiLogger = defaultLogger.child({ component: "api" });

// Применение логгера только для API маршрутов
apiRoutes.use("*", honoLoggerMiddleware(apiLogger));

// Добавление API маршрутов к основному приложению
app.route("/api", apiRoutes);

// Пример обработки запроса с пользовательским логированием
app.post("/api/users", async (c) => {
  const requestLogger = apiLogger.child({
    requestId: c.get("requestId"),
    path: "/api/users",
  });

  requestLogger.info("Получен запрос на создание пользователя");

  try {
    const body = await c.req.json();
    requestLogger.debug("Тело запроса", { body });

    // Логика создания пользователя
    const user = await createUser(body);

    requestLogger.info("Пользователь успешно создан", { userId: user.id });
    return c.json({ success: true, user });
  } catch (error) {
    requestLogger.error(
      "Ошибка при создании пользователя",
      createErrorMeta(error),
    );
    return c.json({ success: false, error: error.message }, 500);
  }
});

export default app;
```

### Интеграция с Inngest

```typescript
import { Inngest } from "inngest";
import {
  defaultLogger,
  createInngestLogger,
} from "@reputation-management/logger";

// Создание клиента Inngest
const inngest = new Inngest({ name: "My App" });

// Создание функции Inngest
const myFunction = inngest.createFunction(
  { name: "My Function" },
  { event: "my/event" },
  async ({ event, step }) => {
    // Создание логгера для функции
    const logger = createInngestLogger(defaultLogger, "My Function");

    // Логирование начала выполнения
    logger.start("my/event", event.data);

    try {
      // Выполнение шага
      logger.stepStart("process-data");
      const result = await step.run("process-data", async () => {
        // Логирование внутри шага
        logger.info("Обработка данных", { data: event.data });
        return { processed: true };
      });
      logger.stepSuccess("process-data", result);

      // Дополнительные шаги с логированием
      logger.stepStart("notify-user");
      await step.run("notify-user", async () => {
        logger.info("Отправка уведомления пользователю", {
          userId: event.data.userId,
        });
        // Реализация отправки уведомления
        return { notified: true };
      });
      logger.stepSuccess("notify-user");

      // Логирование успешного завершения
      logger.success(result);
      return result;
    } catch (error) {
      // Логирование ошибки
      logger.error(error);
      throw error;
    }
  },
);

// Регистрация множества функций с одним логгером
const createUserFunction = inngest.createFunction(
  { name: "Create User" },
  { event: "user/created" },
  async ({ event, step }) => {
    const logger = createInngestLogger(defaultLogger, "Create User", {
      tenant: event.data.tenantId,
      environment: process.env.NODE_ENV,
    });

    logger.start("user/created", { userId: event.data.userId });

    // Реализация функции...

    return { success: true };
  },
);

// Мониторинг выполнения Inngest функций
function setupInngestMonitoring(inngestClient) {
  const monitoringLogger = defaultLogger.child({
    component: "inngest-monitor",
  });

  // Слушаем события выполнения функций
  inngestClient.onFunctionRun((event) => {
    monitoringLogger.info("Inngest функция запущена", {
      functionId: event.functionId,
      eventId: event.eventId,
      attemptNumber: event.attemptNumber,
    });
  });

  // Слушаем события завершения функций
  inngestClient.onFunctionComplete((event) => {
    monitoringLogger.info("Inngest функция завершена", {
      functionId: event.functionId,
      eventId: event.eventId,
      duration: event.duration,
      status: "success",
    });
  });

  // Слушаем события ошибок функций
  inngestClient.onFunctionError((event) => {
    monitoringLogger.error("Ошибка выполнения Inngest функции", {
      functionId: event.functionId,
      eventId: event.eventId,
      error: event.error,
      status: "failed",
    });
  });
}
```

### Интеграция с Vercel Fluid Functions

```typescript
import {
  defaultLogger,
  createVercelFunctionLogger,
} from "@reputation-management/logger";

export default async function handler(req, res) {
  // Создание логгера для функции
  const logger = createVercelFunctionLogger(defaultLogger, "api/example");

  // Логирование начала выполнения с расширенными метаданными
  logger.start({
    method: req.method,
    url: req.url,
    userAgent: req.headers["user-agent"],
    requestId:
      req.headers["x-request-id"] || Math.random().toString(36).substring(2),
    ip: req.headers["x-forwarded-for"] || req.connection.remoteAddress,
  });

  try {
    // Логирование информации о запросе
    logger.info("Обработка запроса", {
      query: req.query,
      headers: filterSensitiveHeaders(req.headers),
      body: req.body ? sanitizeRequestBody(req.body) : undefined,
    });

    // Измерение времени выполнения
    const startTime = Date.now();

    // Выполнение бизнес-логики
    const result = await processRequest(req);

    // Логирование времени выполнения
    const executionTime = Date.now() - startTime;
    logger.info("Запрос обработан", { executionTime });

    // Логирование успешного завершения
    logger.success({
      result: omitSensitiveData(result),
      executionTime,
    });

    return res.status(200).json(result);
  } catch (error) {
    // Расширенное логирование ошибки
    logger.error("Ошибка при обработке запроса", {
      error: error instanceof Error ? error.message : String(error),
      stack: error instanceof Error ? error.stack : undefined,
      path: req.url,
      method: req.method,
      requestId: req.headers["x-request-id"],
    });

    // Определение кода ошибки
    const statusCode = error.statusCode || 500;
    const errorMessage =
      statusCode === 500 ? "Internal Server Error" : error.message;

    // Возврат ответа с ошибкой
    return res.status(statusCode).json({
      error: errorMessage,
      requestId: req.headers["x-request-id"],
    });
  } finally {
    // Логирование завершения обработки запроса
    logger.info("Обработка запроса завершена");
  }
}

// Вспомогательные функции
function filterSensitiveHeaders(headers) {
  const filtered = { ...headers };
  const sensitiveHeaders = ["authorization", "cookie", "x-api-key"];

  for (const header of sensitiveHeaders) {
    if (filtered[header]) {
      filtered[header] = "***";
    }
  }

  return filtered;
}

function sanitizeRequestBody(body) {
  if (!body) return undefined;

  const sanitized = { ...body };
  const sensitiveFields = ["password", "token", "secret", "creditCard"];

  for (const field of sensitiveFields) {
    if (sanitized[field]) {
      sanitized[field] = "***";
    }
  }

  return sanitized;
}

function omitSensitiveData(data) {
  // Реализация удаления чувствительных данных из ответа
  return data;
}

async function processRequest(req) {
  // Пример реализации бизнес-логики
  return { success: true, data: { processed: true } };
}
```

### Мониторинг и анализ логов

```typescript
import { createLogger, LogLevel } from "@reputation-management/logger";
import { format, transports } from "winston";

// Логгер с расширенными возможностями мониторинга
const monitoringLogger = createLogger({
  level: LogLevel.INFO,
  console: true,
  file: true,
  defaultMeta: {
    service: "monitoring-service",
    version: process.env.APP_VERSION || "1.0.0",
    environment: process.env.NODE_ENV || "development",
  },
});

// Добавление транспорта для отправки критических ошибок в систему оповещения
if (process.env.NODE_ENV === "production") {
  monitoringLogger.add(
    new transports.Http({
      host: "alerts.example.com",
      path: "/api/log",
      ssl: true,
      level: "error",
      format: format.json(),
      auth: {
        username: process.env.ALERT_API_USERNAME,
        password: process.env.ALERT_API_PASSWORD,
      },
    }),
  );
}

// Функция для измерения производительности операций
function measurePerformance(operationName, callback) {
  const startTime = performance.now();
  const result = callback();
  const endTime = performance.now();

  monitoringLogger.info("Метрика производительности", {
    operation: operationName,
    durationMs: endTime - startTime,
    timestamp: new Date().toISOString(),
  });

  return result;
}

// Измерение асинхронных операций
async function measureAsyncPerformance(operationName, asyncCallback) {
  const startTime = performance.now();
  try {
    const result = await asyncCallback();
    const endTime = performance.now();

    monitoringLogger.info("Метрика производительности", {
      operation: operationName,
      durationMs: endTime - startTime,
      timestamp: new Date().toISOString(),
      success: true,
    });

    return result;
  } catch (error) {
    const endTime = performance.now();

    monitoringLogger.error("Ошибка операции с метрикой", {
      operation: operationName,
      durationMs: endTime - startTime,
      timestamp: new Date().toISOString(),
      success: false,
      error: error instanceof Error ? error.message : String(error),
    });

    throw error;
  }
}

// Пример использования мониторинга
async function monitoredOperation() {
  return measureAsyncPerformance("database-query", async () => {
    // Асинхронный запрос к базе данных
    const result = await db.query("SELECT * FROM users LIMIT 100");
    return result;
  });
}
```

### Создание дочерних логгеров

```typescript
import { defaultLogger } from "@reputation-management/logger";

// Создание логгера для конкретного сервиса
const userServiceLogger = defaultLogger.child({
  service: "user-service",
  component: "auth",
});

userServiceLogger.info("Пользователь вошел в систему", { userId: "123" });

// Иерархия дочерних логгеров
const authLogger = defaultLogger.child({ component: "auth" });
const loginLogger = authLogger.child({ operation: "login" });
const registrationLogger = authLogger.child({ operation: "registration" });

// Использование дочерних логгеров
function userLogin(username, password) {
  loginLogger.info("Попытка входа в систему", { username });

  // Логика аутентификации...

  loginLogger.info("Успешный вход", { username });
}

function userRegistration(userData) {
  registrationLogger.info("Начало регистрации пользователя");

  // Логика регистрации...

  registrationLogger.info("Пользователь зарегистрирован", {
    username: userData.username,
    email: userData.email,
  });
}

// Передача логгеров между модулями
function createAuthService(options = {}) {
  const logger =
    options.logger || defaultLogger.child({ service: "auth-service" });

  return {
    login: (username, password) => {
      logger.info("Вход в систему", { username });
      // ...
    },
    logout: (userId) => {
      logger.info("Выход из системы", { userId });
      // ...
    },
    // Получение логгера для использования другими функциями
    getLogger: () => logger,
  };
}

// Использование
const authService = createAuthService({
  logger: defaultLogger.child({
    service: "custom-auth-service",
    tenant: "tenant-123",
  }),
});

// Получение логгера из сервиса для дальнейшего использования
const serviceLogger = authService.getLogger();
serviceLogger.debug("Отладочная информация из сервиса аутентификации");
```

## Конфигурация

Логгер поддерживает следующие параметры конфигурации:

| Параметр    | Тип      | По умолчанию                         | Описание                                      |
| ----------- | -------- | ------------------------------------ | --------------------------------------------- |
| level       | LogLevel | LogLevel.INFO                        | Уровень логирования                           |
| logDir      | string   | './logs'                             | Директория для хранения логов                 |
| maxSize     | string   | '20m'                                | Максимальный размер файла лога перед ротацией |
| maxDays     | number   | 14                                   | Максимальное количество дней хранения логов   |
| console     | boolean  | true                                 | Включить логирование в консоль                |
| file        | boolean  | true                                 | Включить логирование в файл                   |
| datePattern | string   | 'YYYY-MM-DD'                         | Формат даты для имени файла                   |
| filePrefix  | string   | 'app'                                | Префикс для имени файла                       |
| defaultMeta | object   | { service: 'reputation-management' } | Дополнительные метаданные для каждого лога    |
| colorize    | boolean  | true                                 | Включить цветное форматирование в консоли     |

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

Логгер поддерживает следующие уровни логирования (в порядке убывания важности):

1. `ERROR` - Ошибки, требующие внимания
2. `WARN` - Предупреждения, которые не являются ошибками
3. `INFO` - Информационные сообщения
4. `HTTP` - HTTP запросы
5. `VERBOSE` - Подробная информация
6. `DEBUG` - Отладочная информация
7. `SILLY` - Детальная отладочная информация

## API документация

### Основные компоненты

#### Logger

Класс `Logger` предоставляет основные методы для логирования:

| Метод                     | Описание                                                    |
| ------------------------- | ----------------------------------------------------------- |
| `error(message, meta?)`   | Логирование ошибок                                          |
| `warn(message, meta?)`    | Логирование предупреждений                                  |
| `info(message, meta?)`    | Логирование информационных сообщений                        |
| `http(message, meta?)`    | Логирование HTTP запросов                                   |
| `verbose(message, meta?)` | Логирование подробной информации                            |
| `debug(message, meta?)`   | Логирование отладочной информации                           |
| `silly(message, meta?)`   | Логирование детальной отладочной информации                 |
| `child(meta)`             | Создание дочернего логгера с предустановленными метаданными |

#### Экспортируемые функции

| Функция                                            | Описание                                                        |
| -------------------------------------------------- | --------------------------------------------------------------- |
| `createLogger(options)`                            | Создает новый экземпляр логгера с указанными опциями            |
| `defaultLogger`                                    | Предустановленный экземпляр логгера с настройками по умолчанию  |
| `createErrorMeta(error)`                           | Создает метаданные для логирования ошибок, включая стек вызовов |
| `formatError(error)`                               | Форматирует ошибку в строку для логирования                     |
| `honoLoggerMiddleware(logger)`                     | Создает middleware для логирования HTTP запросов в Hono         |
| `honoErrorLoggerMiddleware(logger)`                | Создает middleware для логирования ошибок в Hono                |
| `createInngestLogger(logger, functionName)`        | Создает логгер для функций Inngest                              |
| `createVercelFunctionLogger(logger, functionPath)` | Создает логгер для Vercel Fluid Functions                       |

#### Типы

| Тип             | Описание                                                                                        |
| --------------- | ----------------------------------------------------------------------------------------------- |
| `LogLevel`      | Перечисление уровней логирования (`ERROR`, `WARN`, `INFO`, `HTTP`, `VERBOSE`, `DEBUG`, `SILLY`) |
| `LoggerOptions` | Интерфейс опций для создания логгера                                                            |
| `LogMeta`       | Тип для метаданных логирования (расширяет `Record<string, any>`)                                |

### Параметры конфигурации (LoggerOptions)

| Параметр      | Тип      | По умолчанию                         | Описание                                      |
| ------------- | -------- | ------------------------------------ | --------------------------------------------- |
| `level`       | LogLevel | LogLevel.INFO                        | Минимальный уровень логирования               |
| `logDir`      | string   | './logs'                             | Директория для хранения файлов логов          |
| `maxSize`     | string   | '20m'                                | Максимальный размер файла лога перед ротацией |
| `maxDays`     | number   | 14                                   | Максимальное количество дней хранения логов   |
| `console`     | boolean  | true                                 | Включить логирование в консоль                |
| `file`        | boolean  | true                                 | Включить логирование в файл                   |
| `datePattern` | string   | 'YYYY-MM-DD'                         | Формат даты для имени файла                   |
| `filePrefix`  | string   | 'app'                                | Префикс для имени файла                       |
| `defaultMeta` | object   | { service: 'reputation-management' } | Дополнительные метаданные для каждого лога    |
| `colorize`    | boolean  | true                                 | Включить цветное форматирование в консоли     |

## Расширенное использование

### Настройка форматирования логов

```typescript
import { createLogger, LogLevel, format } from "@reputation-management/logger";
import { format as winstonFormat } from "winston";

// Создание логгера с пользовательским форматированием
const logger = createLogger({
  level: LogLevel.INFO,
  console: true,
  file: true,
  // Добавление пользовательского форматера
  format: winstonFormat.combine(
    winstonFormat.timestamp(),
    winstonFormat.json(),
    winstonFormat((info) => {
      info.custom_field = "custom_value";
      return info;
    })(),
  ),
});
```

### Расширение логгера дополнительными транспортами

```typescript
import { createLogger, LogLevel } from "@reputation-management/logger";
import { transports } from "winston";

// Создание логгера с дополнительным транспортом
const logger = createLogger({
  level: LogLevel.INFO,
  console: true,
  file: true,
});

// Добавление транспорта Syslog
logger.add(
  new transports.Syslog({
    host: "logs.papertrailapp.com",
    port: 12345,
    protocol: "tls4",
    localhost: "my-app",
    eol: "\n",
  }),
);
```

### Создание иерархии логгеров для разных компонентов

```typescript
import { defaultLogger } from "@reputation-management/logger";

// Логгер для API-слоя
const apiLogger = defaultLogger.child({ component: "api" });

// Логгеры для разных контроллеров
const userController = apiLogger.child({ controller: "user" });
const orderController = apiLogger.child({ controller: "order" });

// Использование
userController.info("Получение профиля пользователя", { userId: "123" });
orderController.info("Создание заказа", { orderId: "456", userId: "123" });
```

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

1. **Структурированное логирование**: Всегда используйте метаданные для контекста

   ```typescript
   logger.info("Пользователь авторизован", {
     userId: user.id,
     role: user.role,
     requestId: context.requestId,
   });
   ```

2. **Централизованное управление уровнями логирования**:

   ```typescript
   // config.ts
   export const LOG_LEVEL = process.env.LOG_LEVEL || "info";

   // logger.ts
   import { LOG_LEVEL } from "./config";
   import { createLogger, LogLevel } from "@reputation-management/logger";

   export const logger = createLogger({
     level: LOG_LEVEL as LogLevel,
     // другие настройки
   });
   ```

3. **Трассировка запросов** через метаданные:

   ```typescript
   // middleware.ts
   import { v4 as uuidv4 } from "uuid";

   app.use("*", (c, next) => {
     c.set("requestId", uuidv4());
     return next();
   });

   // route-handler.ts
   app.get("/api/users", (c) => {
     const requestId = c.get("requestId");
     logger.info("Запрос списка пользователей", { requestId });
     // ...
   });
   ```

## Руководство для разработчиков

### Установка и настройка

```bash
# Клонирование репозитория
git clone [URL-репозитория]
cd packages/logger

# Установка зависимостей
pnpm install

# Сборка пакета
pnpm run build

# Запуск тестов
pnpm run test

# Проверка линтером
pnpm run lint
```

### Добавление новых интеграций

Для добавления интеграции с новой платформой:

1. Создайте новый файл в директории `src/integrations/`
2. Реализуйте фабричную функцию для создания специализированного логгера
3. Добавьте экспорт в `src/index.ts`
4. Обновите документацию и добавьте примеры использования

### Структура проекта

```
packages/logger/
src/
  ├── constants.ts    # Константы и настройки по умолчанию
  ├── format.ts       # Форматирование логов
  ├── index.ts        # Основной экспорт
  ├── integrations/   # Интеграции с другими платформами
  ├── logger.ts       # Основная реализация логгера
  ├── types.ts        # Типы и интерфейсы
  └── utils.ts        # Вспомогательные функции
```

## Вклад в развитие проекта

Мы приветствуем вклад сообщества! Подробнее о процессе разработки и правилах можно узнать в [CONTRIBUTING.md](./CONTRIBUTING.md).

## Лицензия

MIT