Централизованное логирование

Централизованное логирование является ключевым аспектом при построении масштабируемых приложений на Fresh. Оно позволяет отслеживать работу серверных обработчиков, состояние компонентов, ошибки и производительность приложения в единой системе, упрощая анализ и отладку. Fresh, будучи современным фреймворком на основе Deno, предоставляет гибкие возможности интеграции логирования через стандартные средства JavaScript и дополнительные библиотеки.


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

  1. Единообразие формата логов Логи должны иметь структурированный формат: timestamp, уровень (info, warn, error, debug), источник события (middleware, route handler, component), сообщение. Это упрощает фильтрацию и агрегацию данных в централизованных системах вроде Loki, Elasticsearch или Grafana.

  2. Разделение уровней логирования

    • debug — подробная информация для разработчиков, выводится только в локальной среде.
    • info — ключевые события работы приложения: успешные запросы, статус обработки данных.
    • warn — потенциальные проблемы, которые не нарушают работу приложения.
    • error — критические ошибки, исключения, которые требуют вмешательства.
  3. Асинхронность логирования В Fresh серверные обработчики часто работают с асинхронными функциями. Логи должны записываться без блокировки основного потока, чтобы не влиять на производительность. Используются асинхронные функции записи в файл или отправки данных в удалённые хранилища.


Интеграция стандартного логирования

Fresh основан на Deno, поэтому можно использовать встроенный модуль std/log:

import * as log from "https://deno.land/std@0.201.0/log/mod.ts";

await log.setup({
  handlers: {
    console: new log.handlers.ConsoleHandler("DEBUG", {
      formatter: "{datetime} [{levelName}] {msg}",
    }),
    file: new log.handlers.FileHandler("INFO", {
      filename: "./logs/app.log",
      formatter: "{datetime} [{levelName}] {msg}",
    }),
  },
  loggers: {
    default: {
      level: "DEBUG",
      handlers: ["console", "file"],
    },
  },
});

const logger = log.getLogger();
logger.info("Сервер запущен");
  • ConsoleHandler — вывод в консоль для отладки в локальной среде.
  • FileHandler — сохранение логов в файл для последующего анализа или отправки в централизованное хранилище.
  • Formatter — настраиваемый формат вывода, поддерживающий дату, уровень, сообщение и дополнительные поля.

Логирование запросов в Fresh

Для контроля запросов и отклика полезно внедрять middleware, которое перехватывает все входящие HTTP-запросы:

import { MiddlewareHandlerContext } from "$fresh/server.ts";

export const logRequests = async (req: Request, ctx: MiddlewareHandlerContext) => {
  const start = performance.now();
  const response = await ctx.next();
  const duration = (performance.now() - start).toFixed(2);

  logger.info(`${req.method} ${req.url} - ${response.status} (${duration}ms)`);

  return response;
};

Особенности данного подхода:

  • Замер времени обработки позволяет выявлять узкие места в производительности.
  • Структурированные логи по запросам упрощают последующую агрегацию и визуализацию.

Middleware можно подключать глобально в main.ts, чтобы каждое событие HTTP автоматически логировалось.


Логирование ошибок и исключений

Fresh поддерживает обработку ошибок на уровне серверного рендеринга и API-роутеров. Для централизованного логирования ошибок рекомендуется создавать глобальный обработчик:

import { ErrorHandler } from "$fresh/server.ts";

export const errorHandler: ErrorHandler = (err, ctx) => {
  logger.error(`Ошибка в ${ctx.url}: ${err.message}`);
  return new Response("Произошла ошибка на сервере", { status: 500 });
};
  • Использование единого обработчика предотвращает дублирование кода.
  • Логи ошибок содержат контекст запроса, что облегчает диагностику.

Интеграция с внешними системами

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

  • Elasticsearch / Kibana — анализ, фильтрация, визуализация логов.
  • Grafana Loki — хранение и поиск логов с минимальными задержками.
  • Sentry — мониторинг ошибок и их группировка по стеку вызовов.

Пример отправки логов через HTTP:

async function sendLogToRemote(logEntry: Record<string, unknown>) {
  await fetch("https://logging.example.com/ingest", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(logEntry),
  });
}

logger.info = async (msg: string) => {
  const logEntry = { timestamp: new Date().toISOString(), level: "INFO", message: msg };
  console.info(msg);
  await sendLogToRemote(logEntry);
};

Рекомендации по организации логов в Fresh

  1. Использовать структурированные JSON-логи — упрощает интеграцию с внешними системами.
  2. Разделять логи по модулям — отдельные логгеры для API, middleware и сервисов.
  3. Сохранять логи в нескольких местах — локально для отладки и удалённо для анализа.
  4. Не блокировать основной поток — асинхронная запись критична для производительности.
  5. Регулярно проверять формат и корректность логов — чтобы централизованные системы корректно обрабатывали данные.

Централизованное логирование в Fresh обеспечивает прозрачность работы приложения, позволяет быстро выявлять ошибки и оптимизировать производительность. Правильная организация логов и интеграция с внешними системами значительно упрощает сопровождение крупных проектов.