Guías

Timestamps en Logs: Parsing, Normalización y Corrección de Errores

Por qué esta página

Los logs llegan con formatos mixtos, zonas horarias y precisiones diferentes. Un timestamp incorrecto rompe el ordenamiento, dashboards y líneas de tiempo de incidentes. Esta guía te da verificaciones rápidas, plantillas regex y fragmentos de código para normalizar tiempos de logs rápidamente—y te dirige a convertidores cuando necesitas verificar casos límite.

Lista de verificación de diagnóstico rápido

  • Identificar formato: ISO 8601/RFC 3339, Apache/Nginx ([dd/Mon/yyyy:HH:mm:ss Z]), syslog (MMM d HH:mm:ss), o personalizado.
  • Verificar zona horaria: desplazamiento explícito (+0100, Z) vs. local implícito. Usar UTC por defecto si falta.
  • Verificar precisión: segundos vs. milisegundos vs. microsegundos; asegurar longitud consistente.
  • Manejar bordes DST: parsear a UTC primero; evitar parsing local naive.
  • Aplicar guardias de rango: rechazar años implausibles (ej., <2000 o >2100).

Formatos de log comunes (plantillas)

  • ISO 8601 / RFC 3339: 2024-12-01T10:15:30Z, 2024-12-01T18:15:30+01:00
  • Apache/Nginx: [01/Dec/2024:10:15:30 +0000]
  • Syslog: Dec 1 10:15:30 hostname app[123]: mensaje (sin año/desplazamiento)
  • Numérico personalizado: 1701425730 (segundos), 1701425730000 (ms)

Pasos de normalización

  1. Detectar formato y zona horaria. Si falta, asumir UTC.
  2. Parsear a datetime aware (UTC).
  3. Si es epoch numérico: escalar por longitud (10s/13ms/16µs/19ns).
  4. Almacenar en columna canónica (ej., millis UTC) y opcionalmente mantener la cadena raw.
  5. Validar rango; descartar o marcar valores atípicos antes de indexar.

Fragmentos de código

JavaScript / Node.js

import { DateTime } from "luxon";

export function parseLogTime(raw) {
  const trimmed = raw.trim();
  // ISO/RFC primero
  const iso = DateTime.fromISO(trimmed, { setZone: true });
  if (iso.isValid) return iso.toUTC().toMillis();

  // Apache/Nginx: [01/Dec/2024:10:15:30 +0000]
  const m = trimmed.match(/\[(\d{2}\/\w{3}\/\d{4}:\d{2}:\d{2}:\d{2} [+-]\d{4})\]/);
  if (m) {
    const dt = DateTime.fromFormat(m[1], "dd/MMM/yyyy:HH:mm:ss ZZZZ", { zone: "utc" });
    if (dt.isValid) return dt.toMillis();
  }

  // Epoch numérico (segundos/ms/us/ns)
  if (/^\d+$/.test(trimmed)) {
    const len = trimmed.length;
    if (len === 10) return Number(trimmed) * 1000;
    if (len === 13) return Number(trimmed);
    if (len === 16) return Number(trimmed) / 1000;
    if (len === 19) return Number(trimmed) / 1_000_000;
  }

  throw new Error("Timestamp de log no reconocido");
}

Python

from datetime import datetime, timezone, timedelta
from dateutil import parser

def parse_log_time(raw: str) -> int:
    s = raw.strip()
    # ISO/RFC 3339, syslog con tz, etc.
    try:
        dt = parser.isoparse(s)
        if dt.tzinfo is None:
            dt = dt.replace(tzinfo=timezone.utc)
        return int(dt.astimezone(timezone.utc).timestamp() * 1000)
    except Exception:
        pass

    # Apache/Nginx: [01/Dec/2024:10:15:30 +0000]
    if s.startswith("[") and s.endswith("]"):
        inner = s[1:-1]
        dt = datetime.strptime(inner, "%d/%b/%Y:%H:%M:%S %z")
        return int(dt.astimezone(timezone.utc).timestamp() * 1000)

    # Epoch numérico
    if s.isdigit():
        n = len(s)
        if n == 10:
            return int(s) * 1000
        if n == 13:
            return int(s)
        if n == 16:
            return int(int(s) / 1000)
        if n == 19:
            return int(int(s) / 1_000_000)
    raise ValueError("Timestamp de log no reconocido")

Plantillas Regex

regex
# ISO 8601 / RFC 3339 con desplazamiento
^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})$

# Apache/Nginx
^\[\d{2}/[A-Za-z]{3}/\d{4}:\d{2}:\d{2}:\d{2} [+-]\d{4}\]$

# Syslog (sin año/desplazamiento, añadir externamente)
^[A-Za-z]{3}\s+\d{1,2}\s\d{2}:\d{2}:\d{2}\s.+$

FAQ

  • ¿Qué hacer si falta la zona horaria? Asumir UTC y documentar la suposición.
  • ¿Cómo manejar logs de múltiples fuentes? Normalizar todo a UTC millis antes de agregación.
  • ¿Qué hay de los logs syslog sin año? Usar el año actual o inferir del contexto del archivo.

Herramientas y guías relacionadas