Логирование в плагинах

Логирование в экосистеме Remark и Rehype играет ключевую роль при разработке и отладке плагинов. Благодаря корректной организации логов можно отслеживать процесс обработки AST (Abstract Syntax Tree), выявлять ошибки трансформации и понимать, как плагин взаимодействует с данными. Remark и Rehype используют унифицированную структуру AST, что позволяет создавать единые подходы к логированию как для Markdown, так и для HTML.


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

В плагинах Remark и Rehype чаще всего используют встроенный механизм через объект vfile. VFile представляет собой виртуальный файл и содержит полезные свойства для работы с логами: message, info, fatal. Каждое сообщение в vfile имеет уровень важности и метку позиции, что позволяет точно локализовать проблему в исходном файле.

Пример базового использования:

import { visit } from 'unist-util-visit';

function remarkPluginExample() {
  return (tree, file) => {
    visit(tree, 'text', (node) => {
      if (node.value.includes('TODO')) {
        file.message('Найден TODO в тексте', node);
      }
    });
  };
}
  • file.message(text, node) — создает обычное информационное сообщение.
  • file.warn(text, node) — лог уровня предупреждения.
  • file.fail(text, node) — лог уровня ошибки, прерывающий процесс обработки.

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


Структура сообщений

Сообщения VFile могут включать следующие поля:

  • reason — описание проблемы.
  • line, column — позиция в исходном файле.
  • source — имя плагина или модуля, который сгенерировал сообщение.
  • fatal — булево значение, указывающее, что это критическая ошибка.

Например:

file.message('Некорректное использование ссылки', node, { source: 'remark-my-plugin' });

Это создаст сообщение с меткой источника, которое может быть удобно обработано в консоли или в лог-системе CI/CD.


Логирование в асинхронных плагинах

Для асинхронных плагинов важно учитывать, что сообщения VFile могут создаваться после завершения основной обработки дерева. Поэтому нужно использовать асинхронные функции с корректной передачей VFile:

export default async function remarkAsyncPlugin() {
  return async (tree, file) => {
    await visit(tree, 'link', async (node) => {
      const isValid = await validateUrl(node.url);
      if (!isValid) {
        file.message(`Недопустимый URL: ${node.url}`, node);
      }
    });
  };
}

Асинхронное логирование не блокирует процесс обработки и позволяет интегрировать внешние проверки, например, запросы к API или проверку существования ресурсов.


Пользовательские уровни логирования

Для расширенной отладки можно внедрять собственные уровни логирования. Например, можно использовать объект file.data для хранения структурированных логов:

function logPlugin(tree, file) {
  if (!file.data.logs) file.data.logs = [];

  visit(tree, 'heading', (node) => {
    file.data.logs.push({ type: 'heading', value: node.value });
  });
}

Такой подход позволяет:

  • Собирать логи по категориям.
  • Позволяет интегрировать их с внешними системами аналитики.
  • Избегать прямого вывода в консоль, сохраняя чистоту обработки VFile.

Взаимодействие с консолью и внешними логерами

Для более сложных плагинов часто используется внешнее логирование с console, debug или библиотеками вроде pino или winston. Однако в контексте Remark/Rehype рекомендуется синхронизировать их с VFile, чтобы сообщения оставались привязанными к позициям исходного текста.

Пример интеграции:

import debug from 'debug';
const log = debug('remark:plugin');

function debugPlugin(tree, file) {
  visit(tree, 'paragraph', (node) => {
    log('Обработан абзац: %O', node);
    file.message('Обработан абзац', node);
  });
}

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


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

  1. Привязка к исходной позиции: всегда передавать узел AST при генерации сообщений.
  2. Использование уровней: разделять message, warn и fail для различной критичности.
  3. Асинхронная поддержка: обрабатывать внешние проверки корректно, не блокируя AST.
  4. Структурированные данные: хранить логи в file.data для последующего анализа.
  5. Интеграция с внешними логерами: использовать только для дополнительного мониторинга, основная диагностика должна оставаться через VFile.

Итоговая схема работы логирования

  1. Плагин обходит AST с помощью unist-util-visit.
  2. На каждом узле выполняется проверка условий.
  3. При необходимости создается сообщение через file.message, file.warn или file.fail.
  4. Логи могут сохраняться в file.data или выводиться во внешние системы.
  5. Все сообщения привязаны к исходной позиции, обеспечивая точную диагностику.

Такой подход обеспечивает прозрачность работы плагинов, облегчает отладку и позволяет создавать расширяемые и надежные решения для обработки Markdown и HTML с помощью Remark и Rehype.