Опция onRequest: логирование запросов

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

Наиболее часто данная возможность применяется совместно с методом serve(), когда Esbuild выступает в роли локального веб-сервера.

Логирование запросов помогает решать несколько задач:

  • контроль обращений к ресурсам;
  • анализ загружаемых файлов;
  • диагностика проблем маршрутизации;
  • отслеживание ошибок HTTP;
  • сбор метрик производительности;
  • аудит сетевой активности приложения.

Общая идея работы

При поступлении запроса Esbuild формирует объект с информацией о событии и передаёт его в обработчик onRequest.

Упрощённая схема выглядит следующим образом:

  1. Браузер отправляет запрос.
  2. Встроенный сервер Esbuild принимает запрос.
  3. Вызывается обработчик onRequest.
  4. В обработчике выполняется логирование или другая логика.
  5. Ответ возвращается клиенту.

Таким образом появляется возможность наблюдать все обращения к серверу в реальном времени.


Базовый пример

import * as esbuild from 'esbuild';

await esbuild.serve(
  {
    servedir: 'public',
    onRequest(args) {
      console.log(args);
    }
  },
  {}
);

При каждом запросе в консоль будет выводиться объект с информацией о запросе.

Пример результата:

{
  method: 'GET',
  path: '/app.js',
  status: 200,
  remoteAddress: '127.0.0.1',
  timeInMS: 3
}

Структура объекта args

В обработчик передаётся объект с набором полезных свойств.

method

HTTP-метод запроса.

onRequest(args) {
  console.log(args.method);
}

Возможные значения:

GET
POST
PUT
DELETE
PATCH
OPTIONS
HEAD

Пример вывода:

GET

path

Путь к запрашиваемому ресурсу.

onRequest(args) {
  console.log(args.path);
}

Результат:

/index.html

или

/assets/main.js

Это позволяет определить, какие файлы наиболее активно используются приложением.


status

HTTP-статус ответа.

onRequest(args) {
  console.log(args.status);
}

Возможные значения:

200
301
302
404
500

Пример:

404

Логирование кодов ответа является одним из наиболее полезных сценариев применения onRequest.


remoteAddress

IP-адрес клиента, отправившего запрос.

onRequest(args) {
  console.log(args.remoteAddress);
}

Пример:

127.0.0.1

или

::1

При локальной разработке обычно отображается адрес текущей машины.


timeInMS

Время обработки запроса в миллисекундах.

onRequest(args) {
  console.log(args.timeInMS);
}

Пример:

4

Это значение позволяет оценивать скорость отдачи различных ресурсов.


Простое логирование запросов

Наиболее распространённый вариант использования выглядит следующим образом:

await esbuild.serve({
  servedir: 'public',
  onRequest(args) {
    console.log(
      `[${args.method}] ${args.path} ${args.status}`
    );
  }
});

Вывод:

[GET] /index.html 200
[GET] /app.js 200
[GET] /logo.svg 200

Такой формат значительно удобнее стандартного вывода объекта целиком.


Добавление временной метки

Часто требуется фиксировать время выполнения запроса.

await esbuild.serve({
  servedir: 'public',
  onRequest(args) {
    const timestamp = new Date().toISOString();

    console.log(
      `${timestamp} ${args.method} ${args.path}`
    );
  }
});

Результат:

2025-01-10T14:22:03.123Z GET /app.js
2025-01-10T14:22:03.126Z GET /style.css

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


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

Для улучшения читаемости можно выделять разные типы запросов цветом.

await esbuild.serve({
  servedir: 'public',
  onRequest(args) {
    const color =
      args.status >= 400
        ? '\x1b[31m'
        : '\x1b[32m';

    console.log(
      `${color}${args.method} ${args.path} ${args.status}\x1b[0m`
    );
  }
});

Зелёным цветом отображаются успешные ответы, красным — ошибки.


Логирование только ошибок

При большом количестве запросов полезно выводить только проблемные обращения.

await esbuild.serve({
  servedir: 'public',
  onRequest(args) {
    if (args.status >= 400) {
      console.error(
        `${args.status}: ${args.path}`
      );
    }
  }
});

Пример:

404: /missing.js
404: /favicon.ico

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


Отслеживание медленных запросов

Иногда необходимо обнаружить ресурсы, которые загружаются слишком долго.

await esbuild.serve({
  servedir: 'public',
  onRequest(args) {
    if (args.timeInMS > 100) {
      console.warn(
        `Slow request: ${args.path} (${args.timeInMS} ms)`
      );
    }
  }
});

Пример:

Slow request: /large-image.png (156 ms)

Подобная техника помогает выявлять проблемы производительности.


Сбор статистики запросов

Обработчик может использоваться для накопления статистики.

const stats = {
  requests: 0
};

await esbuild.serve({
  servedir: 'public',
  onRequest(args) {
    stats.requests++;

    console.log(
      `Total requests: ${stats.requests}`
    );
  }
});

