Dev Highlights

Доки по разработке

This project is maintained by teniryte

4. Инструментация, Web Vitals и наблюдаемость

Ваш проект включает примеры:

Здесь собраны принципы и паттерны, как «засунуть весь проект под микроскоп»: логирование, метрики, обработка ошибок.

4.1. Файл instrumentation.ts: глобовые хуки приложения

В App Router Next.js позволяет описывать глобовую инструментaцию в файле:

Ключевые элементы:

Паттерн register:

export async function register() {
  console.log('Instrumentation initialized');

  // Пример: выполняем код только на Node.js‑рантайме
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    const os = await import('os');
    console.log('hostname', os.hostname());
  }

  // Здесь можно условно подключить разные модули в зависимости от рантайма
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    // await import('./instrumentation-node');
  }

  if (process.env.NEXT_RUNTIME === 'edge') {
    // await import('./instrumentation-edge');
  }
}

Идеи:

4.2. Глобовая обработка ошибок запросов: onRequestError

В instrumentation.ts также определяется:

export const onRequestError = async (err: any, request: any, context: any) => {
  console.log('ERROR', err, request, context);
};

Принцип: этот хук позволяет:

Типичный практический вариант:

export async function onRequestError(
  error: unknown,
  request: Request,
  context: { route?: string },
) {
  // Отправляем ошибку в систему мониторинга
  // e.g. Sentry.captureException(error, { extra: { url: request.url } });
  console.error('Request error:', error, 'URL:', request.url, 'Context:', context);
}

4.3. Клиентская инструментaция: instrumentation-client.ts

Файл src/instrumentation-client.ts — место для:

Простейший пример (как в проекте):

console.log('Instrumentation client initialized');

Что обычно делают здесь на практике:

import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  tracesSampleRate: 1.0,
});

4.4. Web Vitals: измерение качества фронтенда

Компонент WebVitals в вашем src/app/_components/WebVitals.tsx использует:

Паттерн:

'use client';

import { useReportWebVitals } from 'next/web-vitals';

function logWebVitals(metric: any) {
  console.log('METRIC', metric);
  // здесь можно отправить метрику на свой backend или в аналитический сервис
}

export function WebVitals() {
  useReportWebVitals(logWebVitals);
  return null;
}

Использование в RootLayout:

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <WebVitals />
        {children}
      </body>
    </html>
  );
}

Какие метрики приходят:

Паттерн отправки метрик на сервер:

function logWebVitals(metric: any) {
  // Пример отправки на backend
  navigator.sendBeacon(
    '/analytics',
    JSON.stringify({
      name: metric.name,
      value: metric.value,
      id: metric.id,
      label: metric.label,
    }),
  );
}

4.5. Страница‑демо 15-instrumentation

Отдельная страница‑маршрут (в вашем коде она возвращает, например, 25-instrumentation/15-instrumentation) служит:

Типичный вид:

export default function InstrumentationDemoPage() {
  return <div>Instrumentation demo page</div>;
}

Можно добавить на неё:

4.6. Общие принципы построения наблюдаемости

На основе ваших примеров можно сформулировать базовый чек‑лист:

4.7. Пример «полной» связки

Ниже — пример того, как можно объединить всё вместе:

// src/instrumentation.ts
import * as Sentry from '@sentry/nextjs';

export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    Sentry.init({
      dsn: process.env.SENTRY_DSN,
      tracesSampleRate: 1.0,
    });
  }
}

export async function onRequestError(
  error: unknown,
  request: Request,
  context: { route?: string },
) {
  Sentry.captureException(error, {
    extra: { url: request.url, route: context.route },
  });
}
// src/instrumentation-client.ts
import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  tracesSampleRate: 1.0,
});
// src/app/_components/WebVitals.tsx
'use client';

import { useReportWebVitals } from 'next/web-vitals';

export function WebVitals() {
  useReportWebVitals((metric) => {
    // Отправить на backend или в Sentry
    console.log('METRIC', metric);
  });

  return null;
}

Этот модуль в вашем «конспекте» отвечает за всё, что связано с наблюдаемостью: как увидеть, что происходит с приложением в продакшене, и как находить проблемы ещё до того, как их заметят пользователи.