Monitoring и Observability: видимость в production

От метрик до distributed tracing: полный стек наблюдаемости

"Работает на моей машине" — худший ответ в production. Без proper monitoring вы узнаете о проблемах от пользователей, а не от систем. Modern observability дает полную видимость в систему. Three Pillars of Observability 1. Metrics (Что происходит) Числовые данные о системе: Counter — монотонно растет (requests count) Gauge — текущее значение (CPU usage) Histogram — распределение значений (latency) 2. Logs (События) Текстовые записи о событиях в системе. 3. Traces (Путь запроса) Путь запроса через микросервисы. Prometheus + Grafana Stack Node.js metrics с prom-client: import promClient from 'prom-client'; // Дефолтные метрики (CPU, memory, etc) promClient.collectDefaultMetrics(); // Custom метрики const httpRequestDuration = new promClient.Histogram({ name: 'http_request_duration_seconds', help: 'Duration of HTTP requests in seconds', labelNames: ['method', 'route', 'status_code'], }); const activeUsers = new promClient.Gauge({ name: 'active_users', help: 'Number of active users', }); // Middleware для сбора метрик app.use((req, res, next) => { const start = Date.now(); res.on('finish', () => { const duration = (Date.now() - start) / 1000; httpRequestDuration .labels(req.method, req.route?.path || req.path, res.statusCode) .observe(duration); }); next(); }); // Endpoint для Prometheus app.get('/metrics', async (req, res) => { res.set('Content-Type', promClient.register.contentType); res.end(await promClient.register.metrics()); }); Grafana Dashboard: Visualize метрики с Prometheus data source: Request rate — rate(http_requests_total[5m]) Error rate — rate(http_requests_total{status=~"5.."}[5m]) P95 latency — histogram_quantile(0.95, http_request_duration_seconds) Structured Logging Winston для Node.js: import winston from 'winston'; const logger = winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ), defaultMeta: { service: 'api' }, transports: [ new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }), ], }); if (process.env.NODE_ENV !== 'production') { logger.add(new winston.transports.Console({ format: winston.format.simple(), })); } // Использование logger.info('User logged in', { userId: user.id, email: user.email, ip: req.ip, }); ELK Stack (Elasticsearch, Logstash, Kibana): Централизованное хранение и поиск логов. // Отправка логов в Elasticsearch import { ElasticsearchTransport } from 'winston-elasticsearch'; const esTransport = new ElasticsearchTransport({ level: 'info', clientOpts: { node: 'http://localhost:9200' }, index: 'logs', }); logger.add(esTransport); Distributed Tracing OpenTelemetry: import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { registerInstrumentations } from '@opentelemetry/instrumentation'; import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express'; import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; const provider = new NodeTracerProvider(); provider.addSpanProcessor( new BatchSpanProcessor(new JaegerExporter()) ); provider.register(); registerInstrumentations({ instrumentations: [ new HttpInstrumentation(), new ExpressInstrumentation(), ], }); // Custom spans const tracer = trace.getTracer('my-service'); app.get('/api/users', async (req, res) => { const span = tracer.startSpan('fetch_users'); try { const users = await db.user.findMany(); span.setStatus({ code: SpanStatusCode.OK }); res.json(users); } catch (error) { span.setStatus({ code: SpanStatusCode.ERROR, message: error.message }); throw error; } finally { span.end(); } }); Alerting Prometheus Alertmanager: groups: - name: api_alerts rules: - alert: HighErrorRate expr: | rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05 for: 5m labels: severity: critical annotations: summary: "High error rate detected" description: "Error rate is {{ $value | humanizePercentage }}" - 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" PagerDuty Integration: Алерты приходят в PagerDuty → SMS/Call on-call инженеру. Application Performance Monitoring (APM) New Relic / Datadog: Автоматическая инструментация Slow transaction tracing Database query analysis Error tracking Sentry для Error Tracking: import * as Sentry from '@sentry/node'; Sentry.init({ dsn: process.env.SENTRY_DSN, environment: process.env.NODE_ENV, tracesSampleRate: 1.0, }); app.use(Sentry.Handlers.requestHandler()); app.use(Sentry.Handlers.tracingHandler()); // Your routes app.use(Sentry.Handlers.errorHandler()); Key Metrics (Golden Signals) 1. Latency P50, P95, P99 response time Цель: P95 2. Traffic Requests per second Active users 3. Errors Error rate (%) Цель: 4. Saturation CPU usage Memory usage Disk I/O Цель: SLI, SLO, SLA SLI (Service Level Indicator): Метрика качества сервиса Availability = Successful requests / Total requests SLO (Service Level Objective): Цель для SLI SLO: 99.9% availability (43.2 min downtime/month) SLA (Service Level Agreement): Контракт с последствиями SLA: 99.9% uptime, иначе refund 10% Error Budget Error Budget = 1 - SLO При SLO 99.9%: Error Budget = 0.1% = 43.2 минуты downtime/месяц Если Error Budget исчерпан → freeze features, focus на reliability On-Call Best Practices Runbooks — документация для каждого алерта Rotation — справедливое распределение on-call Postmortems — анализ каждого инцидента Blameless culture — фокус на процессе, не людях Synthetic Monitoring Мониторинг через автоматические тесты: // Playwright для E2E мониторинга import { test, expect } from '@playwright/test'; test('critical user flow', async ({ page }) => { await page.goto('https://example.com'); await page.click('text=Login'); await page.fill('input[name=email]', 'test@example.com'); await page.fill('input[name=password]', 'password'); await page.click('button[type=submit]'); await expect(page).toHaveURL('/dashboard'); }); // Запускать каждые 5 минут из разных локаций Cost Optimization Reduce logging volume: // Сэмплирование логов if (Math.random() Retention policies: Hot data: 7 дней Warm data: 30 дней Cold data: 1 год (archive) Dashboard Best Practices Status dashboard — overall health Service dashboard — per-service metrics Business metrics — revenue, signups Alerts dashboard — active alerts Заключение: Observability — не опция, а необходимость для production систем. Внедряйте monitoring с первого дня. Собирайте метрики, логи и трейсы. Настройте алерты на критичные проблемы. Создайте runbooks для on-call. Проводите postmortems после инцидентов. Помните: вы не можете улучшить то, что не измеряете. Инвестируйте в observability — это окупится через быстрое обнаружение и решение проблем.