Guide

Zeitstempel in Protokollen: Fehler analysieren, normalisieren und beheben

Warum diese Seite

Protokolle kommen mit unterschiedlichen Formaten, Zeitzonen und Genauigkeit an. Ein fehlerhafter Zeitstempel beeinträchtigt die Bestellung, Dashboards und Zeitpläne für Vorfälle. Dieser Leitfaden bietet Ihnen Schnellprüfungen, Regex-Vorlagen und Sprachausschnitte, um Protokollzeiten schnell zu normalisieren – und verweist Sie auf Konverter, wenn Sie Randfälle überprüfen müssen.

Schnelle Diagnose-Checkliste

  • Identifizieren Sie Format: ISO 8601/RFC 3339, Apache/Nginx ([dd/Mon/yyyy:HH:mm:ss Z]), Syslog (MMM d HH:mm:ss) oder benutzerdefiniert.
  • Prüfen Sie Zeitzone: expliziter Offset (+0800, Z) vs. impliziter lokaler. Standardmäßig UTC, falls fehlend.
  • Überprüfen Sie die Präzision: Sekunden vs. Millisekunden vs. Mikrosekunden; sorgen für eine gleichmäßige Länge.
  • Behandeln Sie DST-Kanten: Zuerst in UTC analysieren; Vermeiden Sie naives lokales Parsen.
  • Range Guards erzwingen: unplausible Jahreszahlen ablehnen (z. B. <2000 oder >2100).

Gängige Protokollformate (Vorlagen)

  • ISO 8601 / RFC 3339: 2024-12-01T10:15:30Z, 2024-12-01T18:15:30+08:00
  • Apache/Nginx: [01/Dec/2024:10:15:30 +0000]
  • Syslog: Dec 1 10:15:30 hostname app[123]: message (kein Jahr/Offset)
  • Benutzerdefinierte Zahl: 1701425730 (Sekunden), 1701425730000 (ms)

Normalisierungsschritte

  1. Format und Zeitzone erkennen. Wenn es fehlt, nehmen Sie UTC an.
  2. Analysieren Sie es in eine bewusste Datums- und Uhrzeitangabe (UTC).
  3. Bei numerischer Epoche: Skalierung nach Länge (10 s/13 ms/16 µs/19 ns).
  4. Speichern Sie in einer kanonischen Spalte (z. B. UTC-Millis) und behalten Sie optional die Rohzeichenfolge bei.
  5. Bereich validieren; Löschen oder markieren Sie Ausreißer vor der Indizierung.

Codeausschnitte

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");
}

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 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")

Regex-Vorlagen

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.+$

Ordnen, Indexieren und Speichern

  • Vor der Speicherung auf UTC-Millisekunden (oder höher) normalisieren; Behalten Sie den Rohtext bei, wenn Sie eine Analyse durchführen müssen.
  • Verwenden Sie Bereichsfilter (WHERE occurred_at_ms >= ... AND occurred_at_ms < ...), um indexfreundlich zu bleiben.
    – Wenn bei der Aufnahme Formate gemischt werden, fügen Sie eine validated_at-Spalte hinzu und isolieren Sie Fehler.
  • Partitionierung nach Datum für große Protokollmengen; Vermeiden Sie das Eingießen von Filtern.

Häufige Fallstricke (und Lösungen)

  • Fehlende Zeitzone: Standardmäßig UTC; Der Protokollparser sollte die Zeitzone des Quellhosts hinzufügen, sofern bekannt.
  • DST-Lücken/Überlappungen: zuerst als UTC analysieren; Nur beim Anzeigen lokalisieren.
  • Gemischte Genauigkeit: Sekunden und Millisekunden gemischt in einer Spalte; Erzwingen Sie die Länge der Einnahme.
  • Syslog fehlt Jahr: Beim Parsen das aktuelle Jahr anhängen und dann den Bereich validieren.
  • Funktionsumhüllte Filter: DATE(occurred_at) in WHERE vermeiden; Verwenden Sie generierte Datumsspalten.

FAQ

  • Wie erkenne ich Millisekunden gegenüber Sekunden? Prüfziffernlänge: 10=Sekunden, 13=ms, 16=µs, 19=ns.
  • Was passiert, wenn die Zeitzone fehlt? Nehmen Sie UTC an oder fügen Sie vor dem Parsen die bekannte Host-Zeitzone ein.
  • Warum sind Dashboards nicht in Ordnung? Wahrscheinlich gemischte Präzision oder String-Sortierung; auf numerische UTC und Index normalisieren.
  • Wie werden historische Protokolle stapelweise bereinigt? Verwenden Sie eine Staging-Tabelle, analysieren und validieren Sie sie und verschieben Sie dann nur bereinigte Zeilen. siehe verwandte Tools.

Verwandte Tools und Anleitungen