Введение
Мониторинг и логирование API критически важны для поддержания работоспособности и производительности системы. Без правильного мониторинга невозможно понять, что происходит с API в production, выявить проблемы до того, как они затронут пользователей, и оптимизировать производительность.
В этом руководстве мы рассмотрим различные аспекты мониторинга и логирования API: health check endpoints, сбор метрик (latency, error rate, throughput), логирование запросов, популярные инструменты (Prometheus, Grafana, Datadog) и настройку alerting.
✅ Что вы узнаете:
- ✅ Создание health check endpoints
- ✅ Сбор метрик (latency, error rate, throughput)
- ✅ Логирование запросов и ответов
- ✅ Настройка Prometheus и Grafana
- ✅ Использование Datadog для мониторинга
- ✅ Настройка alerting для критических событий
- ✅ Best practices для мониторинга
- ✅ Анализ и интерпретация метрик
💡 Зачем нужен мониторинг API?
Мониторинг позволяет выявлять проблемы до того, как они затронут пользователей, оптимизировать производительность, понимать использование API и быстро реагировать на инциденты. Хороший мониторинг — это глаза и уши вашего API.
📋 Содержание
1. Health Check Endpoints 💚
Health check endpoints используются для проверки состояния API. Они должны быстро отвечать и проверять ключевые зависимости (база данных, кэш, внешние сервисы).
Базовый health check
// Express.js health check
app.get('/health', (req, res) => {
res.status(200).json({
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
// Более детальный health check
app.get('/health', async (req, res) => {
const health = {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
checks: {}
};
// Проверка базы данных
try {
await db.query('SELECT 1');
health.checks.database = 'ok';
} catch (error) {
health.checks.database = 'error';
health.status = 'degraded';
}
// Проверка Redis
try {
await redis.ping();
health.checks.redis = 'ok';
} catch (error) {
health.checks.redis = 'error';
health.status = 'degraded';
}
// Проверка внешнего сервиса
try {
await externalService.check();
health.checks.externalService = 'ok';
} catch (error) {
health.checks.externalService = 'error';
health.status = 'degraded';
}
const statusCode = health.status === 'ok' ? 200 : 503;
res.status(statusCode).json(health);
});
Readiness и Liveness проверки
// Liveness probe - приложение работает?
app.get('/health/live', (req, res) => {
res.status(200).json({ status: 'alive' });
});
// Readiness probe - приложение готово принимать трафик?
app.get('/health/ready', async (req, res) => {
const checks = {
database: false,
redis: false,
migrations: false
};
try {
// Проверка БД
await db.query('SELECT 1');
checks.database = true;
// Проверка Redis
await redis.ping();
checks.redis = true;
// Проверка миграций
const migrations = await db.checkMigrations();
checks.migrations = migrations.isUpToDate;
const ready = Object.values(checks).every(check => check === true);
if (ready) {
res.status(200).json({ status: 'ready', checks });
} else {
res.status(503).json({ status: 'not ready', checks });
}
} catch (error) {
res.status(503).json({
status: 'not ready',
error: error.message
});
}
});
// Startup probe - приложение запустилось?
app.get('/health/startup', (req, res) => {
if (app.get('started')) {
res.status(200).json({ status: 'started' });
} else {
res.status(503).json({ status: 'starting' });
}
});
✅ Использование health checks:
- Kubernetes: Liveness и Readiness probes
- Load Balancers: Проверка доступности сервисов
- Monitoring: Автоматические проверки состояния
- CI/CD: Проверка после деплоя
2. Сбор метрик 📈
Метрики — это количественные измерения состояния системы. Ключевые метрики для API: latency (задержка), error rate (частота ошибок), throughput (пропускная способность).
Основные метрики API
| Метрика | Описание | Целевое значение | Как измерять |
|---|---|---|---|
| Latency | Время ответа API | < 200ms (p50), < 500ms (p95) | Время от запроса до ответа |
| Error Rate | Процент ошибок | < 0.1% | Количество ошибок / общее количество запросов |
| Throughput | Запросов в секунду | Зависит от нагрузки | RPS (Requests Per Second) |
| CPU Usage | Использование CPU | < 70% | Процент использования процессора |
| Memory Usage | Использование памяти | < 80% | Процент использования памяти |
| Database Query Time | Время запросов к БД | < 100ms | Среднее время выполнения запросов |
Реализация сбора метрик в Node.js
// Простой сбор метрик
class MetricsCollector {
constructor() {
this.metrics = {
requests: {
total: 0,
errors: 0,
byMethod: {},
byEndpoint: {}
},
latency: [],
timestamps: []
};
}
recordRequest(method, endpoint, statusCode, latency) {
this.metrics.requests.total++;
this.metrics.latency.push(latency);
this.metrics.timestamps.push(Date.now());
if (statusCode >= 400) {
this.metrics.requests.errors++;
}
// Группировка по методу
if (!this.metrics.requests.byMethod[method]) {
this.metrics.requests.byMethod[method] = 0;
}
this.metrics.requests.byMethod[method]++;
// Группировка по endpoint
if (!this.metrics.requests.byEndpoint[endpoint]) {
this.metrics.requests.byEndpoint[endpoint] = 0;
}
this.metrics.requests.byEndpoint[endpoint]++;
// Храним только последние 1000 записей
if (this.metrics.latency.length > 1000) {
this.metrics.latency.shift();
this.metrics.timestamps.shift();
}
}
getMetrics() {
const errorRate = this.metrics.requests.total > 0
? (this.metrics.requests.errors / this.metrics.requests.total) * 100
: 0;
const sortedLatency = [...this.metrics.latency].sort((a, b) => a - b);
const p50 = this.percentile(sortedLatency, 50);
const p95 = this.percentile(sortedLatency, 95);
const p99 = this.percentile(sortedLatency, 99);
const recent = this.metrics.timestamps.filter(
ts => Date.now() - ts < 60000
);
const rps = recent.length / 60;
return {
totalRequests: this.metrics.requests.total,
errorRate: `${errorRate.toFixed(2)}%`,
latency: {
p50: `${p50.toFixed(2)}ms`,
p95: `${p95.toFixed(2)}ms`,
p99: `${p99.toFixed(2)}ms`
},
throughput: `${rps.toFixed(2)} req/s`,
byMethod: this.metrics.requests.byMethod,
byEndpoint: this.metrics.requests.byEndpoint
};
}
percentile(sortedArray, percentile) {
if (sortedArray.length === 0) return 0;
const index = Math.ceil((percentile / 100) * sortedArray.length) - 1;
return sortedArray[index] || 0;
}
reset() {
this.metrics = {
requests: { total: 0, errors: 0, byMethod: {}, byEndpoint: {} },
latency: [],
timestamps: []
};
}
}
const metrics = new MetricsCollector();
// Middleware для сбора метрик
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const latency = Date.now() - start;
metrics.recordRequest(
req.method,
req.path,
res.statusCode,
latency
);
});
next();
});
// Endpoint для получения метрик
app.get('/metrics', (req, res) => {
res.json(metrics.getMetrics());
});
Prometheus формат метрик
// Установка: npm install prom-client
const client = require('prom-client');
// Регистр метрик
const register = new client.Registry();
// Counter для подсчета запросов
const httpRequestsTotal = new client.Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'endpoint', 'status'],
registers: [register]
});
// Histogram для latency
const httpRequestDuration = new client.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'endpoint'],
buckets: [0.1, 0.5, 1, 2, 5],
registers: [register]
});
// Gauge для активных соединений
const activeConnections = new client.Gauge({
name: 'active_connections',
help: 'Number of active connections',
registers: [register]
});
// Middleware для сбора метрик
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
httpRequestsTotal.inc({
method: req.method,
endpoint: req.path,
status: res.statusCode
});
httpRequestDuration.observe(
{ method: req.method, endpoint: req.path },
duration
);
});
next();
});
// Endpoint для Prometheus
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});
// Пример вывода:
// http_requests_total{method="GET",endpoint="/api/users",status="200"} 1234
// http_request_duration_seconds_bucket{method="GET",endpoint="/api/users",le="0.1"} 800
// http_request_duration_seconds_bucket{method="GET",endpoint="/api/users",le="0.5"} 1200
3. Логирование запросов 📝
Логирование позволяет отслеживать все запросы к API, анализировать проблемы и аудировать использование системы.
Базовое логирование
// Winston для логирования
// Установка: npm install winston
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
new winston.transports.Console({
format: winston.format.simple()
})
]
});
// Middleware для логирования запросов
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
logger.info({
method: req.method,
url: req.url,
statusCode: res.statusCode,
duration: `${duration}ms`,
ip: req.ip,
userAgent: req.get('user-agent')
});
if (res.statusCode >= 400) {
logger.error({
method: req.method,
url: req.url,
statusCode: res.statusCode,
error: res.statusMessage,
body: req.body,
query: req.query
});
}
});
next();
});
Структурированное логирование
// Структурированное логирование с контекстом
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: {
service: 'api-service',
environment: process.env.NODE_ENV
},
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'api.log' })
]
});
// Middleware с корреляционным ID
const { v4: uuidv4 } = require('uuid');
app.use((req, res, next) => {
req.id = uuidv4(); // Correlation ID
req.startTime = Date.now();
logger.info({
requestId: req.id,
method: req.method,
path: req.path,
ip: req.ip,
message: 'Incoming request'
});
res.on('finish', () => {
const duration = Date.now() - req.startTime;
logger.info({
requestId: req.id,
method: req.method,
path: req.path,
statusCode: res.statusCode,
duration: `${duration}ms`,
message: 'Request completed'
});
});
next();
});
Логирование с уровнем детализации
// Логирование с разными уровнями
logger.debug('Detailed debug information');
logger.info('General information');
logger.warn('Warning message');
logger.error('Error occurred', { error: error.stack });
// Логирование с контекстом
logger.info('User created', {
userId: user.id,
email: user.email,
timestamp: new Date().toISOString()
});
// Логирование ошибок с контекстом
try {
await db.createUser(userData);
} catch (error) {
logger.error('Failed to create user', {
error: error.message,
stack: error.stack,
userData: userData,
requestId: req.id
});
throw error;
}
4. Prometheus и Grafana 📊
Prometheus — система мониторинга и сбора метрик с временными рядами. Grafana — платформа для визуализации метрик и создания дашбордов.
Настройка Prometheus
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'api-server'
static_configs:
- targets: ['localhost:3000']
metrics_path: '/metrics'
scrape_interval: 5s
Экспорт метрик в Node.js
// Экспорт метрик для Prometheus
const express = require('express');
const client = require('prom-client');
const app = express();
// Создаем регистр
const register = new client.Registry();
// Добавляем стандартные метрики Node.js
client.collectDefaultMetrics({ 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, 10],
registers: [register]
});
const httpRequestsTotal = new client.Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'route', 'status_code'],
registers: [register]
});
// Middleware для метрик
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
const route = req.route ? req.route.path : req.path;
httpRequestDuration.observe(
{
method: req.method,
route: route,
status_code: res.statusCode
},
duration
);
httpRequestsTotal.inc({
method: req.method,
route: route,
status_code: res.statusCode
});
});
next();
});
// Endpoint для Prometheus
app.get('/metrics', async (req, res) => {
try {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
} catch (ex) {
res.status(500).end(ex);
}
});
app.listen(3000);
Grafana дашборд
{
"dashboard": {
"title": "API Metrics Dashboard",
"panels": [
{
"title": "Request Rate",
"targets": [{
"expr": "rate(http_requests_total[5m])",
"legendFormat": "{method} {route}"
}]
},
{
"title": "Request Duration (p95)",
"targets": [{
"expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))",
"legendFormat": "{method} {route}"
}]
},
{
"title": "Error Rate",
"targets": [{
"expr": "rate(http_requests_total{status_code=~\"5..\"}[5m]) / rate(http_requests_total[5m])",
"legendFormat": "Error Rate"
}]
}
]
}
}
✅ Преимущества Prometheus + Grafana:
- Open-source решение
- Мощная система запросов (PromQL)
- Отличная визуализация в Grafana
- Активное сообщество
- Хорошая интеграция с Kubernetes
5. Datadog для мониторинга 🐕
Datadog — комплексная платформа мониторинга, которая предоставляет APM (Application Performance Monitoring), логирование, метрики и трейсинг в одном решении.
Интеграция Datadog
// Установка: npm install dd-trace
const tracer = require('dd-trace').init({
service: 'api-server',
env: process.env.NODE_ENV,
version: '1.0.0'
});
// Автоматическое трейсинг для Express
const express = require('express');
const app = express();
// Datadog APM автоматически трейсит все запросы
app.get('/api/users', async (req, res) => {
// Datadog автоматически отслеживает этот запрос
const users = await db.getUsers();
res.json(users);
});
// Кастомные спаны для детального трейсинга
app.post('/api/users', async (req, res) => {
const span = tracer.startSpan('create_user');
try {
span.setTag('user.email', req.body.email);
const user = await db.createUser(req.body);
span.setTag('user.id', user.id);
res.json(user);
} catch (error) {
span.setTag('error', true);
span.setTag('error.message', error.message);
throw error;
} finally {
span.finish();
}
});
Метрики в Datadog
// Установка: npm install datadog-metrics
const metrics = require('datadog-metrics');
metrics.init({ host: 'api-server', prefix: 'api.' });
// Отправка кастомных метрик
metrics.gauge('users.active', activeUsersCount);
metrics.increment('api.requests');
metrics.histogram('api.response_time', responseTime);
// Счетчик с тегами
metrics.increment('api.requests', 1, ['method:GET', 'endpoint:/api/users']);
// Gauge для мониторинга состояния
metrics.gauge('database.connections', connectionCount);
metrics.gauge('cache.hit_rate', hitRate);
Логирование в Datadog
// Логирование в Datadog
const winston = require('winston');
const { DatadogTransport } = require('winston-datadog-logs');
const logger = winston.createLogger({
transports: [
new DatadogTransport({
apiKey: process.env.DATADOG_API_KEY,
service: 'api-server',
hostname: 'api-server-1',
ddsource: 'nodejs'
})
]
});
logger.info('User created', {
userId: 123,
email: 'user@example.com',
dd: {
trace_id: tracer.currentSpan()?.context().toTraceId(),
span_id: tracer.currentSpan()?.context().toSpanId()
}
});
✅ Преимущества Datadog:
- Комплексное решение (APM + метрики + логи)
- Простая настройка
- Отличная визуализация
- Интеграция с множеством сервисов
- Мощные алерты
❌ Недостатки Datadog:
- Платный сервис
- Может быть дорого для больших объемов
Сравнение инструментов 📊
| Инструмент | Тип | Стоимость | Сложность | Функциональность |
|---|---|---|---|---|
| Prometheus + Grafana | Open-source | Бесплатно | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| Datadog | SaaS | Платно | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| New Relic | SaaS | Платно | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| ELK Stack | Open-source | Бесплатно | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| CloudWatch | SaaS (AWS) | Платно | ⭐⭐⭐ | ⭐⭐⭐⭐ |
6. Alerting (Оповещения) 🚨
Alerting позволяет быстро реагировать на проблемы до того, как они затронут пользователей. Настройка правильных алертов критически важна для поддержания высокого uptime.
Настройка алертов в Prometheus
# alerts.yml
groups:
- name: api_alerts
interval: 30s
rules:
# Алерт на высокую error rate
- alert: HighErrorRate
expr: rate(http_requests_total{status_code=~"5.."}[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate detected"
description: "Error rate is { $value }%"
# Алерт на высокую latency
- alert: HighLatency
expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 1
for: 10m
labels:
severity: warning
annotations:
summary: "High latency detected"
description: "P95 latency is { $value }s"
# Алерт на недоступность сервиса
- alert: ServiceDown
expr: up{job="api-server"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "API service is down"
description: "Service has been down for more than 1 minute"
# Алерт на высокое использование CPU
- alert: HighCPUUsage
expr: rate(process_cpu_user_seconds_total[5m]) > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "High CPU usage"
description: "CPU usage is { $value }%"
Интеграция с уведомлениями
// Отправка алертов в Slack
const { IncomingWebhook } = require('@slack/webhook');
const webhook = new IncomingWebhook(process.env.SLACK_WEBHOOK_URL);
async function sendAlert(alert) {
await webhook.send({
text: `🚨 Alert: ${alert.summary}`,
attachments: [{
color: alert.severity === 'critical' ? 'danger' : 'warning',
fields: [
{ title: 'Description', value: alert.description },
{ title: 'Severity', value: alert.severity },
{ title: 'Time', value: new Date().toISOString() }
]
}]
});
}
// Отправка алертов в email
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 587,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS
}
});
async function sendEmailAlert(alert) {
await transporter.sendMail({
from: 'alerts@api.example.com',
to: 'devops@example.com',
subject: `🚨 ${alert.summary}`,
html: `
Alert: ${alert.summary}
${alert.description}
Severity: ${alert.severity}
Time: ${new Date().toISOString()}
`
});
}
Умные алерты
// Алерты с дедупликацией и группировкой
class AlertManager {
constructor() {
this.alertHistory = new Map();
this.alertCooldown = 5 * 60 * 1000; // 5 минут
}
async sendAlert(alert) {
const alertKey = `${alert.type}-${alert.resource}`;
const lastSent = this.alertHistory.get(alertKey);
// Проверяем cooldown
if (lastSent && Date.now() - lastSent < this.alertCooldown) {
console.log(`Alert ${alertKey} is in cooldown`);
return;
}
// Отправляем алерт
await this.dispatchAlert(alert);
// Запоминаем время отправки
this.alertHistory.set(alertKey, Date.now());
}
async dispatchAlert(alert) {
// Отправка в несколько каналов
await Promise.all([
sendSlackAlert(alert),
sendEmailAlert(alert),
logAlert(alert)
]);
}
}
Best Practices для мониторинга 🌟
✅ 10 правил эффективного мониторинга:
- Мониторьте все критические метрики — latency, error rate, throughput
- Настройте health checks — для всех сервисов
- Логируйте структурированно — JSON формат с контекстом
- Используйте корреляционные ID — для трейсинга запросов
- Настройте алерты правильно — не слишком много, не слишком мало
- Мониторьте зависимости — БД, кэш, внешние API
- Используйте дашборды — для визуализации метрик
- Храните логи централизованно — для поиска и анализа
- Настройте retention policy — баланс между историей и стоимостью
- Регулярно проверяйте алерты — настройте и тестируйте
Ключевые метрики для мониторинга
Golden Signals (Золотые сигналы)
- ✅ Latency — время ответа
- ✅ Traffic — нагрузка на систему
- ✅ Errors — частота ошибок
- ✅ Saturation — использование ресурсов
SLI/SLO/SLA
# Service Level Indicators (SLI)
- Availability: процент успешных запросов
- Latency: процент запросов быстрее X мс
- Error Rate: процент запросов без ошибок
# Service Level Objectives (SLO)
- 99.9% запросов должны быть успешными
- 95% запросов должны отвечать быстрее 500ms
- 99% запросов должны отвечать быстрее 1s
# Service Level Agreements (SLA)
- Контракт с пользователями о доступности сервиса
- Обычно 99.9% или выше
- Включает компенсации при нарушении
Чек-лист мониторинга
☐ Health check endpoints настроены
☐ Метрики собираются и экспортируются
☐ Логирование структурировано
☐ Дашборды созданы для ключевых метрик
☐ Алерты настроены для критических событий
☐ Мониторинг зависимостей настроен
☐ Retention policy для логов установлена
☐ Тестирование алертов проведено
☐ Документация по мониторингу создана
☐ On-call ротация настроена
Заключение
Мониторинг и логирование API — критически важные компоненты для поддержания работоспособности системы. Правильно настроенный мониторинг позволяет выявлять проблемы на ранней стадии и быстро реагировать на инциденты.
💡 Ключевые выводы:
- Health checks необходимы для автоматических проверок
- Метрики должны покрывать все аспекты производительности
- Логирование должно быть структурированным и контекстным
- Prometheus + Grafana — отличный open-source вариант
- Datadog — комплексное платное решение
- Алерты должны быть настроены разумно
- Мониторьте Golden Signals для полной картины
⚠️ Частые ошибки:
- Слишком много или слишком мало алертов
- Отсутствие контекста в логах
- Игнорирование метрик зависимостей
- Неструктурированное логирование
- Отсутствие retention policy для логов
Создайте Mock API за 2 минуты
Используйте Mock API для тестирования мониторинга и логирования без воздействия на production системы. LightBox API позволяет протестировать различные сценарии ошибок и нагрузки.
Попробовать бесплатно →