Guide

로그의 타임스탬프: 오류 구문 분석, 정규화 및 수정

이 페이지가 필요한 이유

로그는 혼합된 형식, 시간대 및 정밀도로 도착합니다. 하나의 잘못된 타임스탬프가 주문, 대시보드 및 사고 타임라인을 깨뜨립니다. 이 가이드는 빠른 검사, 정규식 템플릿, 언어 조각을 제공하여 로그 시간을 빠르게 정규화하고 극단적인 사례를 확인해야 할 때 변환기를 알려줍니다.

빠른 진단 체크리스트

  • 형식 식별: ISO 8601/RFC 3339, Apache/Nginx([dd/Mon/yyyy:HH:mm:ss Z]), syslog(MMM d HH:mm:ss) 또는 사용자 정의.
  • 시간대 확인: 명시적 오프셋(+0800, Z)과 암시적 로컬. 누락된 경우 기본값은 UTC입니다.
  • 정밀도 확인: 초, 밀리초, 마이크로초; 일관된 길이를 보장합니다.
  • DST 가장자리 처리: 먼저 UTC로 구문 분석합니다. 순진한 로컬 구문 분석을 피하십시오.
  • 범위 가드 시행: 타당하지 않은 연도(예: <2000 또는 >2100)를 거부합니다.

일반적인 로그 형식(템플릿)

  • 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 호스트 이름 앱[123]: 메시지(연도/오프셋 없음)
  • 맞춤 숫자: 1701425730(초), 1701425730000(ms)

정규화 단계

  1. 형식과 시간대를 감지합니다. 누락된 경우 UTC로 가정합니다.
  2. 인식 날짜/시간(UTC)으로 구문 분석합니다.
  3. 숫자 에포크인 경우: 길이별로 스케일링합니다(10s/13ms/16μs/19ns).
  4. 표준 열(예: UTC 밀리초)에 저장하고 선택적으로 원시 문자열을 유지합니다.
  5. 범위를 확인합니다. 인덱싱하기 전에 이상값을 삭제하거나 플래그를 지정하세요.

코드 조각

자바스크립트/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");
}

파이썬

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

순서 지정, 색인 생성 및 저장

  • 저장하기 전에 UTC 밀리초(또는 그 이상)로 정규화합니다. 재분석이 필요한 경우 원시 텍스트를 유지하십시오.
  • 인덱스 친화적인 상태를 유지하려면 범위 필터(WHERE 발생_at_ms >= ... AND 발생_at_ms < ...)를 사용하세요.
  • 수집에 형식이 혼합된 경우 validated_at 열과 격리 실패를 추가하세요.
  • 대용량 로그에 대한 날짜별 파티션; 필터에 캐스팅하지 마세요.

일반적인 함정(및 수정 사항)

  • 시간대 누락: 기본값은 UTC입니다. 알려진 경우 로그 파서는 소스 호스트 시간대를 추가해야 합니다.
  • DST 간격/겹침: 먼저 UTC로 구문 분석합니다. 표시할 때만 현지화하세요.
  • 혼합 정밀도: 한 열에 초와 밀리초가 혼합되어 있습니다. 섭취 시 길이를 적용합니다.
  • Syslog 누락 연도: 구문 분석 중에 현재 연도를 추가한 다음 범위를 검증합니다.
  • 함수 래핑 필터: WHERE에서 DATE(occurred_at)를 피하세요. 생성된 날짜 열을 사용합니다.

FAQ

  • 밀리초와 초를 어떻게 감지합니까? 숫자 길이를 확인하세요: 10=초, 13=ms, 16=μs, 19=ns.
  • 시간대가 누락된 경우 어떻게 해야 합니까? UTC를 가정하거나 구문 분석하기 전에 알려진 호스트 시간대를 삽입합니다.
  • 대시보드가 ​​순서가 잘못된 이유는 무엇입니까? 혼합 정밀도 또는 문자열 정렬 가능성이 있습니다. 숫자 UTC 및 인덱스로 정규화합니다.
  • 기록 로그를 일괄 정리하는 방법은 무엇입니까? 준비 테이블을 사용하고 구문 분석하고 검증한 다음 깨끗한 행만 이동합니다. 관련 도구를 참조하세요.

관련 도구 및 가이드