20 KiB
20 KiB
title, source_type, status, branch, parent_branch, related_projects, tags, created, target_merge, status_label, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, contract_packet_sha256
| title | source_type | status | branch | parent_branch | related_projects | tags | created | target_merge | status_label | id | kind | project | work_item | inherits | refines | overrides | depends_on | contract_packet | contract_packet_sha256 | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| branch / feature-startup-failure-log-suppression | branch-note | raw | feature-startup-failure-log-suppression |
|
|
2026-07-03 | in-progress | BR-CA-SKELETON-OPERATIONAL-CONTRACT-058 | project-work-item | ca-skeleton-operational-contract | WI-CA-SKELETON-OPERATIONAL-CONTRACT-058 |
|
|
1 | e7c1afc830ee67bc838ff152355faa14fe6c26669d8914655bda307ea186532b |
branch: feature-startup-failure-log-suppression
Layer:
raw/branch-notes/— ca-tmpl startup failure logging 작업 기록. 실제 git branch:develop.
부모 (필수)
브랜치 계약 패킷
- 생성 시 프로젝트 개정:
1 - 패킷 스키마:
contract_packet: 1 - 완료 조건: suppressible startup failure 조건과 retained actionable error test가 명시된다
상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-BOOTSTRAP-001@1 |
신규 환경의 default 진입 명령은 ./gradlew bootstrap이다 | Work Item 완료 조건에 적용 | raw/project-notes/ca-skeleton-operational-contract |
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-ERROR-ENVELOPE-001@1 |
foundation이 envelope schema와 error.category enum의 단일 owner다 | Work Item 완료 조건에 적용 | raw/project-notes/ca-skeleton-operational-contract |
브랜치 지역 결정
기존 branch-local 결정은 아래
## Decision Evidence Map / 결정-근거 매핑의 D-row가 소유하며 이 packet에서 복제하지 않는다.
| Decision ID | Decision | Relation | Supporting Claims | Status |
|---|
선언한 예외
| Override ID | Overrides | Reason | Approval | Status |
|---|
목표
- Spring Boot startup failure에서 ca-tmpl의 구조화
MIGRATION_FAILED로그와 Spring Boot 기본Application run failedstacktrace가 함께 출력되는 문제를 줄인다. - 목표 정책: startup failure는
startup.phase,error.code,error.category, root-cause 요약만 남기고 framework/driver stacktrace는 application log에 출력하지 않는다.
범위
포함 범위
app-bootstrapstartup failure logging 계약 변경.StartupFailurescanonical log에서 SLF4J throwable 인자 제거.SpringBootExceptionReporter로 typed startup failure의 Spring Boot 기본 실패 report 억제.- Logback
TurboFilter로 startup failure 이후의 SpringApplication 중복 close/report message 억제. - settings/validator startup failure를
StartupFailures.envValidation(...)로 통일. - Spring Boot context refresh cancellation / failure analysis residual log 억제.
- focused tests,
:app-bootstrap:test,verifyCleanArchitectureDependencies,check검증.
제외 범위
- runtime HTTP exception response shape 변경.
- persistence
DB_*SQLState matrix 변경. - production 환경 로그 검증.
근거
| Source | 정당화하는 결정 |
|---|---|
Local code evidence: src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/runtime/startup/StartupFailures.java |
기존 canonical startup failure log가 throwable cause를 SLF4J에 넘겨 stacktrace를 출력하던 사실 확인. |
Local dependency evidence: javap org.springframework.boot.SpringApplication |
SpringBootExceptionReporter#reportException이 true를 반환하면 Spring Boot가 failure를 logged exception으로 등록하고 generic report path를 종료하는 흐름 확인. |
Local test evidence: ./gradlew check |
전체 Gradle guard 통과로 구현·검증 결과 확인. |
TODO
- Startup failure canonical log에서 throwable proxy 제거 — 등급:
actually-implemented - root-cause class/message 구조화 필드 추가 — 등급:
actually-implemented - startup failure 전용
SpringBootExceptionReporter등록 — 등급:actually-implemented - SpringApplication 중복 close/report log filter 추가 — 등급:
actually-implemented - app-bootstrap settings/validator plain startup exception을
StartupValidationException으로 번역 — 등급:actually-implemented - Spring Boot context cancellation / failure analysis residual log filter 확장 — 등급:
actually-implemented - DB down / invalid tracing sample rate
bootRun재현 검증 — 등급:locally-verified - focused tests,
:app-bootstrap:test,verifyCleanArchitectureDependencies실행 — 등급:locally-verified
진행 중 메모
- TDD RED로
StartupFailuresTest가 기존 throwable proxy 때문에 실패하는 것을 먼저 확인했다. SpringBootExceptionReporter만으로는Unable to close ApplicationContextWARN을 제어하지 못하므로, canonical startup failure가 이미 기록된 뒤 SpringApplication의 exact duplicate message만 차단하는 Logback filter를 추가했다../gradlew check첫 실행은 Spotless formatting 위반으로 실패했고,:app-bootstrap:spotlessApply적용 후 재실행에서 통과했다.- 2026-07-03 후속 hardening:
ConfigurationPropertiesrecord와 runtime startup validator가 plainIllegalStateException/IllegalArgumentException을 던지던 사각을StartupFailures.envValidation(...)으로 통일했다. - invalid tracing sample rate 재현에서 기존
BindException/NumberFormatException/FailureAnalysis 출력은 compactSTARTUP_VALIDATION_FAILED로그 1줄로 축소되었다. - DB down migration 재현에서 기존 context refresh cancellation
BeanCreationExceptionWARN은 더 이상 grep 결과에 나타나지 않았다. 남은 SpringBeanPostProcessorChecker/Micrometer WARN은 exception report가 아니라 별도 framework lifecycle/noise 축이다.
결정 사항
- 2026-07-03: startup failure log는 stacktrace 대신 root-cause summary field만 남긴다 / 이유: 운영자가 분류할 수 있는 정보는 유지하면서 driver/framework stacktrace 노출과 중복을 줄이기 위해 / 검토한 대안: 중복 제거만, profile별 stacktrace 분기 / 근거: local code + test evidence.
- 2026-07-03: Spring Boot generic
Application run failed는SpringBootExceptionReporter로 typed startup failure에 한해 억제한다 / 이유: unknown startup failure의 Boot 기본 진단은 유지하기 위해 / 검토한 대안:org.springframework.boot.SpringApplicationlogger 전체 off / 근거: local dependency evidence. - 2026-07-03: context close 중복 WARN은 marker 기반 Logback filter로 exact message만 차단한다 / 이유: reporter 이후 context close 단계에서 발생하는 별도 SpringApplication WARN을 좁은 범위로 억제하기 위해 / 검토한 대안: logger 전체 off, 방치 / 근거: attached runtime log + local test evidence.
- 2026-07-03: startup validation/settings failure는 plain Java exception 대신
StartupFailures.envValidation(...)으로 번역한다 / 이유: Boot binding/context failure path에 들어가더라도 cause chain에StartupFailureException이 포함돼 compact reporter/filter 정책이 적용되게 하기 위해 / 검토한 대안: reporter가 모든IllegalStateException을 잡도록 확장, Spring Boot failure analyzer logger만 억제 / 근거: local bootRun 재현 + focused tests. - 2026-07-03:
LoggingFailureAnalysisReporter와 Spring context refresh cancellation log는 startup failure marker가 켜진 뒤에만 filter에서 억제한다 / 이유: unknown boot failure 진단은 보존하고, 이미 compact startup failure가 기록된 중복 exception detail만 제거하기 위해 / 검토한 대안: Spring logger level 조정, failure analysis reporter 전체 비활성 / 근거: local bootRun 재현 + filter tests.
결정-근거 매핑
| Decision ID | Decision | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|
| D1 | StartupFailures는 cause를 예외에는 보존하되 SLF4J throwable 인자로 넘기지 않고 root-cause class/message만 로그 구조화 필드로 남긴다. |
UNSUPPORTED_DECISION — local code inspection and user-approved policy; trade-off: stacktrace triage detail is removed from startup logs. | actually-implemented + locally-verified | 운영자가 전체 stacktrace를 로그에서 바로 보지 못하므로 재현 환경에서 cause chain 확인이 필요할 수 있다. |
| D2 | StartupFailureExceptionReporter는 cause chain에 StartupFailureException이 있을 때만 true를 반환해 Boot generic failure report를 억제한다. |
UNSUPPORTED_DECISION — local javap inspection of Spring Boot failure reporting path; trade-off: official doc raw source was not captured in this task. |
actually-implemented + locally-verified | Spring Boot internal flow가 major upgrade에서 바뀌면 reporter 효과를 재검증해야 한다. |
| D3 | StartupFailureSpringBootLogFilter는 canonical startup failure 이후 SpringApplication의 Application run failed와 Unable to close ApplicationContext exact message만 차단한다. |
UNSUPPORTED_DECISION — attached log symptom + local filter tests; trade-off: process-local marker assumes startup failure is fatal. | actually-implemented + locally-verified | 동일 JVM에서 startup failure 후 테스트가 계속되는 특수 상황은 marker reset test helper에 의존한다. |
| D4 | Wiki branch-note slug는 logical work unit feature-startup-failure-log-suppression을 사용하고, 실제 git branch develop은 본문에 기록한다. |
UNSUPPORTED_DECISION — LLM Wiki naming lint rejects develop.md; trade-off: ca-tmpl branch-name capture와 wiki naming gate 사이의 충돌을 wiki document shape 우선으로 해결. |
documented-only | git branch 기준 검색 시 logical note slug를 한 번 더 확인해야 한다. |
| D5 | app-bootstrap startup settings/validators는 invalid config를 plain Java exception이 아니라 StartupFailures.envValidation(...)으로 던진다. |
UNSUPPORTED_DECISION — local bug reproduction and user-approved policy; trade-off: direct constructor tests now observe StartupValidationException instead of IllegalArgumentException. |
actually-implemented + locally-verified | LoggingSettings처럼 startup validation owner가 아닌 warn-and-default bootstrap helper는 별도 정책 예외로 남는다. |
| D6 | startup failure marker 이후 LoggingFailureAnalysisReporter와 Spring context refresh cancellation log를 filter에서 억제한다. |
UNSUPPORTED_DECISION — local bootRun symptom and filter tests; trade-off: fatal startup failure window에서 Spring Boot failure-analysis banner를 숨긴다. | actually-implemented + locally-verified | Spring Boot logger/message 이름이 major upgrade에서 바뀌면 bootRun 재현 테스트로 재확인 필요. |
구현 가이드
1. Canonical startup log
Trace: D1
- UNSUPPORTED_IMPL_DECISION: structured field 이름은
error.root_cause.class와error.root_cause.message를 사용했다. 기존error.code/error.categorydotted naming과 맞춘 local convention이다.
| File | 구현 |
|---|---|
StartupFailures.java |
cause가 있을 때 rootCause(cause)를 찾아 class/message를 structured argument로 기록하고, throwable 인자는 넘기지 않는다. |
StartupFailuresTest.java |
migration failure log event의 ThrowableProxy가 null이고 root-cause summary field가 있는지 검증한다. |
2. Spring Boot duplicate report suppression
Trace: D2, D3
- UNSUPPORTED_IMPL_DECISION:
SpringBootExceptionReporter와 LogbackTurboFilter를 함께 사용했다. reporter는 generic report path만 막고, filter는 reporter 이후 context close duplicate WARN만 좁게 막는다.
| File | 구현 |
|---|---|
StartupFailureExceptionReporter.java |
cause chain에 StartupFailureException이 있으면 true, 아니면 false. |
META-INF/spring.factories |
org.springframework.boot.SpringBootExceptionReporter key로 reporter 등록. |
StartupFailureLogState.java |
canonical startup failure가 기록됐는지 process-local marker 제공. |
StartupFailureSpringBootLogFilter.java |
marker가 켜진 뒤 org.springframework.boot.SpringApplication의 exact duplicate messages만 DENY. |
logback-spring.xml |
startup failure duplicate filter를 turbo filter로 등록. |
3. Startup validation translation hardening
Trace: D5, D6
- UNSUPPORTED_IMPL_DECISION: settings record compact constructor도
StartupFailures.envValidation(...)을 직접 호출한다.app-bootstrap의 운영 설정 검증이며 비즈니스 규칙이 아니므로 composition-root 책임 안에 둔다.
| File | 구현 |
|---|---|
AsyncExecutorSettings.java, IdempotencySettings.java, OutboxSettings.java, TracingSettings.java |
invalid runtime setting을 StartupFailures.envValidation(...)으로 변환한다. |
RuntimeNumericBoundsValidator.java, HikariPoolConstraintValidator.java, OpenInViewSafetyValidator.java, SecretSourceValidator.java |
startup safety guard의 plain exception을 StartupFailures.envValidation(...)으로 변환한다. |
StartupFailureSpringBootLogFilter.java |
marker 이후 LoggingFailureAnalysisReporter 전체와 Spring context refresh cancellation prefix를 DENY한다. |
| focused settings/validator/filter tests | invalid path가 StartupValidationException으로 번역되고 residual Boot logs가 filter에서 차단되는지 검증한다. |
엣지·실패·의존
- 실패·엣지 경로: cause chain self-reference는 reporter와 root cause walker가 무한 루프를 피해야 한다.
- 실패·엣지 경로: non-startup exception은 Spring Boot 기본 failure report를 유지해야 한다.
- 다른 계약 의존:
app-bootstraplogging bootstrap과 Spring Bootspring.factoriesloading path에 의존한다.
검증해야 할 주장
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
실제 bootRun에서 DB down 시 Application run failed, context refresh cancellation, BeanCreationException stacktrace가 출력되지 않는다. |
2026-07-03 후속 hardening에서 실제 재현 수행. | APP_DATASOURCE_URL='jdbc:postgresql://127.0.0.1:1/ca_skeleton' timeout 60s ./gradlew :app-bootstrap:bootRun --quiet 2>&1 | rg -n "startup failure|Application run failed|Exception encountered during context initialization|LoggingFailureAnalysisReporter|BeanCreationException|ConnectException|Caused by:" -C 2 |
locally-verified |
실제 bootRun에서 invalid tracing sample rate가 BindException/NumberFormatException stacktrace 대신 compact startup failure로 출력된다. |
2026-07-03 후속 hardening에서 실제 재현 수행. | APP_MIGRATION_ON_STARTUP=false APP_TRACING_SAMPLE_RATE='not-a-number' timeout 60s ./gradlew :app-bootstrap:bootRun --quiet 2>&1 | rg -n "startup failure|Application run failed|Exception encountered during context initialization|LoggingFailureAnalysisReporter|BindException|NumberFormatException|Caused by:" -C 2 |
locally-verified |
Spring Boot major upgrade 후에도 SpringBootExceptionReporter의 true-return behavior가 동일하다. |
local dependency bytecode 확인에 기반한 결정이다. | Spring Boot upgrade branch에서 reporter focused test와 실제 startup failure 로그 재현. | needs-confirmation |
관심사 커버리지
| 관심사 | 상태 | owner | 심각도 | 근거 |
|---|---|---|---|---|
| startup failure canonical log | covered-here | — | — | D1 |
| Spring Boot duplicate failure report | covered-here | — | — | D2, D3, D6 |
| app-bootstrap startup validation/settings plain exception | covered-here | — | — | D5 |
| runtime HTTP exception response | delegated | existing adapter-web error contract | OK | Out of scope |
runtime background log(..., ex) stacktrace |
delegated | future runtime logging hardening | OK | Out of scope |
마주친 문제
- Spotless formatting failure
- 원인: 새 Java 파일의 line wrapping이 Spotless 규칙과 달랐다.
- 시도:
./gradlew check실행. - 해결:
./gradlew :app-bootstrap:spotlessApply후./gradlew check재실행. - 별도 에러 노트로 분리됨: raw/errors/startup-log-suppression-spotless-format-2026-07-03
- 2026-07-03 후속 hardening 중 TDD RED failures
- 원인: 의도적으로 settings/validator tests를
StartupValidationException기대치로 먼저 바꿔 기존 plain exception 사각을 재현. - 해결:
StartupFailures.envValidation(...)전환 후 focused suite green. - 별도 에러 노트: 없음. 의도된 RED 단계로 별도 트러블슈팅 문서화 대상 아님.
- 원인: 의도적으로 settings/validator tests를
- 2026-07-03 전체
check실패- 원인:
:sample-portfolio:testweb context startup 중 TomcatPortInUseException/BindException. - 시도: import order Spotless failure 수정 후
./gradlew check재실행. - 해결: 이번 변경 범위 밖의 sample-portfolio test/runtime port collision으로 분리 기록. focused startup/logging suite,
:app-bootstrap:test,verifyCleanArchitectureDependencies, bootRun 재현은 통과/확인. - 별도 에러 노트로 분리됨: raw/errors/sample-portfolio-tomcat-port-in-use-check-2026-07-03
- 원인:
묶음 (이 branch에서 파생된 자료)
- raw/errors/sample-portfolio-tomcat-port-in-use-check-2026-07-03
- raw/errors/startup-log-suppression-spotless-format-2026-07-03
Sub-branches (세부 작업)
- 없음.
오류 기록 (이 branch 작업 중 발생)
- raw/errors/startup-log-suppression-spotless-format-2026-07-03 —
check중 Spotless formatting failure. - raw/errors/sample-portfolio-tomcat-port-in-use-check-2026-07-03 — 전체
check중 sample-portfolio Tomcat port collision. - 2026-07-03 후속 hardening: TDD RED와 경로 오입력은 작업 중 검증/도구 사용 이슈로 branch-note에만 기록.
면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- 추출할 별도 면접 질문 없음.
강의 (이 작업을 위해 학습한 강의)
- 없음.
job-posting tie-ins (이 작업에서 파생된 글감)
- 후보: startup failure compact logging hardening은 블로그 글감으로 확장 가능하나, 이번 캡처에서는 별도 raw/blog-topic으로 분리하지 않음.
관련 일일 노트
- 없음.
완료 후 정리
- PR 링크:
- 리뷰 메모:
- 머지 결과 / 배포 환경: local verification only.
- wiki 추출 대상:
actually-implemented항목: startup failure stacktrace suppression implementation; startup settings/validator exception translation hardening; residual Spring Boot failure-analysis/context-cancellation log filter.locally-verified항목: focused tests,:app-bootstrap:test,verifyCleanArchitectureDependencies, DB down bootRun reproduction, invalid tracing sample rate bootRun reproduction.prod-verified항목: 없음.
- 추출하지 않을 항목:
- 전체
./gradlew check::sample-portfolio:test의PortInUseException으로 실패. 변경 범위와 분리해 raw error note에 기록. - runtime background
log(..., ex)stacktrace cleanup은 별도 future work.
- 전체