Skip to content

Guía de integración de MiniObserv para proyectos Node.js ​

Esta guía está dirigida a desarrolladores que trabajan con Express.js, NestJS o Next.js y quieren tener visibilidad real sobre lo que su aplicación hace en producción — latencia de peticiones, tasa de errores, presión de memoria, uso de disco — sin incorporar una plataforma de observabilidad pesada.


1. Qué cubre realmente MiniObserv ​

Antes de integrarlo, es importante saber qué capa de observabilidad cubre MiniObserv — y cuál no.

Capa 1 — Infraestructura        ← MiniObserv
  ¿La máquina está bien?
  CPU, memoria, disco, red, líneas de log
  Herramientas: MiniObserv, Datadog, Prometheus

Capa 2 — Aplicación (APM)
  ¿Mi código está bien?
  Queries lentos, N+1, stack traces, latencia por función
  Herramientas: Sentry, Datadog APM, OpenTelemetry

Capa 3 — Negocio
  ¿Mi producto está bien?
  Usuarios activos, conversiones, uso de features
  Herramientas: PostHog, Mixpanel, Amplitude

MiniObserv es Capa 1. Monitorea el servidor donde corre tu app — el sistema operativo, no tu código ni tus datos. No se conecta a tu PostgreSQL, no lee tu esquema de Prisma, no sabe nada de tu lógica de negocio. Cuando despliegas en 3 servidores, te dice que el CPU de cada uno está al 42% — no te dirá qué query de Prisma causó un pico de latencia (eso es Capa 2).

En producción eventualmente necesitarás las tres capas. Empieza con la Capa 1 — si el servidor se queda sin memoria o disco, las otras dos no importan.


2. Por qué MiniObserv para proyectos Node.js ​

Tu framework funciona bien. Lo que no tienes es visibilidad sobre lo que ocurre una vez desplegado.

Lo que obtienes sin escribir código:

  • CPU, memoria, disco y red recolectados cada 10 segundos
  • Todas las líneas de log de tu aplicación en un visor estructurado
  • Alertas cuando los recursos del sistema superan los umbrales que definas
  • Dashboard con gráficas en tiempo real en http://localhost:8080

El agente de MiniObserv es un binario Go independiente. Corre junto a tu aplicación Node.js como un contenedor separado. No conoce Node.js — monitorea el sistema operativo del host. No necesitas instalar nada en tu app para obtener métricas a nivel de sistema.

El SDK (@kamerrezz/miniobserv) es opcional. Úsalo solo cuando necesites enviar métricas personalizadas a nivel de aplicación (distribución de tiempos de respuesta, conteo de errores, profundidad de colas). Empieza sin él — es posible que no lo necesites.


2. Configuración inicial (3 minutos) ​

Agrega MiniObserv a tu docker-compose.yml existente. Necesitas tres nuevos servicios junto a los tuyos:

ServicioQué es¿Toca tu app?
miniobserv-dbTimescaleDB — almacenamiento propio de MiniObservNo
miniobservEl servidor MiniObserv + dashboardNo
miniobserv-agentRecolecta métricas del sistema operativoNo

miniobserv-db es completamente independiente de tu base de datos. Si tu stack usa PostgreSQL + Prisma, MySQL + Sequelize, o cualquier otra cosa — MiniObserv nunca la toca. Tiene su propio contenedor de base de datos aislado. Tu app y MiniObserv no comparten ningún almacenamiento.

Stack típico: Express + Prisma + PostgreSQL ​

Así se ve un proyecto real con MiniObserv integrado:

yaml
services:
  # ── tu stack existente ───────────────────────────────────────
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: miapp
      POSTGRES_PASSWORD: miapp
      POSTGRES_DB: miapp_db
    volumes:
      - app_data:/var/lib/postgresql/data

  app:
    build: .
    environment:
      DATABASE_URL: "postgresql://miapp:miapp@db:5432/miapp_db"
    depends_on:
      - db
      - miniobserv        # opcional: esperar a que el dashboard esté listo
    volumes:
      - app_logs:/var/log/app

  # ── MiniObserv — agrega estos tres servicios ─────────────────
  miniobserv-db:          # DB propia de MiniObserv — aislada de la tuya
    image: timescale/timescaledb:latest-pg16
    environment:
      POSTGRES_USER: minidog
      POSTGRES_PASSWORD: minidog
      POSTGRES_DB: miniobserv
    volumes:
      - miniobserv_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U minidog -d miniobserv"]
      interval: 5s
      retries: 10

  miniobserv:
    image: kamerrezz/miniobserv-server:latest
    environment:
      DATABASE_URL: "postgres://minidog:minidog@miniobserv-db:5432/miniobserv?sslmode=disable"
      AGENT_TOKEN: "tu-secreto-de-32-caracteres-aqui"
      ALERT_RULES: '[{"host":"*","name":"cpu.usage_pct","op":">","threshold":80,"for":"5m"}]'
    ports:
      - "8080:8080"
    depends_on:
      miniobserv-db:
        condition: service_healthy

  miniobserv-agent:
    image: kamerrezz/miniobserv-agent:latest
    environment:
      SERVER_URL: "http://miniobserv:8080"
      AGENT_TOKEN: "tu-secreto-de-32-caracteres-aqui"
      LOG_PATHS: "/var/log/app/app.log"
    volumes:
      - app_logs:/var/log/app:ro   # acceso de solo lectura a los logs de tu app

