자동매매 구조화 로깅 설계 — 나중에 원인을 추적할 수 있게 기록하는 법
로그는 있는데, 무슨 일이 있었는지 재구성이 안 된다
24시간 무인 운영에서 모니터링과 알림 설계를 다뤘다. 그런데 알림을 받고 로그를 열었는데, "무언가 실행됨", "처리 완료" 같은 문장만 잔뜩 있고 정작 어떤 종목의 어떤 주문이 어떤 값으로 처리됐는지 알 수 없는 경우가 있다. 문제가 생긴 그 순간을 재구성하지 못하면, 같은 문제가 또 일어나도 원인을 못 찾는다.
이 글은 로그를 "얼마나 많이 남기느냐"가 아니라 "사후에 얼마나 재구성 가능하게 남기느냐"의 관점에서 다룬다.
문자열 로그의 한계
print(f"{symbol} 주문 처리 완료") 같은 로그는 사람이 읽기엔 편하지만, 나중에 "특정 종목의 주문만 모아서 본다"거나 "특정 시간대 에러만 필터링한다" 같은 작업을 하려면 문자열을 파싱해야 한다. 로그 형식이 조금만 바뀌어도 기존 파싱 코드가 깨진다.
구조화 로깅 — 필드를 가진 데이터로 남긴다
문장 대신 필드를 가진 구조(보통 JSON)로 로그를 남기면, 나중에 특정 필드로 필터링·집계하기가 훨씬 쉬워진다.
python 코드 보기
import json
import time
def log_event(level, event, **fields):
record = {
"ts": time.time(),
"level": level,
"event": event,
**fields,
}
print(json.dumps(record, ensure_ascii=False))
log_event(
"info",
"order_submitted",
symbol="EXAMPLE",
order_type="limit",
qty=10,
client_order_id="abc-123",
)이렇게 남기면 나중에 로그 파일을 텍스트로 읽는 대신, event == "order_submitted"이거나 symbol == "EXAMPLE"인 줄만 걸러내는 게 훨씬 쉬워진다. 로그가 쌓일수록 이 차이는 커진다.
어떤 필드를 남길지가 핵심이다
로그를 구조화해도 필드 설계가 부실하면 소용없다. 최소한 아래 정보는 매 이벤트마다 남기는 걸 권한다.
- 요청을 식별할 수 있는 값 — 멱등성 설계에서 다룬 클라이언트 주문번호 같은 것. 이 값이 있어야 "이 주문이 재시도된 그 주문인지"를 로그에서 추적할 수 있다.
- 입력값 그대로 — 계산된 결과만이 아니라, 그 계산에 들어간 원본 입력값도 함께. 원인 분석은 대개 "왜 이 값이 나왔나"를 거꾸로 추적하는 과정이다.
- 단계 이름(event) — "주문 생성", "주문 전송", "체결 확인" 처럼 어느 단계에서 로그가 찍혔는지 구분되는 이름.
- 에러일 경우 원본 예외 메시지 — 요약하지 말고 원문을 그대로. 요약하는 순간 사후분석에 필요한 정보가 사라진다.
오늘의 정리
- 로그의 목적은 "실행됐다"는 사실 확인이 아니라, 사고 후 그 순간을 재구성하는 것이다. 이 기준으로 로그 설계를 점검해야 한다.
- 문자열 로그는 사람이 읽기엔 편하지만 나중에 필터링·집계하기 어렵다. JSON 같은 구조화 형식이 사후분석에 유리하다.
- 요청 식별자, 원본 입력값, 단계 이름, 원본 에러 메시지는 최소한 매 이벤트마다 남겨야 원인 추적이 가능하다.
이 글의 코드는 로깅 설계 개념을 보여주기 위한 예시이며, 실제 운영 중인 시스템의 로그 형식이나 구현이 아닙니다. 투자 조언이 아니며 특정 종목이나 매매기법을 추천하지 않고, 이 글의 내용과 투자 손실 사이에는 어떠한 인과관계도 없습니다. 수익 보장은 없으며, 투자 손실의 책임은 투자자 본인에게 있습니다.