@jamx-framework/metrics
v1.0.0
Published
JAMX Framework — Performance metrics
Maintainers
Readme
@jamx-framework/metrics
Descripción
Módulo de métricas y monitoreo de salud para JAMX Framework. Proporciona una API para recolectar, agregar y exponer métricas de aplicación (latencia, throughput, errores, uso de recursos) y health checks. Soporta múltiples backends de almacenamiento (Prometheus, StatsD, CloudWatch) y puede exponer endpoints HTTP para scrapers de métricas.
Cómo funciona
El módulo implementa un sistema de observabilidad:
- Metrics: Clase principal que registra contadores, gauges, histogramas y timers
- Health: Sistema de health checks que evalúa el estado de dependencias (DB, cache, colas)
- Middleware: Middleware HTTP que automáticamente registra métricas de requests
- Exporters: Exportadores a formatos estándar (Prometheus, OpenTelemetry)
Componentes principales
- src/metrics.ts: Clase
Metricsque gestiona contadores, gauges, histogramas - src/health.ts: Clase
Healthque ejecuta y agrupa health checks - src/middleware.ts: Middleware HTTP para registrar métricas de requests
- src/types.ts: Tipos compartidos (Metric, HealthCheck, etc.)
- src/index.ts: Punto de exportación
Uso básico
import { Metrics, Health } from '@jamx-framework/metrics';
// Crear instancia
const metrics = new Metrics();
const health = new Health();
// Registrar contador
const requestsCounter = metrics.counter('http_requests_total', {
help: 'Total HTTP requests',
labels: ['method', 'path', 'status'],
});
// Incrementar contador
requestsCounter.increment({ method: 'GET', path: '/api/users', status: 200 });
// Registrar gauge (valor que sube/baja)
const activeConnections = metrics.gauge('active_connections', {
help: 'Current active connections',
});
activeConnections.set(42);
// Registrar timer (latencia)
const requestDuration = metrics.histogram('http_request_duration_seconds', {
help: 'HTTP request duration',
buckets: [0.1, 0.5, 1, 2, 5],
});
requestDuration.observe(0.234);
// Health checks
health.register('database', async () => {
const ok = await db.ping();
return { status: ok ? 'healthy' : 'unhealthy', details: { latency: '10ms' } };
});
health.register('redis', async () => {
const ok = await cache.ping();
return { status: ok ? 'healthy' : 'unhealthy' };
});
// Ejecutar todos los health checks
const result = await health.checkAll();
console.log('Estado general:', result.status); // 'healthy' | 'degraded' | 'unhealthy'Ejemplos
Middleware HTTP automático
import { MetricsMiddleware } from '@jamx-framework/metrics';
import { Server } from '@jamx-framework/server';
const server = new Server();
// Añadir middleware de métricas
server.use(MetricsMiddleware(metrics, {
// Configuración opcional
durationMetric: 'http_request_duration_seconds',
countMetric: 'http_requests_total',
labels: {
method: true, // incluir método HTTP
path: true, // incluir path (cuidado con cardinalidad alta)
status: true, // incluir código de estado
userAgent: false, // no incluir user-agent
},
}));
// Las métricas se registran automáticamente en cada requestExportar a Prometheus
import { Metrics } from '@jamx-framework/metrics';
import * as promClient from 'prom-client';
const metrics = new Metrics();
// Registrar métricas personalizadas
metrics.counter('custom_requests', { labels: ['endpoint'] });
metrics.gauge('queue_size', { labels: ['queue'] });
// Exponer endpoint /metrics
server.get('/metrics', async (req, res) => {
const registry = promClient.register;
// Convertir métricas JAMX a formato Prometheus
for (const metric of metrics.getAll()) {
// Lógica de conversión...
}
res.text(await registry.metrics());
});Health checks compuestos
import { Health } from '@jamx-framework/metrics';
const health = new Health();
// Check de base de datos
health.register('database', {
check: async () => {
try {
await db.ping();
return { status: 'healthy', details: { version: db.version } };
} catch (error) {
return { status: 'unhealthy', details: { error: error.message } };
}
},
timeout: 5000, // 5 segundos timeout
dependencies: ['redis'], // depende de que redis esté healthy
});
// Check de Redis
health.register('redis', {
check: async () => {
const pong = await cache.ping();
return { status: pong === 'PONG' ? 'healthy' : 'unhealthy' };
},
});
// Check de disco
health.register('disk', {
check: async () => {
const stats = await fs.statvfs('/');
const freePercent = (stats.f_bavail / stats.f_blocks) * 100;
return {
status: freePercent > 10 ? 'healthy' : 'unhealthy',
details: { freePercent },
};
},
});
// Endpoint de health
server.get('/health', async (req, res) => {
const result = await health.checkAll();
const status = result.status === 'healthy' ? 200 : 503;
res.json(result, status);
});Timers y contextos
import { Metrics } from '@jamx-framework/metrics';
const metrics = new Metrics();
const timer = metrics.timer('function_execution_seconds', {
labels: ['function_name'],
});
async function processOrder(orderId: string) {
const stop = timer.start({ function_name: 'processOrder' });
try {
// Lógica de procesamiento
await orderService.process(orderId);
stop(); // Registrar duración
} catch (error) {
stop(); // Registrar duración incluso en error
throw error;
}
}Métricas personalizadas con etiquetas
const ordersCreated = metrics.counter('orders_created_total', {
labels: ['payment_method', 'country'],
});
// Incrementar con etiquetas específicas
ordersCreated.increment({
payment_method: 'credit_card',
country: 'US',
});
// Consultar métrica actual
const current = ordersCreated.getValue({
payment_method: 'credit_card',
country: 'US',
});
console.log('Órdenes con tarjeta en US:', current);Uso con inyección de dependencias
import { Container } from '@jamx-framework/core';
import { Metrics, Health } from '@jamx-framework/metrics';
// Registrar en contenedor
Container.registerSingleton('metrics', () => new Metrics());
Container.registerSingleton('health', () => new Health());
// Resolver y usar
const metrics = Container.resolve<Metrics>('metrics');
const health = Container.resolve<Health>('health');
// Registrar métrica
const apiCalls = metrics.counter('api_calls_total');
apiCalls.increment();
// Verificar salud
const status = await health.check('database');Flujo interno
- Inicialización: Se crean instancias de
MetricsyHealth - Registro: Se registran contadores, gauges, histogramas con nombres y etiquetas
- Recolección: Durante la ejecución, se invoca
.increment(),.set(),.observe() - Agregación: Las métricas se agregan por etiquetas en memoria
- Exportación: Se pueden exportar a Prometheus, StatsD, CloudWatch, etc.
- Health checks: Se ejecutan checks registrados (DB, cache, disco) y se agrupan resultados
- Middleware: El middleware HTTP automáticamente registra duración y conteo de requests
API Reference (Resumen)
Metrics
counter(name, options): Crea un contador (solo incrementa)gauge(name, options): Crea un gauge (setea valor)histogram(name, options): Crea un histograma (distribución de valores)timer(name, options): Crea un timer (mide duración)getAll(): Metric[]: Obtiene todas las métricas registradasreset(): Limpia todas las métricas
Health
register(name, check): Registra un health checkcheck(name?): Ejecuta check específico o todoscheckAll(): Promise<HealthResult>: Ejecuta todos y agrupagetStatus(): 'healthy' | 'degraded' | 'unhealthy'
Metric (interface)
name: stringtype: 'counter' | 'gauge' | 'histogram' | 'timer'help?: stringlabels?: string[]value: number | Map<string, number>
HealthCheck (interface)
name: stringcheck: () => Promise<HealthStatus>timeout?: numberdependencies?: string[]
Performance Considerations
- In-memory aggregation: Las métricas se agregan en memoria, sin I/O en cada registro
- Lock-free: Usa estructuras atómicas cuando es posible
- Sampling: Se puede configurar sampling para alto volumen
- Batch export: Exportación en batch para reducir overhead
- Histogram buckets: Elegir buckets apropiados para no gastar memoria
Configuration Options
const metrics = new Metrics({
// Configuración global
prefix: 'jamx_', // prefijo para nombres de métricas
defaultLabels: { env: 'production' }, // labels por defecto
sampling: 1.0, // 1.0 = 100%, 0.1 = 10%
autoFlush: true, // flush automático a backend
flushInterval: 5000, // ms entre flushes
});
const health = new Health({
timeout: 10000, // timeout por defecto para checks
parallel: true, // ejecutar checks en paralelo
cacheDuration: 30000, // cachear resultados por 30s
});Testing
Tests en packages/metrics/tests/unit/:
pnpm testCubre:
- Creación de contadores, gauges, histogramas
- Incrementos y observaciones
- Health checks simples y compuestos
- Middleware HTTP
- Exportación a formatos
Compatibility
- Compatible con Node.js 18+
- Exporta a Prometheus text format
- Integra con OpenTelemetry (opcional)
- Funciona en Windows, macOS, Linux
CLI Integration
jamx metrics:export <format>: Exporta métricas a formato específicojamx health:check: Ejecuta health checks y muestra resultadojamx metrics:reset: Limpia métricas acumuladasjamx metrics:tail: Muestra métricas en tiempo real (comotop)
Best Practices
- Usar nombres consistentes:
http_requests_total,db_query_duration_seconds - Limitar cardinalidad: Evitar etiquetas con alta cardinalidad (user_id, session_id)
- Health checks rápidos: Los checks deben terminar en segundos, no minutos
- Dependencias claras: Documentar dependencias entre health checks
- Alertas basadas en métricas: Configurar alertas en Prometheus/Grafana
- Métricas de negocio: Además de técnicas, registrar métricas de negocio (signups, purchases)
- Sampling en producción: Para alto tráfico, usar sampling para reducir overhead
This metrics module provides comprehensive observability for JAMX applications, enabling developers to monitor performance, detect issues, and understand application behavior in production environments.