volumes:
  app_data:           # datos de tu app
  miniobserv_data:    # datos de MiniObserv — completamente separados
  app_logs:           # compartido entre app (escritura) y agente (lectura)

Genera un AGENT_TOKEN seguro:

bash
openssl rand -hex 32

Usa el mismo valor en miniobserv y en miniobserv-agent. Nunca sale de tu infraestructura.

Abre http://localhost:8080 después de docker compose up. El dashboard aparece en aproximadamente 10 segundos.


3. Integración con Express.js ​

3a. Escribir logs en un archivo (para el seguimiento de logs) ​

El agente de MiniObserv observa los archivos indicados en LOG_PATHS y transmite cada línea nueva al visor de logs del dashboard. La forma más sencilla es añadir un transport de archivo al logger de Winston existente:

javascript
const winston = require('winston');

const logger = winston.createLogger({
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: '/var/log/myapp/app.log' }),
  ],
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json(),
  ),
});

Monta el mismo volumen en el contenedor de tu app para que el agente pueda leer el archivo:

yaml
services:
  api:
    volumes:
      - app_logs:/var/log/myapp

3b. Middleware de registro de peticiones (formato estructurado) ​

Registra cada petición para que el flujo de logs del dashboard muestre actividad HTTP significativa. El nivel se determina por el código de estado — ERROR para 5xx, WARN para 4xx, INFO para el resto:

javascript
app.use((req, res, next) => {
  res.on('finish', () => {
    const level =
      res.statusCode >= 500 ? 'error' :
      res.statusCode >= 400 ? 'warn' : 'info';

    logger[level]({
      method: req.method,
      path: req.path,
      status: res.statusCode,
    });
  });
  next();
});

Esto es suficiente para ver errores HTTP y rutas lentas en el dashboard sin usar el SDK.

3c. Enviar métricas personalizadas con el SDK (opcional) ​

Instala el SDK:

bash
npm install @kamerrezz/miniobserv

Inicializa el cliente una vez y reutilízalo en toda la aplicación:

javascript
import { MiniObservClient } from '@kamerrezz/miniobserv';

const obs = new MiniObservClient({
  baseUrl: process.env.MINIOBSERV_URL ?? 'http://miniobserv:8080',
  agentToken: process.env.AGENT_TOKEN,
  defaultHost: process.env.HOSTNAME ?? 'api-server',
});

Envía una señal personalizada desde un middleware — disparar y olvidar, nunca bloquear la petición:

javascript
app.use((req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    const heapUsed = process.memoryUsage().heapUsed;
    const heapTotal = process.memoryUsage().heapTotal;

    obs.pushMetric('mem.used_pct', (heapUsed / heapTotal) * 100)
      .catch(() => {}); // nunca dejes que la observabilidad rompa la petición
  });
  next();
});

Una nota sobre los nombres de métricas: MiniObserv tiene un conjunto fijo de nombres canónicos (cpu.usage_pct, mem.used_pct, disk.used_pct, etc.). El agente ya los completa desde el sistema operativo. Al enviar desde el SDK, puedes usar los mismos nombres para añadir puntos de datos desde la perspectiva de la app, o enviar cualquier valor que quieras rastrear. El enfoque recomendado para la mayoría de apps es comenzar con el patrón sidecar (solo el agente) y añadir envíos por SDK únicamente para señales de alto valor como tasa de errores o latencia de base de datos.


4. Integración con NestJS ​

4a. Interceptor global para el registro de peticiones ​

Crea un interceptor que registre cada petición con método, URL, estado y duración:

