ChildProcessManager::setupEventHandlers | Child process exited
RCA: ChildProcessManager::setupEventHandlers | Child process exited
Error Log#
ChildProcessManager::setupEventHandlers | Child process exited
Impact#
- Service:
cupixworks-sitetrack-instance - Team: innovo
- 발생 횟수: 935
- 최초 발생: 2026-04-06T07:58:37.427Z
- 최근 발생: 2026-04-07T09:38:29.303Z
Root Cause Summary#
ChildProcessManager의 setupEventHandlers 메서드에서 child process의 exit 및 close 이벤트를 exit code와 관계없이 항상 logger.error로 기록하고 있다. 실제로 모든 로그 항목의 exit code는 0 (정상 종료)이며, 로그 전후 컨텍스트를 보면 BIM validation 작업이 성공적으로 완료된 후 정상적으로 프로세스가 종료된 것이다. 즉, 실제 에러가 아닌 정상 동작이 error level로 잘못 분류되어 935건의 false positive 에러가 발생했다. 동일한 코드를 공유하는 cupixworks-captue-skatmaster-arm-instance, cupixworks-any-mesh-agent 서비스에서도 같은 패턴이 확인된다.
Technical Analysis#
Code Path#
- Entry point:
child-process.manager.ts:54—start()메서드에서 child process를 fork하고setupEventHandlers()호출 - Event registration:
child-process.manager.ts:100-152—setupEventHandlers()메서드에서 6개 이벤트 핸들러 등록 - Failure point (잘못된 로그 레벨):
child-process.manager.ts:122-127—exit이벤트 핸들러
exit 이벤트 핸들러에서 exit code를 확인하지 않고 무조건 logger.error를 호출한다:
// child-process.manager.ts:122-127
this.process.on('exit', (code: number | null, signal: string | null) => {
logger.error('ChildProcessManager::setupEventHandlers | Child process exited', {
code,
signal
});
});
동일하게 close 이벤트 핸들러도 exit code와 무관하게 logger.error를 사용한다:
// child-process.manager.ts:107-120
this.process.on('close', (code: number | null, signal: string | null) => {
logger.error('ChildProcessManager::setupEventHandlers | Child process closed', {
code,
signal
});
const errorMsg = signal
? `Process killed by signal ${signal} (code: ${code})`
: `Process exited with code ${code}`;
this.rejectAllPending(new Error(errorMsg));
this.process = undefined;
this.emit('close', code, signal);
});
기대 동작: exit code가 0이면 정상 종료이므로 logger.info 또는 logger.debug로 기록하고, exit code가 0이 아니거나 signal로 종료된 경우에만 logger.error로 기록해야 한다.
실제 동작: exit code와 무관하게 항상 logger.error로 기록하여, 정상 완료된 모든 child process 종료가 에러로 보고된다.
추가로, close 핸들러(line 117)에서 rejectAllPending()을 호출하는데, 정상 종료 시(code 0)에는 pending message가 이미 처리 완료된 상태이므로 실제 영향은 없다. 하지만 의미론적으로는 exit code 0일 때 reject 대신 정상 처리 경로를 타는 것이 더 정확하다.
Log Evidence#
Datadog에서 에러 로그를 조회한 결과, 모든 항목의 exit code가 0임을 확인했다:
service:cupixworks-sitetrack-instance "ChildProcessManager" status:error
대표적인 로그 쌍 (항상 동시에 발생):
{
"timestamp": "2026-04-07T09:18:50.933Z",
"message": "ChildProcessManager::setupEventHandlers | Child process exited",
"code": 0,
"signal": null,
"host": "3e8f53015217",
"sitetrack.id": 2319,
"job.id": 174382,
"team": "hawkins/128"
}
{
"timestamp": "2026-04-07T09:18:50.933Z",
"message": "ChildProcessManager::setupEventHandlers | Child process closed",
"code": 0,
"signal": null,
"host": "3e8f53015217",
"sitetrack.id": 2319,
"job.id": 174382,
"team": "hawkins/128"
}
에러 발생 직전의 info 로그로 BIM validation이 정상 완료되었음을 확인:
09:18:50.635Z [info] "cupix::bim_algorithm::BimValidator::processSiteInsight() end. elapsed: 55.016"
09:18:50.636Z [info] "cupix::bim_algorithm::BimValidator::process() end. elapsed: 55.016"
09:18:50.934Z [info] "Sitetrack::executeBimPointsValidator | end - sitetrack.id: 2319"
에러 발생 직후의 info 로그로 정상 종료 흐름 확인:
09:18:51.599Z [info] "Sitetrack::run | end"
09:18:51.600Z [info] "Sitetrack::terminateService | force shutdown after 10 seconds"
영향 범위 — 3개 서비스에서 동일 패턴 확인:
service:cupixworks-sitetrack-instance "ChildProcessManager" status:error → sitetrack 작업
service:cupixworks-captue-skatmaster-arm-instance "ChildProcessManager" status:error → capture 작업
service:cupixworks-any-mesh-agent "ChildProcessManager" status:error → mesh/BIM 작업
3개 리전(us-west-2, ap-southeast-1, ap-southeast-2)에서 다수 팀(shimizu, hawkins, accoes, maryl, tpc, walmart, gilbaneco, rsquare, tanseisha, gad 등)에 걸쳐 발생.
Fix Recommendation#
즉시 조치 (Critical)#
- 파일:
applications/agents/packages/base/src/manager/child-process.manager.ts:122-127 exit이벤트 핸들러에서code === 0이면logger.info로, 그 외는logger.error로 분기 처리- 동일하게
close이벤트 핸들러(child-process.manager.ts:107-111)도 exit code에 따라 로그 레벨 분기
단기 개선 (1주 이내)#
close핸들러(line 113-117)에서code === 0인 경우rejectAllPending()을 호출하지 않도록 분기 추가. 정상 종료 시 pending message가 없을 가능성이 높지만, 의미론적으로 정확한 처리가 필요하다.stderr이벤트 핸들러(line 138-141)에서도 stderr 출력을 무조건logger.error로 기록하는 대신, AWS SDK deprecation warning 같은 비치명적 메시지는logger.warn으로 분류하는 것을 고려
장기 개선 (재발 방지)#
ChildProcessManager에 structured exit status 개념 도입 — 정상 종료, 비정상 종료, timeout 종료를 명확히 구분하는 enum 또는 타입 정의- child process lifecycle 이벤트에 대한 로그 레벨 가이드라인을 팀 코딩 컨벤션에 추가
- Datadog에서 false positive error가 발생하지 않도록 로그 레벨 정책 리뷰
Monitoring#
- 수정 후
cupixworks-sitetrack-instance서비스의 error count 추이를 모니터링하여 false positive가 제거되었는지 확인:
service:cupixworks-sitetrack-instance status:error "ChildProcessManager::setupEventHandlers"
- 수정 후에도 남는 error 로그가 있다면 실제 비정상 종료(code != 0)만 표시되는지 확인
- 동일 코드를 공유하는
cupixworks-captue-skatmaster-arm-instance,cupixworks-any-mesh-agent서비스도 함께 모니터링
Risk Assessment#
- Risk level: low — 로그 레벨 변경만으로 기능적 영향 없음
- 예상 복잡도: trivial —
exit/close핸들러에서 exit code 조건 분기를 추가하는 단순 변경