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)
정규화 단계
- 형식과 시간대를 감지합니다. 누락된 경우 UTC로 가정합니다.
- 인식 날짜/시간(UTC)으로 구문 분석합니다.
- 숫자 에포크인 경우: 길이별로 스케일링합니다(10s/13ms/16μs/19ns).
- 표준 열(예: UTC 밀리초)에 저장하고 선택적으로 원시 문자열을 유지합니다.
- 범위를 확인합니다. 인덱싱하기 전에 이상값을 삭제하거나 플래그를 지정하세요.
코드 조각
자바스크립트/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 및 인덱스로 정규화합니다.
- 기록 로그를 일괄 정리하는 방법은 무엇입니까? 준비 테이블을 사용하고 구문 분석하고 검증한 다음 깨끗한 행만 이동합니다. 관련 도구를 참조하세요.