typescript
// src/common/observability.interceptor.ts
import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
  Logger,
} from '@nestjs/common';
import { Observable, tap } from 'rxjs';

@Injectable()
export class ObservabilityInterceptor implements NestInterceptor {
  private readonly logger = new Logger('HTTP');

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const req = context.switchToHttp().getRequest();
    const start = Date.now();

    return next.handle().pipe(
      tap(() => {
        const res = context.switchToHttp().getResponse();
        const duration = Date.now() - start;
        this.logger.log(
          `${req.method} ${req.url} → ${res.statusCode} (${duration}ms)`,
        );
      }),
    );
  }
}

4b. Registro global en main.ts ​

typescript
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ObservabilityInterceptor } from './common/observability.interceptor';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalInterceptors(new ObservabilityInterceptor());
  await app.listen(3000);
}
bootstrap();

4c. Redirigir los logs de NestJS a un archivo con Winston ​

bash
npm install nest-winston winston
typescript
// src/winston.config.ts
import * as winston from 'winston';

export const winstonConfig: winston.LoggerOptions = {
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: '/var/log/myapp/app.log' }),
  ],
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json(),
  ),
};
typescript
// main.ts
import { WinstonModule } from 'nest-winston';
import { winstonConfig } from './winston.config';

const app = await NestFactory.create(AppModule, {
  logger: WinstonModule.createLogger(winstonConfig),
});

Con esto en su lugar, cada log de NestJS — incluyendo la salida del interceptor — aterriza en /var/log/myapp/app.log, que el agente de MiniObserv observa automáticamente.


5. Integración con Next.js ​

5a. Middleware de rutas API (App Router, Next.js 14+) ​

El middleware de Next.js corre en el Edge y no tiene acceso a las APIs de archivo de Node.js. La forma más sencilla de llevar logs a MiniObserv es escribir JSON estructurado a stdout y redirigir la salida de Docker a un archivo.

typescript
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const start = Date.now();
  const response = NextResponse.next();

  console.log(
    JSON.stringify({
      level: 'INFO',
      method: request.method,
      path: request.nextUrl.pathname,
      duration_ms: Date.now() - start,
    }),
  );

  return response;
}

export const config = {
  matcher: '/api/:path*',
};

5b. Redirigir el stdout de Next.js a un archivo (docker-compose) ​

Redirige la salida del contenedor a un archivo que el agente pueda seguir:

yaml
services:
  nextjs:
    command: sh -c "node server.js 2>&1 | tee /var/log/myapp/app.log"
    volumes:
      - app_logs:/var/log/myapp

El comando tee escribe tanto en stdout (para que docker logs siga funcionando) como en el archivo. El agente de MiniObserv recoge las nuevas líneas automáticamente a través de LOG_PATHS.


6. Reglas de alerta para aplicaciones Node.js ​

Define las reglas de alerta como un arreglo JSON en la variable de entorno ALERT_RULES del servidor de MiniObserv. Cada regla se activa cuando la condición se mantiene durante la duración especificada:

json
[
  { "host": "*", "name": "cpu.usage_pct",  "op": ">", "threshold": 80, "for": "5m"  },
  { "host": "*", "name": "mem.used_pct",   "op": ">", "threshold": 85, "for": "10m" },
  { "host": "*", "name": "disk.used_pct",  "op": ">", "threshold": 90, "for": "1m"  }
]
  • host: "*" coincide con todos los hosts que reportan a este servidor
  • for es la duración mínima sostenida antes de que se dispare la alerta — evita ruido por picos transitorios
  • Las alertas disparadas aparecen en el dashboard; configura ALERT_NOTIFICATIONS para recibir también notificaciones por webhook (ver Sección 8)

Un punto de partida práctico para una API Node.js típica:

MétricaUmbralDuraciónMotivo
cpu.usage_pct80%5mCarga sostenida, no un pico aislado
mem.used_pct85%10mLas fugas de memoria crecen lentamente
disk.used_pct90%1mEl disco lleno es inmediato y fatal

8. Notificaciones y salud de hosts ​

8a. Notificaciones por webhook ​

MiniObserv puede notificar a servicios externos cuando una alerta se activa o se resuelve. Establece ALERT_NOTIFICATIONS en el servidor con un arreglo JSON de destinos webhook:

yaml
miniobserv:
  environment:
    ALERT_NOTIFICATIONS: '[{"type":"webhook","url":"https://hooks.slack.com/services/YOUR/WEBHOOK/URL"}]'

Funciona con Slack, Discord, Teams, PagerDuty o cualquier servicio que acepte un POST con JSON. Cada notificación se entrega con un timeout de 5 segundos (dispara y olvida, sin reintentos en v1).

