Introduction: Why Monitoring is Critical for TypeScript Applications
\nTypeScript is a powerful tool for building scalable web applications, but even the highest quality code is not immune to production failures. Without a monitoring system, you risk learning about problems from users rather than from automatic alerts. In this article, we will cover the complete monitoring setup cycle: from collecting metrics to sending notifications.
\nWe will examine the three key pillars of observability: metrics (numerical system indicators), logs (detailed event records), and tracing (request tracking). Special attention will be given to integration with popular tools — Prometheus, Grafana, and Sentry.
\n\n1. Key Metrics for TypeScript Applications
\nBefore setting up alerts, you need to determine which metrics are critical for your application. Let's divide them into three groups:
\n\n1.1 Business Metrics
\nReflect the health of the application from the user's perspective:
\n- \n
- Requests Per Second (RPS) — shows server load \n
- Response Time (Latency) — p50, p95, p99 percentiles \n
- Error Count — 4xx and 5xx status codes \n
- Active Users — sessions, WebSocket connections \n
1.2 Technical Metrics
\nShow the state of the infrastructure:
\n- \n
- CPU and Memory — processor load and memory leaks \n
- Event Loop Lag — delays in the Node.js event loop \n
- Garbage Collection — frequency and duration of garbage collection \n
- HTTP Connection Pool — number of open connections \n
1.3 Example: Collecting Metrics with Prometheus in TypeScript
\nInstall the prom-client library for Node.js:
npm install prom-client @types/prom-client\n\nCreate a file metrics.ts:
import client from 'prom-client';\nimport express, { Request, Response } from 'express';\n\n// Create a metrics registry\nconst register = new client.Registry();\nclient.collectDefaultMetrics({ register });\n\n// Custom metric: HTTP request count\nconst httpRequestCounter = new client.Counter({\n name: 'http_requests_total',\n help: 'Total number of HTTP requests',\n labelNames: ['method', 'route', 'status_code'],\n registers: [register],\n});\n\n// Response time histogram\nconst httpRequestDuration = new client.Histogram({\n name: 'http_request_duration_seconds',\n help: 'Duration of HTTP requests in seconds',\n labelNames: ['method', 'route', 'status_code'],\n buckets: [0.1, 0.5, 1, 2, 5],\n registers: [register],\n});\n\n// Middleware for collecting metrics\nexport function metricsMiddleware(req: Request, res: Response, next: Function) {\n const end = httpRequestDuration.startTimer();\n res.on('finish', () => {\n const labels = {\n method: req.method,\n route: req.route?.path || 'unknown',\n status_code: res.statusCode.toString(),\n };\n httpRequestCounter.inc(labels);\n end(labels);\n });\n next();\n}\n\n// Endpoint for Prometheus\nexport function metricsEndpoint(req: Request, res: Response) {\n res.set('Content-Type', register.contentType);\n res.send(register.metrics());\n}\n\n2. Setting Up Alerts: From Theory to Practice
\nAlerts should be smart — too many false positives exhaust the team. Use the "three sigma" rule for anomalies and threshold values for critical metrics.
\n\n2.1 Alert Rules in Prometheus
\nCreate a file alerts.yml:
groups:\n - name: typescript-app\n rules:\n - alert: HighErrorRate\n expr: |\n rate(http_requests_total{status_code=~"5.."}[5m]) \n / \n rate(http_requests_total[5m]) > 0.05\n for: 2m\n labels:\n severity: critical\n annotations:\n