@kiz8/logger
v0.1.0
Published
Универсальный логгер для системы управления репутацией
Downloads
7
Readme
@reputation-management/logger
Универсальный логгер для системы управления репутацией, построенный на базе Winston.
Архитектура и обзор
@reputation-management/logger представляет собой унифицированное решение для логирования в проектах системы управления репутацией. Пакет предоставляет обертку над библиотекой Winston, добавляя необходимую для проекта функциональность и интеграцию.
Основные архитектурные компоненты:
- Ядро логгера: Основано на Winston с настраиваемыми транспортами для консоли и файлов.
- Форматирование: Структурированные JSON-логи с метаданными и читаемое форматирование для консоли.
- Интеграции: Готовые интеграции с Hono (HTTP), Inngest (фоновые задачи) и Vercel Fluid Functions.
- Утилиты: Вспомогательные функции для логирования ошибок и создания дочерних логгеров.
Архитектура логгера разработана для обеспечения:
- Гибкости: Возможность настройки под различные сценарии использования
- Расширяемости: Простое добавление новых транспортов и форматеров
- Консистентности: Единый формат логов во всех частях системы
- Производительности: Оптимизированное логирование для снижения влияния на производительность
Возможности
- Гибкая конфигурация уровней логирования
- Логирование в консоль и файлы
- Ротация файлов логов
- Форматирование логов с метаданными
- Интеграция с Hono для логирования HTTP запросов
- Интеграция с Inngest для логирования фоновых задач
- Интеграция с Vercel Fluid Functions
- Создание дочерних логгеров с предустановленными метаданными
Установка
npm install @reputation-management/loggerили
yarn add @reputation-management/loggerИспользование
Базовое использование
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();Логирование ошибок
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; // Переброс ошибки для обработки выше
}
}Структурированное логирование и трассировка
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
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
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
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 } };
}Мониторинг и анализ логов
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;
});
}Создание дочерних логгеров
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 | Включить цветное форматирование в консоли |
Уровни логирования
Логгер поддерживает следующие уровни логирования (в порядке убывания важности):
ERROR- Ошибки, требующие вниманияWARN- Предупреждения, которые не являются ошибкамиINFO- Информационные сообщенияHTTP- HTTP запросыVERBOSE- Подробная информацияDEBUG- Отладочная информация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 | Включить цветное форматирование в консоли |
Расширенное использование
Настройка форматирования логов
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;
})(),
),
});Расширение логгера дополнительными транспортами
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",
}),
);Создание иерархии логгеров для разных компонентов
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" });Лучшие практики логирования в продакшене
Структурированное логирование: Всегда используйте метаданные для контекста
logger.info("Пользователь авторизован", { userId: user.id, role: user.role, requestId: context.requestId, });Централизованное управление уровнями логирования:
// 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, // другие настройки });Трассировка запросов через метаданные:
// 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 }); // ... });
Руководство для разработчиков
Установка и настройка
# Клонирование репозитория
git clone [URL-репозитория]
cd packages/logger
# Установка зависимостей
pnpm install
# Сборка пакета
pnpm run build
# Запуск тестов
pnpm run test
# Проверка линтером
pnpm run lintДобавление новых интеграций
Для добавления интеграции с новой платформой:
- Создайте новый файл в директории
src/integrations/ - Реализуйте фабричную функцию для создания специализированного логгера
- Добавьте экспорт в
src/index.ts - Обновите документацию и добавьте примеры использования
Структура проекта
packages/logger/
src/
├── constants.ts # Константы и настройки по умолчанию
├── format.ts # Форматирование логов
├── index.ts # Основной экспорт
├── integrations/ # Интеграции с другими платформами
├── logger.ts # Основная реализация логгера
├── types.ts # Типы и интерфейсы
└── utils.ts # Вспомогательные функцииВклад в развитие проекта
Мы приветствуем вклад сообщества! Подробнее о процессе разработки и правилах можно узнать в CONTRIBUTING.md.
Лицензия
MIT