Вывод:

Total requests: 1
Total requests: 2
Total requests: 3

Подсчёт запросов по типам файлов

Часто требуется определить, какие ресурсы используются чаще всего.

const counters = {
  js: 0,
  css: 0,
  html: 0
};

await esbuild.serve({
  servedir: 'public',
  onRequest(args) {
    if (args.path.endsWith('.js')) {
      counters.js++;
    }

    if (args.path.endsWith('.css')) {
      counters.css++;
    }

    if (args.path.endsWith('.html')) {
      counters.html++;
    }

    console.log(counters);
  }
});

Возможный результат:

{
  js: 12,
  css: 4,
  html: 1
}

Формирование собственного лог-формата

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

Пример создания структурированного сообщения:

await esbuild.serve({
  servedir: 'public',
  onRequest(args) {
    const log = {
      method: args.method,
      path: args.path,
      status: args.status,
      duration: args.timeInMS
    };

    console.log(JSON.stringify(log));
  }
});

Вывод:

{
  "method":"GET",
  "path":"/app.js",
  "status":200,
  "duration":2
}

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


Логирование в файл

Вместо консоли информация может записываться в журнал.

import fs from 'fs';

await esbuild.serve({
  servedir: 'public',
  onRequest(args) {
    const line =
      `${args.method} ${args.path} ${args.status}\n`;

    fs.appendFileSync('server.log', line);
  }
});

Содержимое файла:

GET /index.html 200
GET /app.js 200
GET /image.png 200

Подобный подход полезен при длительном тестировании приложения.


Мониторинг ошибок 404

Одной из самых частых задач является отслеживание отсутствующих файлов.

await esbuild.serve({
  servedir: 'public',
  onRequest(args) {
    if (args.status === 404) {
      console.error(
        `Missing file: ${args.path}`
      );
    }
  }
});

Вывод:

Missing file: /old-script.js
Missing file: /assets/logo-old.svg

Это помогает поддерживать актуальность ссылок и импортов.


Анализ активности клиента

С помощью onRequest можно увидеть полный набор ресурсов, запрашиваемых браузером.

await esbuild.serve({
  servedir: 'public',
  onRequest(args) {
    console.log(args.path);
  }
});

Например:

/
/index.css
/app.js
/fonts/main.woff2
/logo.svg

По журналу легко определить порядок загрузки файлов приложения.


Использование совместно с системой мониторинга

Обработчик способен отправлять данные во внешние сервисы.

await esbuild.serve({
  servedir: 'public',
  onRequest(args) {
    sendMetric({
      path: args.path,
      status: args.status,
      duration: args.timeInMS
    });
  }
});

Подобная интеграция позволяет строить графики:

  • количества запросов;
  • среднего времени ответа;
  • числа ошибок;
  • наиболее популярных ресурсов.

Ограничения onRequest

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

Не предназначен для изменения ответа

onRequest ориентирован на наблюдение и логирование.

Он не используется для:

  • модификации содержимого ответа;
  • изменения заголовков;
  • переписывания маршрутов;
  • реализации middleware.

Для подобных задач обычно применяются полноценные HTTP-серверы, например Node.js с Express, Fastify или Koa.


Используется преимущественно в режиме разработки

Встроенный сервер Esbuild создавался прежде всего для локальной разработки и тестирования.

Поэтому onRequest чаще всего применяется для:

  • диагностики;
  • отладки;
  • анализа сетевой активности;
  • контроля работы сборки.

Вызывается для каждого запроса

При большом количестве ресурсов обработчик может вызываться сотни раз за короткий промежуток времени.

Например:

GET /index.html
GET /app.js
GET /runtime.js
GET /vendor.js
GET /logo.svg
GET /fonts/main.woff2
GET /styles.css

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


Практический пример комплексного логирования

await esbuild.serve({
  servedir: 'public',

  onRequest(args) {
    const date = new Date().toISOString();

    const message =
      `[${date}] ` +
      `${args.remoteAddress} ` +
      `${args.method} ` +
      `${args.path} ` +
      `${args.status} ` +
      `${args.timeInMS}ms`;

    console.log(message);

    if (args.status >= 400) {
      console.error(
        `Request failed: ${args.path}`
      );
    }

    if (args.timeInMS > 100) {
      console.warn(
        `Slow request detected: ${args.path}`
      );
    }
  }
});

Пример результата:

[2025-01-10T14:40:02.222Z] 127.0.0.1 GET /index.html 200 2ms
[2025-01-10T14:40:02.225Z] 127.0.0.1 GET /app.js 200 4ms
[2025-01-10T14:40:02.228Z] 127.0.0.1 GET /missing.js 404 1ms
Request failed: /missing.js

Такой подход обеспечивает полноценное журналирование, позволяет контролировать ошибки, анализировать производительность и получать подробную информацию о работе локального сервера Esbuild через единую точку наблюдения — обработчик onRequest.