Formato del payload:

json
{"event":"firing","rule":{"host":"*","name":"mem.used_pct","op":">","threshold":85,"for":"10m"},"value":87.4,"fired_at":"2026-06-05T16:42:23Z"}

event vale "firing" cuando se supera el umbral y "resolved" cuando la métrica vuelve a estar por debajo.

8b. Alertas automáticas de host caído ​

El servidor registra el último heartbeat de cada agente conectado. Si un agente deja de reportar métricas — por un crash del contenedor, kill por OOM, partición de red u otro motivo — MiniObserv enviará automáticamente un webhook host.down una vez que el host lleve silencioso más tiempo del indicado en HOST_DOWN_AFTER (predeterminado: 50s).

No se necesita configuración adicional más allá de establecer ALERT_NOTIFICATIONS. No hace falta escribir una regla de alerta para esto — el estado de los hosts se rastrea de forma automática.

VariablePredeterminadoSignificado
HOST_STALE_AFTER20sEl host se vuelve naranja (stale) en el dashboard
HOST_DOWN_AFTER50sEl host se vuelve rojo (down) y se dispara el webhook

Puedes consultar el estado actual de todos los hosts con GET /api/v1/hosts (público, sin autenticación):

bash
curl -s http://localhost:8080/api/v1/hosts | jq .

8c. Snippet actualizado de docker-compose con notificaciones ​

yaml
miniobserv:
  build:
    context: ./path/to/theminidog
    dockerfile: Dockerfile.server
  environment:
    DATABASE_URL: "postgres://minidog:minidog@miniobserv-db:5432/miniobserv?sslmode=disable"
    AGENT_TOKEN: "tu-secreto-de-32-caracteres-aqui"
    ALERT_RULES: '[{"host":"*","name":"cpu.usage_pct","op":">","threshold":80,"for":"5m"},{"host":"*","name":"mem.used_pct","op":">","threshold":85,"for":"10m"}]'
    ALERT_NOTIFICATIONS: '[{"type":"webhook","url":"https://hooks.slack.com/services/YOUR/WEBHOOK/URL"}]'
    HOST_STALE_AFTER: "20s"
    HOST_DOWN_AFTER: "50s"
  ports:
    - "8080:8080"
  depends_on:
    miniobserv-db:
      condition: service_healthy

7. Lo que obtienes sin escribir una sola línea de SDK ​

Si solo agregas los dos servicios de MiniObserv a tu docker-compose y apuntas LOG_PATHS al archivo de log de tu app, obtienes:

  • Métricas de sistema — CPU, memoria, disco y red recolectados cada 10 segundos, de forma automática
  • Flujo de logs — cada línea que tu app escribe en el archivo de log, visible en el dashboard con marcas de tiempo
  • Alertas — notificaciones cuando los recursos superan los umbrales que definiste
  • Dashboard en vivo — gráficas en tiempo real en http://localhost:8080, sin configuración adicional

El SDK es para cuando necesitas ir más lejos: rastrear tasas de error por ruta, medir la latencia de consultas a la base de datos, registrar la profundidad de colas. Empieza sin él. Incorpóralo cuando tengas una señal específica que no puedas observar a partir de los logs y las métricas de sistema.


Referencia ​

Nombres canónicos de métricas ​

NombreUnidadDescripción
cpu.usage_pct%Utilización de CPU en todos los núcleos
mem.used_pct%Memoria usada como porcentaje del total
mem.used_bytesbytesMemoria actualmente en uso
mem.total_bytesbytesMemoria total instalada
disk.used_pct%Porcentaje de disco usado (mount /)
disk.used_bytesbytesEspacio en disco en uso
disk.total_bytesbytesCapacidad total del disco
net.bytes_inbytesBytes de red recibidos (delta por intervalo)
net.bytes_outbytesBytes de red enviados (delta por intervalo)

Las métricas net.* son valores delta — no se emiten datos en el primer intervalo de recolección.

Variables de entorno del agente ​

VariableObligatoriaPredeterminadoDescripción
SERVER_URLsí—URL base del servidor de MiniObserv
AGENT_TOKENsí—Secreto HS256 compartido (mínimo 16 caracteres)
COLLECT_INTERVALno10sFrecuencia de recolección (1s–300s)
LOG_PATHSno—Rutas separadas por coma para el tail de logs
AGENT_HOSTnohostname del SOEtiqueta de host en todas las métricas

Released under the MIT License.