Guide

Carimbos de data e hora em logs: análise, normalização e correção de erros

Por que esta página

Os logs chegam com formatos, fusos horários e precisão mistos. Um carimbo de data e hora incorreto interrompe pedidos, painéis e cronogramas de incidentes. Este guia fornece verificações rápidas, modelos de regex e trechos de linguagem para normalizar rapidamente os tempos de log - e indica conversores quando você precisar verificar casos extremos.

Lista de verificação de diagnóstico rápido

  • Identifique formato: ISO 8601/RFC 3339, Apache/Nginx ([dd/Mon/yyyy:HH:mm:ss Z]), syslog (MMM d HH:mm:ss) ou personalizado.
  • Verifique fuso horário: deslocamento explícito (+0800, Z) vs. local implícito. O padrão é UTC se estiver ausente.
  • Verifique a precisão: segundos vs. milissegundos vs. microssegundos; garantir comprimento consistente.
  • Lidar com bordas DST: analisar primeiro em UTC; evite análise local ingênua.
  • Aplicar guardas de campo: rejeitar anos implausíveis (por exemplo, <2000 ou >2100).

Formatos de log comuns (modelos)

  • ISO 8601/RFC 3339: 2024-12-01T10:15:30Z, 2024-12-01T18:15:30+08:00
  • Apache/Nginx: [01/dez/2024:10:15:30 +0000]
  • Syslog: 1º de dezembro 10:15:30 hostname app[123]: mensagem (sem ano/deslocamento)
  • Número personalizado: 1701425730 (segundos), 1701425730000 (ms)

Etapas de normalização

  1. Detecte formato e fuso horário. Se estiver faltando, assuma UTC.
  2. Analise em uma data e hora consciente (UTC).
  3. Se época numérica: escala por duração (10s/13ms/16µs/19ns).
  4. Armazene em uma coluna canônica (por exemplo, milis UTC) e, opcionalmente, mantenha a string bruta.
  5. Validar intervalo; elimine ou sinalize valores discrepantes antes da indexação.

Trechos de código

###JavaScript/Node.js

import { DateTime } from "luxon";

export function parseLogTime(raw) {
  const trimmed = raw.trim();
  // ISO/RFC first
  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();
  }

  // Numeric epoch (seconds/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("Unrecognized log timestamp");
}

###Píton

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

def parse_log_time(raw: str) -> int:
    s = raw.strip()
    # ISO/RFC 3339, syslog with 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 like: [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)

    # Numeric epoch
    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("Unrecognized log timestamp")

Modelos Regex

regex
# ISO 8601 / RFC 3339 with offset
^\\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 (no year/offset, add externally)
^[A-Za-z]{3}\\s+\\d{1,2}\\s\\d{2}:\\d{2}:\\d{2}\\s.+$

Ordenação, indexação e armazenamento

  • Normalize para milissegundos UTC (ou superior) antes do armazenamento; mantenha o texto bruto se precisar analisar novamente.
  • Use filtros de intervalo (WHERE ocorreu_at_ms >= ... AND ocorreu_at_ms < ...) para manter a indexação amigável.
  • Se a ingestão misturar formatos, adicione uma coluna validated_at e coloque falhas em quarentena.
  • Particionamento por data para logs de alto volume; evite lançar em filtros.

Armadilhas comuns (e soluções)

  • Fuso horário ausente: padrão é UTC; o analisador de log deve adicionar o fuso horário do host de origem, se conhecido.
  • Intervalos/sobreposições de horário de verão: analise primeiro como UTC; localize apenas ao exibir.
  • Precisão mista: segundos e milissegundos misturados em uma coluna; impor comprimento na ingestão.
  • Syslog ano ausente: anexe o ano atual durante a análise e valide o intervalo.
  • Filtros agrupados por função: evite DATE(occurred_at) em WHERE; use colunas de data geradas.

Perguntas frequentes

  • Como detectar milissegundos versus segundos? Verifique o comprimento dos dígitos: 10=segundos, 13=ms, 16=µs, 19=ns.
  • E se o fuso horário estiver faltando? Suponha o UTC ou injete o fuso horário do host conhecido antes de analisar.
  • Por que os painéis estão fora de ordem? Provavelmente precisão mista ou classificação de strings; normalize para UTC numérico e índice.
  • Como limpar logs históricos em lote? Use uma tabela intermediária, analise + valide e mova apenas linhas limpas; veja ferramentas relacionadas.

Ferramentas e guias relacionados