Logging для отладки

Во фреймворке Fresh логирование является ключевым инструментом для отслеживания поведения приложений на этапе разработки и диагностики ошибок в продакшене. Fresh, основанный на Deno и использовании стандартов современного JavaScript/TypeScript, предоставляет гибкие возможности для ведения логов.

Основы логирования

В Fresh логирование реализуется с помощью стандартного объекта console (console.log, console.error, console.warn, console.info, console.debug) и специализированных библиотек, совместимых с Deno, таких как std/log. Эти инструменты позволяют различать уровни логирования и направлять вывод в разные потоки, например, stdout или файлы.

import { log } from "https://deno.land/std@0.203.0/log/mod.ts";

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

const logger = log.getLogger();
logger.info("Приложение запущено");
logger.debug("Отладочная информация");
logger.error("Произошла ошибка");

В этом примере показано, как настроить разные обработчики логов, чтобы отдельные сообщения выводились в консоль, а более важные — сохранялись в файл.

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

Fresh и Deno поддерживают следующие стандартные уровни логов:

  • DEBUG — подробная информация для разработки.
  • INFO — базовая информация о событиях приложения.
  • WARNING — предупреждения о возможных проблемах.
  • ERROR — критические ошибки, требующие внимания.
  • CRITICAL — ошибки, которые могут привести к остановке приложения.

Использование уровней позволяет фильтровать сообщения и сосредоточиться на наиболее значимых событиях.

Логирование в обработчиках и маршрутах

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

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

export async function handler(req: Request, ctx: HandlerContext) {
  const start = performance.now();
  try {
    const resp = await ctx.next();
    const duration = (performance.now() - start).toFixed(2);
    console.info(`[${req.method}] ${req.url} - ${resp.status} (${duration}ms)`);
    return resp;
  } catch (err) {
    console.error(`Ошибка обработки запроса ${req.url}:`, err);
    throw err;
  }
}

Такой подход помогает отслеживать производительность каждого запроса и выявлять узкие места в приложении.

Логирование состояния приложения

Для сложных приложений важно логировать внутреннее состояние, например, данные, получаемые из базы, или результаты вычислений:

const users = await db.query("SELECT * FROM users");
console.debug("Полученные пользователи:", users);

Использование console.debug позволяет выводить объемные структуры данных только на этапе разработки, без загрязнения логов продакшена.

Форматирование и структурированные логи

Для улучшения читаемости и последующего анализа рекомендуется использовать структурированные логи в формате JSON:

console.log(JSON.stringify({
  level: "INFO",
  message: "Пользователь авторизовался",
  userId: 123,
  timestamp: new Date().toISOString()
}));

Структурированные логи удобно парсить внешними инструментами мониторинга и визуализации.

Настройка логирования в продакшене

В продакшене важно отключать детальное логирование и оставлять только критические сообщения. Можно использовать переменные окружения для контроля уровня логов:

const LOG_LEVEL = Deno.env.get("LOG_LEVEL") || "INFO";

if (LOG_LEVEL === "DEBUG") {
  console.debug("Подробная отладочная информация");
}
console.info("Основные события приложения");

Такой подход обеспечивает баланс между информативностью логов и производительностью.

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

Fresh позволяет интегрировать логирование с внешними системами мониторинга, например, Loki, ELK или Sentry. Для этого структурированные JSON-логи отправляются через HTTP или TCP:

const logData = JSON.stringify({
  level: "ERROR",
  message: "Не удалось подключиться к базе",
  timestamp: new Date().toISOString(),
});

await fetch("https://loki.example.com/loki/api/v1/push", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: logData,
});

Такой подход обеспечивает централизованное хранение и анализ логов большого приложения.

Практические рекомендации

  • Использовать разные уровни логирования для разработки и продакшена.
  • Логи критических ошибок должны сохраняться и отправляться в централизованную систему.
  • Для объемных структур данных применять JSON или форматированные строки.
  • Логирование запросов и ответов помогает выявлять узкие места и ошибки на ранних стадиях.
  • Настраивать обработчики логов отдельно для консоли и файлов, чтобы не перегружать основной вывод.

Logging в Fresh — это не просто вывод сообщений, а важный инструмент для отладки, мониторинга и поддержки приложения на всех этапах жизненного цикла.