Мониторинг TypeScript приложений: метрики и алерты от А до Я

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему мониторинг критичен для TypeScript-приложений

TypeScript — мощный инструмент для создания масштабируемых веб-приложений, но даже самый качественный код не застрахован от сбоев в продакшене. Без системы мониторинга вы рискуете узнавать о проблемах от пользователей, а не от автоматических алертов. В этой статье мы разберем полный цикл настройки мониторинга: от сбора метрик до отправки уведомлений.

Мы рассмотрим три ключевых столпа observability: метрики (числовые показатели системы), логи (детальные записи событий) и трейсинг (отслеживание запросов). Особое внимание уделим интеграции с популярными инструментами — Prometheus, Grafana и Sentry.

1. Основные метрики для TypeScript-приложений

Прежде чем настраивать алерты, нужно определить, какие метрики критичны для вашего приложения. Разделим их на три группы:

1.1 Бизнес-метрики

Отражают здоровье приложения с точки зрения пользователя:

  • Количество запросов в секунду (RPS) — показывает нагрузку на сервер
  • Время ответа (latency) — p50, p95, p99 перцентили
  • Количество ошибок — 4xx и 5xx статусы
  • Активные пользователи — сессии, коннекты к WebSocket

1.2 Технические метрики

Показывают состояние инфраструктуры:

  • CPU и Memory — загрузка процессора и утечки памяти
  • Event Loop Lag — задержки в цикле событий Node.js
  • Garbage Collection — частота и длительность сборки мусора
  • HTTP connection pool — количество открытых соединений

1.3 Пример: сбор метрик с Prometheus в TypeScript

Установим библиотеку prom-client для Node.js:

npm install prom-client @types/prom-client

Создадим файл metrics.ts:

import client from 'prom-client';
import express, { Request, Response } from 'express';

// Создаем регистр метрик
const register = new client.Registry();
client.collectDefaultMetrics({ register });

// Кастомная метрика: количество HTTP запросов
const httpRequestCounter = new client.Counter({
  name: 'http_requests_total',
  help: 'Total number of HTTP requests',
  labelNames: ['method', 'route', 'status_code'],
  registers: [register],
});

// Гистограмма времени ответа
const httpRequestDuration = new client.Histogram({
  name: 'http_request_duration_seconds',
  help: 'Duration of HTTP requests in seconds',
  labelNames: ['method', 'route', 'status_code'],
  buckets: [0.1, 0.5, 1, 2, 5],
  registers: [register],
});

// Middleware для сбора метрик
export function metricsMiddleware(req: Request, res: Response, next: Function) {
  const end = httpRequestDuration.startTimer();
  res.on('finish', () => {
    const labels = {
      method: req.method,
      route: req.route?.path || 'unknown',
      status_code: res.statusCode.toString(),
    };
    httpRequestCounter.inc(labels);
    end(labels);
  });
  next();
}

// Endpoint для Prometheus
export function metricsEndpoint(req: Request, res: Response) {
  res.set('Content-Type', register.contentType);
  res.send(register.metrics());
}

2. Настройка алертов: от теории к практике

Алерты должны быть умными — слишком много ложных срабатываний утомляет команду. Используем правило "трех сигм" для аномалий и пороговые значения для критических метрик.

2.1 Правила алертов в Prometheus

Создайте файл alerts.yml:

groups:
  - name: typescript-app
    rules:
      - alert: HighErrorRate
        expr: |
          rate(http_requests_total{status_code=~"5.."}[5m]) 
          / 
          rate(http_requests_total[5m]) > 0.05
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "High error rate ({{ $value | humanizePercentage }})"
          description: "Error rate above 5% for 2 minutes"

      - alert: HighLatency
        expr: |
          histogram_quantile(0.95, 
            rate(http_request_duration_seconds_bucket[5m])
          ) > 2
        for: 3m
        labels:
          severity: warning
        annotations:
          summary: "High p95 latency ({{ $value }}s)"
          description: "95th percentile latency above 2 seconds"

2.2 Интеграция с Telegram через Webhook

Напишем простой сервис для отправки алертов в Telegram:

import axios from 'axios';

interface AlertPayload {
  status: string;
  labels: Record;
  annotations: Record;
}

class AlertManager {
  private telegramToken: string;
  private chatId: string;

  constructor(token: string, chatId: string) {
    this.telegramToken = token;
    this.chatId = chatId;
  }

  async sendAlert(alert: AlertPayload): Promise<void> {
    const message = `
🚨 *${alert.status.toUpperCase()}*: ${alert.labels.alertname}
📊 Severity: ${alert.labels.severity}
📝 ${alert.annotations.description}
    `;

    await axios.post(
      `https://api.telegram.org/bot${this.telegramToken}/sendMessage`,
      {
        chat_id: this.chatId,
        text: message,
        parse_mode: 'Markdown',
      }
    );
  }
}

// Использование
const alertManager = new AlertManager(
  process.env.TELEGRAM_BOT_TOKEN!,
  process.env.TELEGRAM_CHAT_ID!
);

3. Логирование и трейсинг в TypeScript

Метрики показывают "что" произошло, логи — "почему". Для структурированного логирования используем pino или winston.

3.1 Структурированное логирование с pino

import pino from 'pino';

const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  transport: {
    target: 'pino-pretty',
    options: {
      colorize: true,
      translateTime: 'SYS:standard',
    },
  },
  redact: ['req.headers.authorization', 'req.body.password'],
});

// Middleware для логирования запросов
export function requestLogger(req: any, res: any, next: Function) {
  const start = Date.now();
  res.on('finish', () => {
    logger.info                

Блоги

Рекомендаций по книгам