PEP 830 – 예외 및 트레이스백에 타임스탬프 추가
- Author:
- Gregory P. Smith <greg at krypto.org>
- Discussions-To:
- Discourse thread
- Status:
- Draft
- Type:
- Standards Track
- Created:
- 15-Mar-2026
- Python-Version:
- 3.16
- Post-History:
- 12-Apr-2026, 18-Apr-2026
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
이 PEP는 관찰 가능한 오버헤드 없이 예외가 인스턴스화된 시점을 기록하는 선택적 __timestamp_ns__속성을 BaseException에 추가합니다. 환경 변수 또는 명령줄 플래그를 통해 활성화하면, 형식이 지정된 트레이스백에 예외 메시지와 함께 이 타임스탬프가 표시됩니다.
동기
예외 그룹(PEP 654)이 도입됨에 따라 Python 프로그램은 이제 서로 관련 없는 여러 예외를 동시에 전파할 수 있습니다. 이러한 예외를 디버깅하거나 외부 로그 및 메트릭과 예외를 연관시킬 때는 각 예외가 언제 발생했는지 아는 것이 무엇이 발생했는지 아는 것만큼 중요한 경우가 많으며, 이는 프로덕션에서 문제를 진단할 때 흔히 겪는 어려움입니다.
현재 이 정보를 얻을 수 있는 표준 방법은 없습니다. Python 작성자는 예외 메시지에 직접 타이밍 정보를 추가하거나 로깅 프레임워크에 의존해야 하는데, 이는 비용이 많이 들 수 있고 일관성 없이 수행되며 오류가 발생하기 쉽습니다.
여러 백엔드에서 데이터를 동시에 가져오고 빠르게 실패하는 대신 모든 실패를 보고하는 비동기 서비스를 생각해 보십시오. 그 결과 생성되는 ExceptionGroup에는 제출 순서대로 모든 오류가 포함되지만, 각 오류가 언제 발생했는지는 나타나지 않습니다.:
import asyncio
async def fetch_user(uid):
await asyncio.sleep(0.5)
raise ConnectionError(f"User service timeout for {uid}")
async def fetch_orders(uid):
await asyncio.sleep(0.1)
raise ValueError(f"Invalid user_id format: {uid}")
async def fetch_recommendations(uid):
await asyncio.sleep(2.3)
raise TimeoutError("Recommendation service timeout")
async def get_dashboard(uid):
results = await asyncio.gather(
fetch_user(uid),
fetch_orders(uid),
fetch_recommendations(uid),
return_exceptions=True,
)
errors = [r for r in results if isinstance(r, Exception)]
if errors:
raise ExceptionGroup("dashboard fetch failed", errors)
asyncio.run(get_dashboard("usr_12@34"))
PYTHON_TRACEBACK_TIMESTAMPS=iso를 사용하면 출력이 다음과 같이 됩니다.
+ Exception Group Traceback (most recent call last):
| File "service.py", line 26, in <module>
| asyncio.run(get_dashboard("usr_12@34"))
| ...
| File "service.py", line 24, in get_dashboard
| raise ExceptionGroup("dashboard fetch failed", errors)
| ExceptionGroup: dashboard fetch failed (3 sub-exceptions) <@2026-04-19T07:24:31.102431Z>
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "service.py", line 5, in fetch_user
| raise ConnectionError(f"User service timeout for {uid}")
| ConnectionError: User service timeout for usr_12@34 <@2026-04-19T07:24:29.300461Z>
+---------------- 2 ----------------
| Traceback (most recent call last):
| File "service.py", line 9, in fetch_orders
| raise ValueError(f"Invalid user_id format: {uid}")
| ValueError: Invalid user_id format: usr_12@34 <@2026-04-19T07:24:28.899918Z>
+---------------- 3 ----------------
| Traceback (most recent call last):
| File "service.py", line 13, in fetch_recommendations
| raise TimeoutError("Recommendation service timeout")
| TimeoutError: Recommendation service timeout <@2026-04-19T07:24:31.102394Z>
+------------------------------------
하위 예외는 제출 순서대로 나열되지만, 타임스탬프를 통해 순서 검증이 실제로 먼저 실패했고(28.899초), 사용자 서비스가 0.5초 후에 실패했으며(29.300초), 추천 서비스가 2.3초 후 마지막으로 실패했다는 사실(31.102초)이 드러납니다. 또한 이를 메트릭 대시보드, 로드 밸런서 로그, 다른 서비스의 트레이스 또는 프로그램 자체의 로그와 연관시켜 전체 상황을 파악할 수 있습니다.
사양
예외 타임스탬프 속성
새로운 읽기/쓰기 속성 __timestamp_ns__가 BaseException에 추가됩니다. 이 속성은 Unix epoch 이후 UTC 기준 나노초를 저장하며(time.time_ns()와 동일한 의미 및 정밀도), 멤버 디스크립터를 통해 C int64_t로 노출됩니다. 타임스탬프가 비활성화되었거나, 제어 흐름 예외인 경우(아래 참조), 또는 시계 읽기에 실패한 경우 값은 0입니다.
MemoryError와 같이 내부 프리 리스트에서 재사용되는 예외 인스턴스는 원래 할당될 때가 아니라 전달될 때 타임스탬프가 기록됩니다. 인터프리터의 최후 수단인 정적 MemoryError싱글턴은 __timestamp_ns__에 0을 유지합니다.
제어 흐름 예외
정상적인 제어 흐름에 성능 영향을 주지 않기 위해, 기능이 활성화된 경우에도 StopIteration 또는 StopAsyncIteration에 대한 타임스탬프는 수집되지 않습니다. 이러한 예외는 반복 중 매우 높은 빈도로 발생하므로, 검사에는 무시할 수 있을 정도의 오버헤드를 위해 isinstance가 아닌 C 타입 포인터 동일성 검사가 사용됩니다.
hasattr, 세 인자를 사용하는 getattr, dict.get, dict.setdefault 및 __missing__디스패치처럼 예외를 제어 흐름으로 사용하는 것으로 설명되기도 하는 다른 관용구는 특별한 처리가 필요하지 않습니다. CPython C 내부의 핫 패스는 이미 예외 객체를 인스턴스화하지 않고 “찾을 수 없음”을 반환합니다.
구성
이 기능은 CPython의 두 가지 표준 메커니즘을 통해 활성화됩니다.
PYTHON_TRACEBACK_TIMESTAMPS환경 변수- 나노초 정밀도의 십진수 타임스탬프를 사용하려면
ns또는1로 설정하고, ISO 8601 UTC 형식을 사용하려면iso로 설정하십시오. 비어 있거나 설정되지 않았거나0으로 설정하면 타임스탬프가 비활성화됩니다(기본값). -X traceback_timestamps=<format>명령줄 옵션- 동일한 값을 허용합니다. 환경 변수보다 우선합니다.
-X traceback_timestamps가=<format>값 없이 지정되면, 암시적인=1(나노초 정밀도 형식)으로 동작합니다.
다른 CPython 구성 동작과 일관되게, 잘못된 환경 변수 값은 조용히 무시되지만 잘못된 -X 플래그 값은 오류입니다.
PyConfig에 새로운 traceback_timestamps 필드가 선택한 형식을 저장하며, sys.flags.traceback_timestamps로 액세스할 수 있습니다.
표시 형식
타임스탬프는 <@timestamp> 형식을 사용하여 트레이스백의 예외 메시지 줄에 추가됩니다. 이는 형식이 지정된 트레이스백 출력에만 영향을 미치며, str(exc)와 repr(exc)는 변경되지 않습니다. iso를 사용한 예시입니다.
Traceback (most recent call last):
File "<stdin>", line 3, in california_raisin
raise RuntimeError("not enough sunshine")
RuntimeError: not enough sunshine <@2026-04-12T18:07:30.346914Z>
색상 출력이 활성화된 경우, 예외 메시지와 시각적으로 구분되도록 타임스탬프를 흐린 색상으로 표시합니다.
ns 형식은 에포크 이후의 초를 소수점 이하 아홉 자리로 표시합니다. 예를 들어 <@1776017178.687320256>입니다. iso 형식은 마이크로초 단위의 정밀도로 표시하며, 기본 __timestamp_ns__ 값은 표시 형식과 관계없이 항상 나노초 정밀도입니다.
트레이스백 모듈 업데이트
TracebackException 및 공개 형식 지정 함수(print_exc, print_exception, format_exception, format_exception_only)에 timestamps 키워드 인자(기본값 None)가 추가됩니다.
None- 전역 구성을 따릅니다. 기능이 활성화된 경우에만 구성된 형식을 사용하여 타임스탬프를 표시합니다.
False- 기능이 활성화되어 있어도 타임스탬프를 표시하지 않습니다.
True- 전역 구성과 관계없이 0이 아닌
__timestamp_ns__를 표시하며, 형식이 설정되어 있으면 구성된 형식을 사용하고 그렇지 않으면ns를 사용합니다.
0이 아닌 값만 표시됩니다. 수집이 비활성화된 경우에도, 수집이 활성화된 프로세스에서 언피클된 인스턴스에서 또는 __timestamp_ns__가 직접 할당된 경우에 0이 아닌 값이 발생할 수 있습니다.
형식이 지정된 트레이스백 문자열에서 <@...> 타임스탬프 접미사를 제거하는 새 유틸리티 함수 traceback.strip_exc_timestamps(text)를 제공합니다. 트레이스백 출력을 있는 그대로 비교하는 모든 작업에 유용합니다.
독테스트 업데이트
새로운 doctest.IGNORE_EXCEPTION_TIMESTAMPS 옵션 플래그가 추가됩니다. 활성화하면 독테스트 출력 검사기가 비교 전에 실제 출력에서 타임스탬프를 제거하므로, 예외를 생성하는 독테스트가 타임스탬프 활성화 여부와 관계없이 통과합니다.
서드파티 프로젝트는 타임스탬프를 활성화한 상태에서 테스트 실행을 지원할 것으로 기대되지 않으며, 많은 프로젝트가 이를 원할 것이라고도 생각하지 않습니다.
근거
타임스탬프는 BaseException C 구조체의 단일 int64_t 필드에 저장되며, Unix epoch 이후의 나노초를 기록합니다. 이 설계는 예외 노트(PEP 678)를 사용하는 대신 선택되었습니다. 구조체 필드는 값이 채워지지 않을 때 비용이 들지 않고, 예외 발생 시 문자열 및 리스트 객체를 생성하지 않으며, 모든 형식 지정 작업을 트레이스백 렌더링 시점까지 지연하기 때문입니다. 이 기능은 전적으로 선택 사항이며 예외 처리 의미 체계를 변경하지 않습니다.
정보를 전달하는 수단으로 예외 노트를 사용하는 것은 성능 오버헤드와 명시적인 목적의 부재로 인해 실현 불가능하다고 판단되었습니다. 노트는 훌륭하지만, 훨씬 다른 사용 사례를 위해 설계되었으며, 모든 Exception 인스턴스화 시 캡처되는 데이터를 수집하는 방법으로 설계된 것은 아닙니다.
인스턴스화 시점과 발생 시점
타임스탬프는 예외가 발생한 시점이 아니라 예외 객체가 생성된 시점에 기록됩니다. 압도적으로 일반적인 raise SomeError(...) 형식에서는 이 두 시점이 동일합니다. 두 시점이 다른 경우에는 일반적으로 인스턴스화 시점이 더 유용한 값입니다. 단독 raise 또는 raise exc 재발생은 오류가 마지막으로 다시 발생한 시점이 아니라 처음 발생한 시점을 보존하며, 예외를 생성하고 누적한 후 나중에 이를 ExceptionGroup으로 감싸는 코드는 각 오류가 감지된 시점을 얻습니다.
인스턴스화 시점은 구현 지점으로서도 더 깔끔합니다. 예외 인스턴스화는 BaseException.__init__ 및 해당 vectorcall 경로를 통해 집중 처리됩니다. 예외 발생에는 이에 상응하는 단일 집중 처리 지점이 없습니다. 최하위 수준의 _PyErr_SetRaisedException은 스레드의 예외 상태를 일상적으로 저장하고 복원할 때도 호출되며, Python의 raise 문, C의 PyErr_SetObject, 직접적인 PyErr_SetRaisedException 호출은 그 위에서 서로 구별되는 코드 경로입니다.
Open Issues 아래의 Recording Time of First Raise를 참조하십시오.
성능 측정
pyperformance 모음은 병합 기준, 기능을 비활성화한 PR 브랜치, 그리고 us 및 iso 모드에서 기능을 활성화한 PR 브랜치에서 실행되었습니다. 이러한 측정은 이 PEP의 원래 버전에 부합하는 참조 구현을 대상으로 수행되었으며, Change History에 기록된 수정 이전에 이루어졌습니다. 해당 수정은 표시 계층만 단순화한 것이므로 성능 차이는 예상되지 않습니다.
유의미한 성능 변화는 관찰되지 않았습니다. 안정적으로 재현할 수 없고 벤치마킹 설정의 잡음 임계값보다 낮은 1~2%의 간헐적인 변동만 있었습니다.
제어 흐름의 특수 사례를 검증하기 위해 Objects/exceptions.c에서 StopIteration / StopAsyncIteration 제외를 담은 두 줄을 제거한 상태로도 모음을 실행했습니다. 단 하나의 벤치마크인 async_generators에서만 회귀가 나타났으며, 안정적으로 약 10% 느리게 실행되었습니다. 이는 대부분의 애플리케이션 동작을 반영하지 않는 사실상 마이크로벤치마크일 가능성이 높지만, 해당 최적화의 가치를 보여 줍니다.
벤치마크는 다음과 같은 명령을 사용하여 configure --enable-optimizations 빌드에서 실행되었습니다.
pyperformance run -p baseline-3a7df632c96/build/python -o baseline-eopt.json
pyperformance run -p traceback-timestamps/build/python -o traceback-timestamps-default-eopt.json
PYTHON_TRACEBACK_TIMESTAMPS=1 pyperformance run --inherit-environ PYTHON_TRACEBACK_TIMESTAMPS -p traceback-timestamps/build/python -o traceback-timestamps-env=1-eopt.json
PYTHON_TRACEBACK_TIMESTAMPS=iso pyperformance run --inherit-environ PYTHON_TRACEBACK_TIMESTAMPS -p traceback-timestamps/build/python -o traceback-timestamps/Results.silencio/traceback-timestamps-env=iso-eopt.json
PYTHON_TRACEBACK_TIMESTAMPS=1 pyperformance run --inherit-environ PYTHON_TRACEBACK_TIMESTAMPS -p traceback-timestamps-without-StopIter-cases/build/python -o traceback-timestamps/Results.silencio/traceback-timestamps-without-StopIter-cases-env=1-eopt.json
하위 호환성
이 기능은 기본적으로 비활성화되어 있으며 기존 예외 처리 코드에 영향을 주지 않습니다. __timestamp_ns__ 속성은 BaseException 인스턴스에서 항상 읽을 수 있으며, 타임스탬프를 수집하지 않을 때는 0을 반환합니다.
타임스탬프가 비활성화되면 예외는 기존의 2-튜플 형식인 (type, args)로 피클됩니다. 0이 아닌 타임스탬프가 있으면 예외는 상태 딕셔너리에 __timestamp_ns__를 포함하는 (type, args, state_dict) 형식으로 피클됩니다. 이전 Python 버전은 __setstate__를 통해 이를 올바르게 언피클합니다. 항상 3-튜플 형식(타임스탬프가 0인 형식)을 출력하면 로직이 단순해지지만, 기능이 꺼져 있을 때 피클 출력이 바이트 단위로 동일하게 유지되도록 하고 일반적인 경우에 성능 영향을 주지 않기 위해 이를 피했습니다. 일반적으로 더 단순한 코드가 바람직하지만, 모든 예외의 피클 크기가 기본 동작으로 증가하게 하는 것이 더 큰 위험이라고 판단되었습니다.
피클된 예외 예제
트레이스백 타임스탬프 수집이 활성화된 경우:
$ build/python -X traceback_timestamps=iso -c 'import pickle; print(pickle.dumps(RuntimeError("pep-830"), protocol=pickle.HIGHEST_PROTOCOL))'
b'\x80\x05\x95L\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x0cRuntimeError\x94\x93\x94\x8c\x07pep-830\x94\x85\x94R\x94}\x94\x8c\x10__timestamp_ns__\x94\x8a\x08\xf4\xd8\x94`\x15\xaf\xa5\x18sb.'
StopIteration에 대한 특수 사례로 인해 타임스탬프 데이터가 포함된 딕셔너리를 전달하지 않습니다:
$ build/python -X traceback_timestamps=iso -c 'import pickle; print(pickle.dumps(StopIteration("pep-830"), protocol=pickle.HIGHEST_PROTOCOL))'
b'\x80\x05\x95,\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\rStopIteration\x94\x93\x94\x8c\x07pep-830\x94\x85\x94R\x94.'
기능이 비활성화된 경우(기본값) 예외에도 타임스탬프가 포함되지 않습니다:
$ build/python -X traceback_timestamps=0 -c 'import pickle; print(pickle.dumps(RuntimeError("pep-830"), protocol=pickle.HIGHEST_PROTOCOL))'
b'\x80\x05\x95+\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x0cRuntimeError\x94\x93\x94\x8c\x07pep-830\x94\x85\x94R\x94.'
이는 Python 3.13이 생성하는 결과와 일치합니다:
$ python3.13 -c 'import pickle; print(pickle.dumps(RuntimeError("pep-830"), protocol=pickle.HIGHEST_PROTOCOL))'
b'\x80\x05\x95+\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x0cRuntimeError\x94\x93\x94\x8c\x07pep-830\x94\x85\x94R\x94.'
유지보수 부담
__timestamp_ns__ 필드는 구성과 관계없이 모든 예외 객체에 존재하는 BaseException C 구조체의 단일 int64_t 필드입니다. 수집 코드는 보호된 clock_gettime 호출이며, 서식 지정 코드는 트레이스백 표시 시점에만 실행됩니다. 둘 다 작고 독립적입니다.
지속적으로 발생하는 주요 비용은 테스트 모음에 있습니다. 트레이스백 출력을 문자 그대로 비교하는 테스트는 선택적 타임스탬프 접미사를 고려해야 합니다. 이를 위해 두 가지 헬퍼가 제공됩니다:
traceback.strip_exc_timestamps(text)는 서식이 지정된 트레이스백 문자열에서<@...>접미사를 제거합니다.test.support.force_no_traceback_timestamps및_test_class접미사가 붙은 변형은 테스트 또는TestCase클래스가 실행되는 동안 타임스탬프 수집을 비활성화하는 데코레이터입니다.
트레이스백 관련 테스트를 제외하면, ~1230개 테스트 파일 중 대략 14개(약 1%)에서 이러한 헬퍼 중 하나가 필요했으며, 일반적으로 stderr를 캡처하고 예상 트레이스백 출력과 비교하는 테스트였습니다(예: test_logging, test_repl, test_wsgiref, test_threading). 이 패턴은 트레이스백의 ANSI 색상 코드에 대해 force_not_colorized를 사용할 때와 동일한 접근 방식을 따릅니다.
커버리지를 유지하기 위해 몇몇 GitHub Actions 실행에서 타임스탬프를 활성화하는 CPython 자체 CI를 제외하면, 대부분의 프로젝트는 테스트 모음을 실행하는 동안 이 기능을 활성화하지 않을 가능성이 높습니다.
보안 영향
없습니다. 이 기능은 선택적으로 활성화되며 기본적으로 비활성화됩니다.
이 기능을 가르치는 방법
__timestamp_ns__ 속성과 구성 옵션은 exceptions 모듈 레퍼런스, traceback 모듈 레퍼런스 및 명령줄 인터페이스 문서에 문서화됩니다.
이는 강력한 기능으로, 기본적으로 비활성화되어 있으며 명시적으로 활성화하지 않는 한 표시되지 않습니다. 입문 자료에서 다룰 필요는 없습니다.
참조 구현
참조 구현은 CPython PR #129337입니다.
거부된 아이디어
예외 노트 사용
타임스탬프를 첨부하기 위해 PEP 678의 .add_note()를 사용하는 것은 여러 가지 이유로 거부되었습니다. 노트는 예외 발생 시점에 문자열 및 리스트 객체를 생성해야 하므로, 타임스탬프가 표시되지 않는 경우에도 오버헤드가 발생합니다. 예외를 잡을 때 추가된 노트는 발생 시점이 아니라 포착 시점을 반영하며, 비동기 코드에서는 이 차이가 상당할 수 있습니다. 모든 예외가 포착되는 것은 아니므로(일부는 최상위 수준으로 전파되거나 직접 로깅됨), 포착 시점의 노트는 일관되지 않게 적용됩니다. 구조체 필드는 소스에서 타임스탬프를 포착하고 모든 서식 지정은 표시 시점까지 지연합니다.
sys.excepthook 사용
sys.excepthook은 포착되지 않은 예외가 최상위 수준에 도달했을 때, 예외가 생성된 시점이 아니라 표시 시점에만 실행됩니다. 위의 동기를 제공한 예제에서는 모든 작업이 완료된 후 결과로 생성된 ExceptionGroup에 대해 훅이 한 번 호출되므로 모든 하위 예외가 동일한 타임스탬프를 받게 됩니다. 포착되어 로깅된 예외는 훅에 전혀 도달하지 않습니다.
sys.monitoring 사용
인터프리터를 변경하지 않고도 PEP 669를 기반으로 서드파티 애드온으로 동등한 기능을 원칙적으로 구축할 수 있습니다. 즉, 시계를 읽고 예외에 __timestamp_ns__ 속성을 설정하거나 add_note()를 호출하는 C 구현 호출 가능 객체를 sys.monitoring.events.RAISE에 등록하는 방식입니다. STOP_ITERATION과 RERAISE는 별도의 이벤트이므로 RAISE만 구독하면 이터레이터 핫 패스를 피하면서 재발생 시 원래 타임스탬프도 자연스럽게 보존합니다.
이 방식은 구조체 필드 접근 방식보다 비용이 클 것으로 예상됩니다. RAISE는 예외마다 한 번이 아니라 언와인드 중 각 Python 프레임마다 한 번씩 발생하므로 콜백이 작업에 필요한 것보다 더 자주 실행되며, 각 호출은 직접 호출되지 않고 vectorcall을 통해 디스패치됩니다. 구조체 필드가 없으면 값을 저장할 때 예외 인스턴스의 __dict__와 타임스탬프를 위한 PyLongObject가 할당되며, add_note()의 경우에는 딕셔너리와 __notes__ 리스트 및 형식화된 문자열이 추가로 할당됩니다. 제한된 모니터링 도구 ID 중 하나도 소비됩니다. add_note() 변형은 추가 통합 없이 트레이스백에 표시됩니다. __timestamp_ns__ 변형은 다른 코드에서 사용 중일 수 있는 sys.excepthook 싱글턴이나, 값을 표시하기 위한 traceback 모듈의 몽키 패치도 필요합니다. 프로그램에서 예외가 발생할 때마다 리스트와 문자열, 그리고 잠재적으로 인스턴스 딕셔너리까지 할당하는 것은 오버헤드 측면에서 지나치다고 판단되어 이 방식은 시도되지 않았습니다.
중간 방안으로는 수집에만 sys.monitoring을 사용하고, 이 PEP의 int64_t 구조체 필드를 BaseException에 유지하면서 0이 아닌 __timestamp_ns__를 traceback 모듈에서 표시하도록 할 수 있습니다. 이렇게 하면 BaseException 생성자에서 조건부 시계 읽기가 제거되어 기능이 꺼져 있을 때는 약간의 비용을 절약할 수 있지만, 기능이 켜져 있을 때는 오버헤드가 눈에 띄게 증가합니다.
항상 수집과 항상 표시
Collecting 타임스탬프(인스턴스화 중 clock_gettime 호출)와 형식화된 트레이스백에 이를 displaying 하는 것은 별개의 문제입니다.
항상 표시하는 방안은 대부분의 사용자에게 필요하지 않은 잡음을 추가하므로 채택하지 않았습니다. 표시가 비활성화된 경우에도 항상 수집하는 것은 구조체에 int64_t 필드가 어쨌든 존재하므로 비용이 적지만, 수집하지 않으면 기능이 꺼져 있을 때 성능에 영향을 줄 가능성을 피할 수 있으며 표시되지 않을 타임스탬프를 수집할 현재 이유도 없습니다. 트레이스백 표시와 무관하게 예외 타임스탬프에 프로그래밍 방식으로 접근하는 것이 유용해진다면 이 방안을 다시 검토할 수 있습니다.
런타임 API
이는 라이브러리나 애플리케이션 코드가 아니라 프로세스를 시작하는 주체가 설정하도록 의도된 연산자 수준의 설정입니다. 프로세스 전체 상태에 대한 런타임 토글을 노출하면 프로그램의 여러 부분이 그 값을 두고 서로 충돌하게 될 수 있으므로, 시작 시 고정해 두면 이를 방지할 수 있습니다. 단순하게 유지하기 위해 현재는 제외했습니다. 런타임 구성 가능성에 대한 수요가 있다면 나중에 이를 추가하는 데 방해되는 요소는 없습니다.
트레이스백 헤더 줄의 타임스탬프
Traceback (most recent call last): 헤더에 타임스탬프를 배치하자는 제안은 Type: message 줄을 파싱하는 도구를 덜 방해한다는 이유에서 나왔습니다. __cause__ 또는 __context__ 체인의 각 연결과 ExceptionGroup의 각 하위 예외에는 각각 고유한 헤더가 있으므로, 이는 일반적인 경우를 다룹니다. 그러나 헤더는 예외에 __traceback__가 있을 때만 출력됩니다. 구성되어 직접 첨부된 예외는 헤더 없이 Type: message만 표시됩니다. 표준 라이브러리 자체도 이렇게 합니다. concurrent.futures.ProcessPoolExecutor와 multiprocessing.Pool은 프로세스 경계를 넘어 작업자의 형식화된 트레이스백을 전달하기 위해 구성된 _RemoteTraceback를 __cause__로 첨부하며, 이는 헤더 없이 표시됩니다. 메시지 줄에 덧붙이는 것만이 표시되는 모든 예외에 대해 렌더링을 보장하는 배치 위치입니다.
읽기 순서에 관한 논거도 있습니다. 오류를 찾기 위해 로그를 살펴볼 때 사람들은 일반적으로 예외 유형을 검색하여 Type: message 줄에 도달한 다음 프레임을 따라 위쪽으로 읽습니다. 타임스탬프를 해당 줄에 배치하면 흔히 마지막에 읽는 블록의 맨 위가 아니라 시선이 처음 도달하는 위치에 놓이게 됩니다.
더 설명적인 timestamps 매개변수 이름
초기 초안에서는 traceback 서식 매개변수를 no_timestamp로 명명했는데, 이 불리언 값은 이중 부정처럼 읽혔습니다. allow_timestamps 또는 show_timestamp와 같은 더 긴 대안도 고려되었습니다. 짧은 긍정형인 timestamps를 선택했으며, 이는 None로 기본 설정되는 세 상태 값입니다(전역 설정을 따름). 독스트링과 문서에서는 정확한 동작을 다룹니다.
사용자 지정 타임스탬프 형식
사용자가 정의하는 형식 문자열을 허용하면 복잡성이 크게 증가합니다. 두 가지 내장 형식(ns, iso)은 일반적인 요구 사항을 충족합니다. 프로그래밍 방식으로 사용하기 위한 십진 초와 외부 시스템과의 상관 관계를 위한 ISO 8601을 제공합니다.
구성 가능한 제어 흐름 예외 집합
건너뛸 추가 예외를 사용자가 등록하도록 허용하는 방안은 받아들여지지 않았습니다. 제외 검사는 예외 생성의 핫 패스에서 실행되며, 속도를 위해 C 타입 포인터 동일성을 사용합니다. 구성 가능한 집합을 지원하려면 isinstance 검사(너무 느리고 MRO를 순회함) 또는 타입 포인터의 해시 집합(효용이 불분명한 복잡성)이 필요합니다. StopIteration과 StopAsyncIteration은 clock_gettime의 비용을 측정할 수 있을 정도의 빈도로 발생하는 유일한 예외입니다. 실용적인 필요성이 발생하면 추가 제외 항목을 효율적으로 등록하는 API를 후속 개선 사항으로 추가할 수 있습니다.
밀리초 정밀도
time.time_ns()와 일치하고 고빈도 예외 상황에 충분한 해상도를 제공하기 위해 밀리초보다 나노초 정밀도를 선택했습니다.
밀리초 및 마이크로초 표시 형식 분리
초기 초안에서는 ns와 함께 us 표시 형식을 제공했으며, ms 형식도 제안되었습니다. 이 형식들은 표시되는 소수 자릿수만 ns와 달랐습니다. 십진 출력은 기계에서 사용하도록 의도되었으므로 정밀도를 잘라도 실질적인 이점이 없습니다. 따라서 완전한 정밀도의 ns 형식만 제공합니다. 사람이 읽기 쉬운 형식을 원하는 사용자는 iso를 사용해야 합니다.
설정되지 않은 경우 None 반환
타임스탬프가 수집되지 않았을 때 0이 아니라 None을 반환하는 편이 조금 더 파이썬답지만, __timestamp_ns__는 C 구조체에서 일반 int64_t 멤버 디스크립터로 노출됩니다. None을 지원하려면 사용자 지정 게터와 박싱된 저장 공간이 필요합니다. 0은 모호하지 않습니다.
거친 시계 사용
참조 구현은 표준 벽시계를 최고 해상도로 읽는 PyTime_TimeRaw를 사용합니다. 벤치마킹 결과 실제로는 이 비용을 관찰할 수 없는 것으로 나타났습니다. CLOCK_REALTIME_COARSE와 같은 거친 시계는 해상도가 낮아 많은 요구에 충분하지 않으며, 측정 가능한 이점도 없습니다.
미해결 문제
표시 위치
현재 사양은 Type: message줄에 타임스탬프를 덧붙입니다. 대신 Traceback (most recent call last):헤더에 배치하거나 별도의 줄에 배치하자는 제안이 있었습니다. 현재 선택의 근거는 Timestamp in the Traceback Header Line 을 참조하십시오. 이는 수정될 수 있습니다. 위치를 구성 가능하게 만드는 것도 가능하지만, 추가되는 모든 설정 항목은 지원해야 할 복잡성을 증가시킵니다.
기존 코드가 Type: message줄을 구문 분석하므로 접미사가 추가되면 중단될 수 있다는 우려가 제기되었습니다. Traceback헤더에도 동일한 우려가 적용됩니다. 참조 구현에서는 doctest 트레이스백 매처와 CPython 자체의 test.support 헬퍼도 이미 조정해야 했지만, 이러한 경우는 드물었습니다.
sys.monitoring 대안 벤치마킹
거부된 아이디어에 있는 Using sys.monitoring을 참조하여 설계와 예상 비용을 확인하십시오. 이러한 분석에도 불구하고 인터프리터 변경을 피하려는 강한 선호가 있다면, 이 문제를 해결하기 위해 프로토타입과 벤치마크를 작성할 수 있습니다.
최초 예외 발생 시각 기록
인스턴스화 시점 대신 또는 이에 더하여 최초 예외 발생 시각을 기록하자는 제안이 있었습니다. 이는 거부된 것이 아니라 보류된 사항입니다. 예외가 발생하기 훨씬 전에 구성되는 경우를 다룰 수 있지만, 인터프리터의 발생 경로에서 깔끔한 훅 지점을 식별하고(Instantiation Time vs. Raise Time을 참조하십시오) 재발생의 의미를 정의해야 합니다. 예외를 처음 사용하기 훨씬 전에 구성하는 패턴을 일반적으로 사용하는 코드는 알려진 바가 없습니다.
감사의 말
Nathaniel J. Smith에게는 최초 아이디어를 제안해 주신 데 감사드리며, Daniel Colascione에게는 구현에 대한 2025년 초기 검토 의견을 주신 데 감사드립니다.
변경 이력
- 2026년 4월 18일
ns표시 형식을ns접미사가 붙은 정수에서 소수점 이하 아홉 자리의 초 단위로 변경하여,datetime.fromtimestamp()를 직접 사용할 수 있게 했습니다.us형식을 제거했습니다.ns가 이제1을 통해 활성화하거나 명시적인 형식 값 없이 활성화할 때의 기본값입니다.traceback모듈의no_timestamp매개변수를 세 가지 상태를 갖는timestamps로 교체했습니다(기본값은None이며 전역 설정을 따릅니다).__timestamp_ns__는 UTC이고, 시계 읽기 실패 시0이 반환되며, 프리 리스트 및 싱글턴MemoryError인스턴스의 타임스탬프 처리 방식이 무엇인지 명확히 했습니다. 또한str(exc)와repr(exc)는 변경되지 않으며,iso표시 형식은 마이크로초 해상도를 갖는다는 점도 명확히 했습니다.hasattr, 세 인자를 받는getattr및 딕셔너리 누락 경로는 CPython의 핫 경로에서 예외를 인스턴스화하지 않으므로 특별한 처리가 필요하지 않다는 점을 명시했습니다.- 인스턴스화 시점과 발생 시점에 관한 근거 하위 섹션을 추가했습니다.
sys.excepthook및sys.monitoring,Traceback헤더 줄에 타임스탬프를 배치하는 방안, 설정되지 않은 경우None을 반환하는 방안, 별도의 ms/us 표시 형식, 저해상도 시계를 사용하는 방안에 대한 거부된 아이디어 항목을 추가했으며, 런타임 API 거부 내용을 다시 표현했습니다.- 표시 위치,
sys.monitoring대안 벤치마킹, 최초 예외 발생 시각 기록을 다루는 미해결 문제 섹션을 추가했습니다. - 동기 부여 예제를 독립적으로 실행할 수 있도록 재구성했습니다.
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.