CR #201 » SCS-020_監控與觀測機制程式設計規格.md
SCS-020 監控與觀測機制(Observability)程式設計規格(跨域)
| 項目 | 內容 |
|---|---|
| 文件編號 | SCS-020 |
| 版本 | 1.11 |
| 狀態 | 已核定(規格先行·跨域) |
| 對應需求 |
BRD-003:監控機制(21 項)、System Monitoring(11 項)、Monitoring(8 項)——均對應 SAD-006 §5、CSP-004
|
| 技術棧 | OpenTelemetry → Jaeger(本地)/Prometheus + Grafana + Loki(參 CSP-006) |
| 關聯文件 |
BRD-003、SAD-006 §5、CSP-004、CSP-001(例外/追蹤 common-spring) |
修訂記錄
| 版本 | 日期 | 異動 |
|---|---|---|
| 1.11 | 2026-10-01 |
新增 §9.12 分流比例管理端點(api-gateway)——解鎖 SDD-007 §5.1.2.3 桶 C 之「分流比例管理」(/system/split-ratio,原記「僅 actuator」)。選頁理由:SAD-004 §2.4 之分流機制(CycleSplitDecision/CycleSplitGlobalFilter/CycleSplitProperties,POC-13 已驗收)完整存在,缺的只是管理面——不需新領域模型、不會造空殼,且「灰度放量與回退」為遷移期最高頻維運操作。規格先行:立本節 → 實作 → SDD-007 §5.4.30 逐鈕定義 → 頁面。設計要點:定點行替換(僅改 enabled:/percentage: 兩行,保留註解)+ ContextRefresher.refresh()(不重啟)+ 回讀驗證(effectiveMatchesRequest,不以請求值冒充生效值)+ 失敗不靜默(CC-PLT-5000)。⚠️ 未竟:角色級授權/稽核落庫/回退歷程/多實例組態同步(見 §9.12.5) |
| 1.10 | 2026-10-01 |
BEAM PMO 2026-10-01 裁定之兩項執行(GOV-007):① §9.5 #3 更正——CorrelationIdFilter 之「待裁定/未擅自註冊」係過期記載;實已由 CorrelationIdAutoConfiguration(登錄 AutoConfiguration.imports)以 FilterRegistrationBean 註冊(BEAM PMO 2026-09-21 18:57 裁定「保留並註冊」),並顯式指定 ORDER(LOWEST_PRECEDENCE - 100)確保晚於 OTel 觀測範圍。② §9.5 #5 實作——19 個 application.yml 均設 logging.logback.rollingpolicy.total-size-cap: 5GB(BEAM PMO 裁定「限制 5GB」);併更正原建議之屬性名(logging.file.total-size-cap 不存在;正解為 logging.logback.rollingpolicy.total-size-cap,實查 metadata 確認)。⚠️ per-service 語意(本地 19 模組共用目錄)已於 §9.5 #5 誠實揭露 |
| 1.9 | 2026-10-01 |
新增 §9.11.6 業務 KPI 區塊(BffDashboard.business,CR-026):/dashboard 由「系統營運面」擴充為「系統營運面 + 業務 KPI」。business 含 4 子塊(cards/applications/authorizations/disputes),各 oneOf schema-or-null,獨立降級(任一統計取不到 → 該子塊為 null,不以 0 充數);由 BusinessStatsService 並行 4 路 CompletableFuture + DelegatingSecurityContextExecutor 聚合,逾時 STATS_TIMEOUT_MS=5000
|
| 1.8 | 2026-10-01 |
新增 §9.11 營運總覽聚合端點(bff-web)——解鎖 SDD-007 §5.1.2.3 桶 C 之「營運總覽」(/dashboard)。規格先行:立本節 → 改 tools/gen_openapi.py → 重生契約 → 實作 → SDD-007 §5.4.28 逐鈕定義 → 頁面。★ 回訪查證(關鍵):掃全數 17 服務契約,皆無「統計/摘要」端點(僅 CRUD 與列表)→ 業務 KPI 無法由既有端點推得 → 故明訂範圍界定(§9.11.1):本版為系統營運面(服務健康/批次/端點連線摘要,bff-web 自足),業務 KPI 屬第二階段(需各服務統計端點,另立 CR)。設計要點:複用既有三個聚合服務(不重複探測)、並行執行、區塊降級(失敗區塊為 null,不以 0 充數)、不快取、fail-closed
|
| 1.7 | 2026-09-30 |
新增 §9.8.6「實作註記:分層與目標埠」(HANDOVER #A-105)。⚠️ HealthAggregationService(application)原直接注入 infrastructure.config.BffHealthProperties,違反 CSP-002 §6.3(LayeringTest 於 HEAD 即紅)→ 採「應用層立埠」:application/HealthTargetsPort + infrastructure/config/HealthTargetsConfigImpl(沿用 AccountOverviewPort 慣例);併補 3 個單測(JDK 內建 HttpServer 造真目標,測真實網路行為而非 stub)→ :platform:bff-web:test 12 測 0 失敗、全綠(原 9 測 1 失敗)。⚠️ 併載:空 targets 之語意不得放寬(回空 Map=「未組態」→ 必須 DOWN),埠之實作原樣轉發即為確保不被靜默補值 |
| 1.6 | 2026-09-30 |
§9.9.1 更正 + 四服務列表端點規格先行(#A-104)。⚠️ 原載「四個既有端點,皆已實作並真機驗證」經實測不成立——四個路徑皆只有 POST,GET 回 405/404/404/404。故先行新增四個 GET 列表端點:GET /statement-runs/GET /clearing/runs/GET /migration-jobs/GET /report-jobs(各 ?limit=N,預設 20/上限 100),並於 SCS-011 §6.1/SCS-013 §6.1/SCS-015 §6.1/SCS-017 §6.1 各立規格。時間錨點:clearing 無 startedAt 欄位 → 本聚合取值順序為 startedAt → createdAt,不臆造。§9.9.3 補 limit 參數定義(每服務上限,預設 10/上限 50)。四服務 DTO 刻意不統一——以別名鏈吸收差異,避免各服務背負非其職責之欄位 |
| 1.5 | 2026-09-30 |
新增 §9.9 批次作業監控 + §9.10 端點與連線管理(bff-web)——解鎖桶 C 兩頁(SDD-007 §5.4.26/§5.4.27)。⚠️ 併補記:§9.9/§9.10 之規格與契約已於同日先行完成(契約 81 端點、0 錯誤),惟當時未登修訂記錄,本版一併補登。§9.10.2 之原設計經實測修正:gateway actuator endpoint 於本技術棧版本(Spring Cloud Gateway 5.0.3)未被註冊(/actuator 索引僅 7 個,加入 exposure.include 亦無效)→ 改採 api-gateway 自供唯讀端點 GET /api/v1/gateway/routes(#A-103)。實作要點:讀 RouteDefinitionLocator(非 RouteLocator——後者已解析為物件、無法還原原文);原文還原依 getArgs() 之 LinkedHashMap 插入順序(javap 確認);維持宣告順序不重排;追蹤碼於 controller 方法進入時取(避免 Reactor 脈絡傳遞之依賴)。真機:bff 之 routesAvailable=true(修正前 false)、17 筆路由、service 由 routeId 正確推導;連線維度 UP 7/17 不受影響。§9.10.5 補 filters 未由 bff 呈現(契約未含,不逕行加欄位)與不自訂錯誤碼(CC-GW-* 無對應語意) |
| 1.4 | 2026-09-29 |
新增 §9.8 健康聚合端點(bff-web)——解鎖 SDD-007 §5.1.2.3 桶 C 之「健康檢查與監控」頁。規格先行:立本節 → 改 tools/gen_openapi.py → 重生契約(78 端點、0 錯誤)→ 實作 → SDD-007 §5.4.24 逐鈕定義 → 頁面。★ 併載兩個實測揭露之陷阱:①@Value("#{...}") 讀 Map 靜默失敗(property key 含 - 非合法 SpEL)→ 須用 @ConfigurationProperties;②並行探測必須傳遞 SecurityContext(token-relay 模式下 RelayingOutboundTokenProvider 僅適用同執行緒)→ 須用 DelegatingSecurityContextExecutor,否則全部服務探測失敗
|
| 1.0 | 2026-09-19 | 初版核定(規格先行) |
| 1.2 | 2026-09-20 | WorkBuddy AI |
| 1.3 | 2026-09-20 | WorkBuddy AI |
| 1.4 | 2026-09-21 | WorkBuddy AI |
| 1.1 | 2026-09-19 | 新增 §9 實作機制與實測證據(W-4 接線):三支柱實測結果、四項實作時發現之缺陷與修正、五項已知限制 |
1. 對應需求(NFR)
| NFR 類別 | 數量 | 來源 | 驗收 |
|---|---|---|---|
| 監控機制 | 21 |
SAD-006 §5、CSP-004
|
監控指標與告警規則驗證、儀表板檢視 |
| System Monitoring | 11 | SAD-006 §5 |
監控覆蓋率檢核 |
| Monitoring | 8 | SAD-006 §5 |
監控項目逐項驗證 |
2. 觀測架構
應用(Spring Boot)──OTel SDK──→ Jaeger(Trace,本地)
└──→ Prometheus(Metrics)→ Grafana(Dashboard)
└──→ Loki(Logs,結構化)
-
Trace:統一以 OTel W3C Trace Context 為跨系統唯一識別(
CSP-004 §6,BEAM PMO 2026-09-20 裁定),
由common-spring之TraceContext讀取(取值順序:OTel span → MDC → 臨時值)。
⚠️ 已廢除第二套correlation_id——原 v1.0 設計另設correlation_id貫穿日誌,
實測導致 API 回應與日誌之 ID 互不相通(10b8a469…≠894ff808…),查測時無法對照。
⚠️CorrelationIdFilter目前未註冊(AutoConfiguration.imports未登錄、類別亦無@Component),
其職責已被 OTel 取代;註冊或退役待 BEAM PMO 裁定(詳 §9.5 #3),故本節不將其列為現行機制。
ℹ️ 勿與DomainEvent.correlationId()混淆——那是領域事件信封欄位
(CSP-007、SAD-001 AP-05、SDD-006 §4.1),用於事件鏈之業務關聯,仍然存在且有效;
被廢除的是「日誌/追蹤」層的那一個。 - Metrics:RED(Request/Error/Duration)+ USE(Utilization/Saturation/Errors),經 Micrometer 暴露。
3. 指標(Metrics)
| 指標 | 說明 | 來源 |
|---|---|---|
| 授權成功率/失敗率 | 業務指標 | authorization-service |
| 端點 P99 延遲 | 效能指標 | 所有 REST 端點(Micrometer) |
| JVM/連線池飽和度 | USE | common-spring |
| Kafka 消費滯後 | 管線健康 | common-event |
| 批次執行狀態 | 作業指標 | common-batch |
4. 追蹤(Tracing)
- 所有 inbound/outbound 自動埋點(
common-springTraceContext);資料庫/Kafka 跨度自動化。 -
OTel
traceId貫穿日誌(ECS 輸出為頂層駝峰traceId/spanId,見CSP-004 §3.1),
使 API 回應之traceId與日誌之traceId為同一值,便於跨服務排查。
⚠️ 原設計之correlation_id已廢除(CSP-004 §6),理由見 §2。
5. 日誌與稽核留痕
- 結構化 JSON 日誌 → Loki。
-
稽核留痕:關鍵變更(管制碼設定、額度調整、爭議)除業務表(
block_code_history等)外,亦落日誌,符合銀行審計要求(common-security加密/遮蔽)。
6. 告警(Alerting)
- Prometheus Alertmanager → Grafana 告警;依
BRD-003監控機制 21 項定義規則(延遲門檻、錯誤率、飽和度)。 - 分級(P1/P2/P3)與值班(SRE)路徑。
7. 測試案例(對應 TRP-001)
- 監控覆蓋率檢核(各服務端點/批次指標齊全)
- 追蹤貫穿測試(gateway→bff→服務→Kafka→下游
traceId連續;correlation_id已廢除,見 §2) - 告警規則觸發測試(注入延遲/錯誤驗證告警)
8. 已知限制(誠實揭露)
- 具體儀表板版面與告警閾值(與
BRD-00321 項對應)待 SRE 與SAD-006 §5凍結。 - 本地 Jaeger 與生產 Prometheus/Grafana/Loki 之佈署差異(SAD-006 部署章節)待確認。
- 稽核日誌之保留期與合規(如金管會要求)待法遵確認。
9. 實作機制與實測證據(W-4 接線,2026-09-19)
目的:將 §2 之觀測架構由「文件」落為「可驗證之實物」。
本節所有數據皆為實測,非設計推估。
9.1 三支柱實測結果
| 支柱 | 路徑 | 實測證據 | 判定 |
|---|---|---|---|
| Metrics | Prometheus 直接 scrape host.docker.internal:<8081–8098>/actuator/prometheus
|
4 個服務(8084/8088/8089/8091)target UP;jvm_memory_used_bytes 回傳 24 個序列,含 heap/nonheap 實際值 |
✅ 通過 |
| Traces | Spring Boot → OTLP/HTTP → OTel Collector → Jaeger | Jaeger /api/services 列出 party-service;查得 5 筆 trace,span 含 http get /api/v1/parties/{partyId}(8,027µs) |
✅ 通過 |
| Logs | Spring Boot 結構化 ECS JSON 寫檔 → Collector filelog → OTLP → Loki |
Loki 查得實際日誌,含 message/traceId/spanId/log_level/deployment_environment=local
|
✅ 通過 |
Metrics 採「直接 scrape」而非「OTLP push」之理由:
micrometer-registry-prometheus已於各服務就緒、/actuator/prometheus已暴露,
直接 scrape 零新增依賴且已驗證可用。OTLP push 需另加micrometer-registry-otlp;
Collector 之 metrics pipeline 已保留,日後可切換。
9.2 實作時發現之缺陷(原組態從未成功啟動)
⚠️ 重要更正:先前認知為「容器已在,只差接線」。實測發現
cc-otelcol自建立以來從未成功啟動(無限重啟),組態有三處致命缺陷。
| # | 缺陷 | 實測症狀 | 修正 |
|---|---|---|---|
| 1 |
loki exporter 已自 contrib 移除
|
unknown type: "loki" for id: "loki",Collector 啟動即失敗 |
改走 Loki 3.x 原生 OTLP 攝取端點 /otlp/v1/logs(實測回 422 表示端點存在;/otlp/v1/metrics 回 404=不存在) |
| 2 |
replace_pattern 用 Go RE2,不支援 lookahead (?=
|
invalid or unsupported Perl syntax: (?=,啟動失敗 |
改為 replace_pattern(..., "^.+$", "****") 完整遮蔽。亦較符合 CSP-004 §6「禁止在 span 記錄敏感 payload」 |
| 3 |
prometheus.yml 之 target 寫容器 DNS 名(party-service:8080) |
17 個 target 全數 DOWN(服務跑在主機,非 compose 內) | 改為 host.docker.internal:<8081–8098>(實測解析為 192.168.65.254) |
附帶修正:otlp→otlp_grpc、otlphttp→otlp_http 之 deprecated 別名;
Collector 健康埠 13133 原未映射至 compose,本機無法探測(易誤判為「沒起來」)。
9.3 Spring Boot 4 之 OTLP 陷阱(與 Flyway 同源)
命名空間不一致——誤用不報錯,只會靜默不匯出:
| 信號 | 正確屬性 | 所需依賴 |
|---|---|---|
| Traces | management.opentelemetry.tracing.export.otlp.endpoint |
spring-boot-starter-opentelemetry |
| Metrics | management.otlp.metrics.export.url |
micrometer-registry-otlp |
| Logs | management.opentelemetry.logging.export.otlp.endpoint |
尚需第三方 log bridge |
原寫於各服務之手動組合(micrometer-tracing-bridge-otel + opentelemetry-exporter-otlp)
不會觸發 management.opentelemetry.tracing.export.otlp.* 之自動配置。已改用 starter,
並確認其傳遞引入上述兩者(故原手動宣告為冗餘,已移除)。
9.4 日誌為何不走「應用直接 OTLP 推送」
Spring Boot 4.1 不提供將 Logback 日誌橋接至 SdkLoggerProvider 的內建機制
(官方文件:「Spring Boot doesn't support bridging logs to this bean out of the box」)。
唯一官方推薦之 opentelemetry-logback-appender-1.0,所有版本皆為 -alpha
(實測 Maven Central,最新 2.31.1-alpha)——銀行案不應引入 alpha 相依。
故採:Spring Boot 內建結構化日誌(logging.structured.format.file=ecs,無須第三方 encoder)
寫檔 → Collector filelog receiver 尾隨讀取 → OTLP → Loki。零第三方相依。
9.6 推廣至全模組(HANDOVER #C-23,2026-09-20)
背景:§9.5 #4 記載「僅
party-service接線(觀測 1/17)」。本節為該項之解除。
根因(實測,非推測):其餘 18 個模組(16 個服務 + api-gateway + bff-web)
之 build.gradle.kts 採手動組合——
implementation("io.micrometer:micrometer-tracing-bridge-otel")
implementation("io.opentelemetry:opentelemetry-exporter-otlp")
此組合不會觸發 Spring Boot 4 之 management.opentelemetry.tracing.export.otlp.* 自動配置
(見 §9.3),故依賴看似齊備、實則從未匯出任何 trace,且不拋出任何錯誤。
與 §9.2 之 Flyway/loki exporter 同屬「靜默不做」型缺陷。
修正範圍
| # | 修正 | 數量 |
|---|---|---|
| ① | 依賴改為 org.springframework.boot:spring-boot-starter-opentelemetry(移除原手動組合) |
18 個模組(party-service 原已正確) |
| ② | 組態補 management.opentelemetry.tracing.export.otlp.endpoint + logging.structured.format.file=ecs + logging.file.name
|
18 個模組 |
| ③ |
tools/gen_service_skeleton.py 模板同步修正(service/gateway/bff 3 處) |
避免新模組重蹈覆轍 |
| ④ |
prometheus.yml 新增 cardcore-platform job(api-gateway:8100/bff-web:8101) |
原僅 scrape 17 個領域服務,平台層為監控缺口 |
① 與 ② 皆同步修改「既有檔案」與「產生器模板」:產生器只寫入不存在的檔案,
故僅改模板無法修復既有 18 個模組,兩邊皆須動手。
逐模組實測(逐一啟動,非抽樣)
| 模組 | 埠 | /actuator/health |
/actuator/prometheus |
Jaeger trace | 結構化日誌 JSON | Loki 行數 |
|---|---|---|---|---|---|---|
authorization-service |
8081 | 200 | 200 | ≥5 | ✅ | 48 |
card-service |
8082 | 200 | 200 | ≥5 | ✅ | 49 |
limit-service |
8083 | 200 | 200 | ≥5 | ✅ | 48 |
party-service |
8084 | 200 | 200 | ≥5 | ✅ | 112 |
product-service |
8085 | 200 | 200 | ≥5 | ✅ | 48 |
pricing-service |
8086 | 200 | 200 | ≥5 | ✅ | 48 |
posting-service |
8087 | 200 | 200 | ≥5 | ✅ | 48 |
payment-service |
8088 | 200 | 200 | ≥5 | ✅ | 49 |
installment-service |
8089 | 200 | 200 | ≥5 | ✅ | 48 |
rewards-service |
8091 | 200 | 200 | ≥5 | ✅ | 48 |
statement-service |
8092 | 200 | 200 | ≥5 | ✅ | 48 |
dispute-service |
8093 | 200 | 200 | ≥5 | ✅ | 48 |
clearing-service |
8094 | 200 | 200 | ≥5 | ✅ | 48 |
merchant-service |
8095 | 200 | 200 | ≥5 | ✅ | 49 |
migration-service |
8096 | 200 | 200 | ≥5 | ✅ | 48 |
notification-service |
8097 | 200 | 200 | ≥5 | ✅ | 48 |
report-service |
8098 | 200 | 200 | ≥5 | ✅ | 48 |
api-gateway |
8100 | 200 | 200 | ≥5 | ✅ | 26 |
bff-web |
8101 | 200 | 200 | ≥5 | ✅ | 17 |
上表「Jaeger trace」為該服務當次探測回傳之 trace 筆數(查詢上限 5,故
≥5即代表有匯出)。
三支柱之最終驗證
| 支柱 | 驗證方式 | 結果 |
|---|---|---|
| Traces | Jaeger /api/services
|
19 個模組全數在列(+jaeger-all-in-one = 20) |
| Logs | Loki 依 service_name_extracted 查詢 |
19/19 皆有日誌(48–112 行;gateway 26/bff 17 行) |
| Metrics | Prometheus /api/v1/targets
|
21 個 target:領域服務 17 個、平台層 2 個、Collector、Prometheus 自身。 領域服務 scrape UP 17/17(分兩批實測:11 + 6),平台層 UP 2/2 |
實測時之三個踩坑(皆為「不報錯」型)
-
取樣率:預設
TRACING_SAMPLING=0.1(10%),單次請求有 90% 機率不被取樣,
驗證會隨機失敗。實測時須設為1.0。 -
Loki 查詢語法:
service_name_extracted係 Loki 3.x 自動萃取之
structured metadata,不能置於 stream selector{{}}內——查得到 0 筆且不報錯;
須寫為{{service_name="unknown_service"}} | service_name_extracted="<svc>"。
另/loki/api/v1/query為 instant query,不支援 log query(須用/query_range)。 -
併發啟動限制:19 個 JVM 同時啟動時,6 個服務因 HikariPool
Connection is not available, request timed out after 3003ms於 Flyway 初始化階段失敗。
屬本機資源限制(4 核/Oracle sessions 上限 322),與本次監控接線無關——
該 6 個服務於逐一啟動與後續分批啟動時皆正常,且皆已取得 trace/日誌/scrape 證據。
9.7 IAM(Keycloak)納入監控(HANDOVER #C-35,2026-09-20)
背景:§9.6 完成 19 個應用模組之接線後,身分認證服務(Keycloak)仍完全無指標,
且其健康端點由主機探測一律 404。
查明結果(實測 + 官方文件):健康端點本來就正常——
Keycloak 26 之 /health*、/metrics 位於管理埠 9000(官方文件:「exposed on the
management port 9000 by default」),不是主埠 8180。原 compose 未映射 9000,
故由主機探測皆 404,易被誤判為「端點壞掉」。
容器內實測 9000/health/ready → 200 {"status":"UP"}。
| # | 修正 | 實測結果 |
|---|---|---|
| ① |
docker-compose.yml 映射 9000:9000
|
/health//health/ready//health/live//health/started 皆 200;內容含 Keycloak database connections async health check: UP
|
| ② | 補 KC_METRICS_ENABLED=true
|
/metrics 200;約 930 個序列(vendor 673/jvm 61/cache 48/http 40…)。註:Database 健康檢查項目需同時啟用指標才會出現(官方文件) |
| ③ |
prometheus.yml 新增 keycloak job(target host.docker.internal:9000,metrics_path=/metrics) |
up{job="keycloak"}=1、序列 930;target 總數 21 → 22
|
回歸驗證:容器重建後,權杖簽發仍正常
(POST /realms/cardcore/protocol/openid-connect/token → demo.holder、角色 cardcore-customer)。
另註:cardcore-services 為機密客戶端,password grant 須帶 client_secret
(僅帶 client_id 會回 unauthorized_client)。
⚠️ 資安提醒(未解決,須於上線前處理):管理埠不應對外暴露。
本機開發可接受;SIT 以上應僅允許監控網段存取,或改以容器內HEALTHCHECK
於容器內探測({ printf 'HEAD /health/ready HTTP/1.0\r\n\r\n' >&0; grep 'HTTP/1.0 200'; } 0<>/dev/tcp/localhost/9000),
即無須映射埠。
9.8 健康聚合端點(bff-web)與作業台健康頁(2026-09-29)
緣由:
SDD-007 §5.1.2.3之桶 C「健康檢查與監控」(/system/health)原記
「須先於 bff-web 新增健康聚合端點方可實作」——本節即為該端點之規格。
依「規格先行」:先立本節 → 改tools/gen_openapi.py→ 重生契約 → 實作 →SDD-007 §5.4.24逐鈕定義 → 頁面。
9.8.1 為何前端不能直接呼叫各服務 /actuator/health
| 障礙 | 說明 |
|---|---|
| CORS | 各服務未對瀏覽器來源開放 |
| gateway 未代理 actuator | 前端無法經閘道取得 |
| bff-web 原僅 1 支聚合端點 | 無 /health 聚合 |
→ 故須由 bff-web 代為探測並聚合。
9.8.2 端點規格
GET /api/v1/bff/health(回應 BffHealth,200)
| 欄位 | 型別 | 說明 |
|---|---|---|
overall |
UP/DEGRADED/DOWN
|
UP=全部 UP;DEGRADED=部分 DOWN;DOWN=全部 DOWN |
checkedAt |
date-time |
本次檢查時刻 |
services[] |
array | 各服務:service/status(UP/DOWN)/latencyMs/error
|
9.8.3 行為要求
-
並行探測各服務之
/actuator/health;單一逾時不阻塞整體(逾時 2 秒)。 -
降級呈現:失敗者標
DOWN並帶error,不使整體失敗——
與BffSection之「單一下游失敗不拖垮整體」同一政策(SAD-004 §2.2)。 - 不快取——健康狀態須即時;快取會使畫面顯示過期狀態(比不顯示更危險)。
-
失敗即如實呈現,不得以樂觀預設值掩蓋(
CSP-001 §8 B2)。
9.8.4 ⚠️ 定位說明(回應 SDD-007 §5.1.2.3 之「本頁定位須先釐清」)
本端點與其頁面之定位為 作業台——維運人員之即時總覽(哪些服務 UP/DOWN)。
不取代 Prometheus/Grafana 之長期指標與告警——後者仍為 NFR Monitoring 之正式方案。
→ 頁面須明載此定位,避免使用者誤解為「監控看板」(瀏覽器畫面無法呈現歷史趨勢與告警規則)。
9.8.5 未竟
- 端點涵蓋之服務清單以
cardcore.services.*之組態為準;未組態者不列入(避免硬編 17 個服務)。 - 本端點不聚合 JVM/連線池等細部指標——那屬
/actuator/prometheus,仍由 Prometheus 蒐集。
9.8.6 實作註記:分層與目標埠(2026-09-30 修正,HANDOVER #A-105)
緣由:
HealthAggregationService(application)原直接注入
infrastructure.config.BffHealthProperties——違反CSP-002 §6.3之依賴方向
(interfaces → application → domain ← infrastructure),
LayeringTest.applicationMustNotDependOnInfrastructure於HEAD即紅。
修正採「應用層立埠」(非把屬性類別搬進 application):
| 角色 | 類別 | 說明 |
|---|---|---|
埠(application) |
HealthTargetsPort |
targets()/timeoutMs();沿用 AccountOverviewPort 既有慣例
|
實作(infrastructure) |
HealthTargetsConfigImpl |
適配 @ConfigurationProperties;原樣轉發、不補預設值
|
為何不搬類別:屬性類別本質是基礎設施之組態繫結,搬進 application 只是把違規搬家,
且讓應用層背負 Spring 繫結細節。立埠後應用層只依賴抽象,得以手寫 fake 測試(無須啟動 Spring)。
⚠️ 空
targets之語意不得放寬:回空 Map 代表「未組態」,
呼叫端必須回報DOWN(§9.8.3 #4),不得以樂觀預設值掩蓋。
埠之實作原樣轉發,即為確保此語意不被靜默補值。
測試(HealthAggregationServiceTest,3 例):以 JDK 內建 HttpServer 造「活的目標」、
以連不上的埠造「死的目標」——測真實網路行為(逾時、狀態碼判定、逾時/連線失敗之分流),
而非 stub 假造 HTTP 結果(後者測的是 stub,不是探測邏輯)。三例:空 targets→DOWN/
一活一死→DEGRADED(UP 帶 latencyMs、DOWN 如實帶原因)/全死→DOWN。
9.9 批次作業監控聚合端點(bff-web)與作業台批次頁(2026-09-30)
緣由:
SDD-007 §5.1.2.3之桶 C「批次作業監控」(/system/batch)——
本節為其後端端點之規格。與 §9.8 之健康聚合不同:本節聚合的是各服務的批次執行紀錄。⚠️ 前置變更(2026-09-30 補):聚合之前提是四服務各有「列出批次」端點——
實測四者皆無(僅有觸發與單筆查詢)→ 故先行新增四個GET列表端點(見 §9.9.1),
規格分載於SCS-011 §6.1/SCS-013 §6.1/SCS-015 §6.1/SCS-017 §6.1。
9.9.1 資料來源(⚠️ 原載「四個既有端點」與事實不符——已更正)
⚠️ 實測揭露(2026-09-30,
#A-104):本節原載「四個既有端點,皆已實作並真機驗證」——
經實測不成立:四個路徑皆只有POST(觸發),GET實測分別回
405/404/404/404。故本規格先行新增四個GET列表端點,bff-web改接之。
| 服務 | 列表端點(本規格新增) | 規格出處 | 狀態值域 | 時間錨點 |
|---|---|---|---|---|
statement-service |
GET /api/v1/statement-runs?limit=N |
SCS-011 §6.1 |
GENERATING/GENERATED/FAILED
|
startedAt |
clearing-service |
GET /api/v1/clearing/runs?limit=N |
SCS-013 §6.1 |
RUNNING/MATCHED/DIFFERENCE/FAILED
|
createdAt |
migration-service |
GET /api/v1/migration-jobs?limit=N |
SCS-015 §6.1 |
RUNNING/COMPLETED/FAILED
|
startedAt |
report-service |
GET /api/v1/report-jobs?limit=N |
SCS-017 §6.1 |
QUEUED/RUNNING/COMPLETED/FAILED
|
startedAt |
⚠️
clearing無startedAt欄位(CLEARING_RUNS僅有CREATED_AT/UPDATED_AT)——
故本聚合之時間錨點取值順序為startedAt→createdAt;皆取不到者留空,
不以當前時間或 0 填充(§9.9.4 #5不臆造原則)。
⚠️ 四服務之列表回應形狀各異(CycleRunView[]/ClearingRunSummary[]/
MigrationJobSummary[]/ReportJobSummary[])——本服務以firstText/firstLong之
別名鏈吸收差異(如jobId|runId),不強求四服務統一 DTO(那會使各服務背負
非其職責之欄位)。
9.9.2 ★ 統一狀態分組(本節核心)
四服務之狀態值域各不相同——若畫面直接顯示原始狀態,使用者無法一眼看出「哪些批次要注意」。
故定義統一分組(原始狀態一律如實保留,分組僅為呈現用):
| 分組 | 涵蓋之原始狀態 | 語意 |
|---|---|---|
PENDING |
QUEUED |
已排入、尚未開始 |
RUNNING |
GENERATING/RUNNING
|
執行中 |
SUCCEEDED |
GENERATED/MATCHED/COMPLETED
|
成功完成 |
WARNING |
DIFFERENCE |
完成但有差異(對帳不平——非失敗,但須人工確認) |
FAILED |
FAILED |
失敗 |
⚠️
DIFFERENCE不可歸為SUCCEEDED:對帳有差異之批次帳務上不等於成功,
歸為成功會使差異被忽略(假綠)。
⚠️ 分組不得取代原始狀態——回應中兩者並存,畫面以原始狀態為主、分組為輔(篩選/排序用)。
9.9.3 端點規格
GET /api/v1/bff/batch-jobs?limit=N(回應 BffBatchJobs,200)
| 參數 | 型別 | 說明 |
|---|---|---|
limit |
integer(可選) |
每服務回傳筆數上限(轉傳至四服務之 limit)。預設 10,上限 50
|
| 欄位 | 說明 |
|---|---|
checkedAt |
本次查詢時刻 |
jobs[] |
統一清單:jobType/service/jobId/status(原始)/statusGroup/processedCount/successCount/errorCount/startedAt/finishedAt/detail
|
unavailable[] |
查詢失敗之服務:service/error——如實呈現,不使整體失敗
|
jobType:STATEMENT_CYCLE/CLEARING_RUN/MIGRATION_JOB/REPORT_JOB。
9.9.4 行為要求
- 並行查詢四個服務;單一逾時不阻塞整體(逾時 3 秒——批次查詢較健康檢查重)。
-
部分失敗不拖垮整體:失敗之服務列入
unavailable[]並帶原因,其餘照常呈現。 -
合併後依
startedAt遞減(最近執行者在前)。 - 不快取——批次狀態須即時。
-
不得臆造:服務未回傳之欄位(如
clearing無processedCount)留空,
不以 0 或推測值填充(CSP-001 §8 B2)。
9.9.5 未竟
- 本端點僅查詢(監控);不提供觸發或中止批次之功能——那屬各服務之職責與權限範疇。
-
clearing之DIFFERENCE目前僅能呈現差異筆數/金額,無法下鑽至明細(需clearing另立端點)。
9.10 端點與連線管理聚合端點(bff-web)與作業台端點頁(2026-09-30)
緣由:
SDD-007 §5.1.2.3之桶 C「端點與連線管理」(/system/endpoints)——
本節為其後端端點之規格。SDD-007 §5.1.1另載:原型之「網路連線管理(拓樸/主機連線/容錯切換)」
已由本頁涵蓋(判定為重疊)。
9.10.1 本頁之兩個維度
| 維度 | 資料來源 | 說明 |
|---|---|---|
| 端點(路由) |
api-gateway 之 GET /api/v1/gateway/routes(自訂唯讀端點;原 actuator 版不可用,見 §9.10.2) |
各服務之入口路由(routeId/uri/predicates) |
| 連線(狀態) | 各服務 /actuator/health(與 §9.8 同源) |
該路由之目標服務是否連得上 |
★ 兩者並列才完整:只看路由不知通不通;只看健康不知入口在哪。
9.10.2 前置變更(gateway)——⚠️ 原設計經實測後修正(2026-09-30)
原擬:於 api-gateway 之 management.endpoints.web.exposure.include 加入 gateway,
以取用 /actuator/gateway/routes。
實測結果(本版本不可用):
| 檢查 | 結果 |
|---|---|
/actuator 索引 |
僅 7 個 endpoint:prometheus/beans/env/info/health/metrics/refresh——無 gateway
|
| 啟動日誌 | Exposing 7 endpoints beneath base path '/actuator' |
| 路由屬性之前綴 |
spring.cloud.gateway.server.**webflux**.routes[...](Spring Cloud Gateway 2025 已改前綴) |
→ gateway actuator endpoint 於本版本未被註冊(非 exposure 設定問題——已加入 include 仍無效)。
改採方案:自訂唯讀端點
GET /api/v1/gateway/routes(api-gateway 自身提供)
| 欄位 | 說明 |
|---|---|
id |
路由識別碼(如 authorization) |
uri |
目標 URI |
predicates |
述詞原文(如 Path=/auth-api/**) |
filters |
過濾器原文(如 RewritePath=/auth-api/(?<segment>.*), /api/$\{segment}) |
⚠️
$\{segment}之反斜線是組態原文,不是筆誤——application.yml即如此書寫;
Spring 之RewritePathGatewayFilterFactory會執行replacement.replace("$\\", "$")
再交給String.replaceAll(經javap -c確認)。本端點原文透傳、不代為去轉義
——去轉義等於隱藏真實組態,維運反而看不到「實際寫了什麼」。
要求:
- 唯讀——不提供新增/修改/刪除路由(避免誤觸造成路由中斷)。
-
僅供內部維運(
bff-web聚合用)——不對外;仍受 gateway 之認證保護。 - ⚠️ 不得以
management.endpoint.env.show-values=ALWAYS替代——
那會暴露所有環境變數(含憑證),安全風險,不可接受。
★ 實作要點(2026-09-30 實作,#A-103):
| 項目 | 決定 | 理由 |
|---|---|---|
| 資料來源 |
RouteDefinitionLocator(非 RouteLocator) |
前者為組態宣告、保留宣告順序且可還原原文;後者已解析為 Predicate/GatewayFilter 物件,無法還原原文(違反「原文透傳」) |
| 原文還原 |
Name=arg0, arg1(依 getArgs() 之 LinkedHashMap 插入順序串接) |
_genkey_0、_genkey_1… 之插入順序即組態順序(經 javap 確認欄位為 LinkedHashMap,非 HashMap) |
| 路由順序 | 維持宣告順序,不重排 | 與 application.yml 逐條對照(排序會失去對照性) |
| 回應格式 | 統一封套(CSP-003 §3),data 為路由陣列
|
全系統一致;bff-web 讀 data
|
| 端點路徑 |
/api/v1/gateway/routes(非 /actuator/**) |
自訂端點;與既有路由前綴(/auth-api/** 等)不重疊,不會被述詞攔截 |
| 追蹤碼取值 | 於 controller 方法進入時取(非於 map 內) |
map 之執行執行緒不保證仍帶 OTel 脈絡(需 Hooks.enableAutomaticContextPropagation()),於請求執行緒取值可確保回應 traceId 與日誌同源(CSP-004 §6) |
| 錯誤處理 | 不自訂錯誤碼 |
SDD-006 §3.4 無對應之 CC-GW-* 碼(僅 1001/1002/1003/4290/5001);不得自創碼。且本端點讀記憶體組態、無實際失敗路徑,失敗時由 bff-web 以 routesAvailable=false 降級呈現 |
★ 真機驗證(2026-09-30,#A-103):
| 驗證項 | 結果 |
|---|---|
GET /api/v1/gateway/routes(SSO 開啟、無 token) |
401——證實端點受閘道認證保護(要求 2「仍受認證保護」成立) |
GET /api/v1/gateway/routes(SSO 關閉) |
200、data 為 17 筆路由、順序=application.yml 宣告順序、predicates/filters 原文正確 |
GET /api/v1/bff/endpoints |
200、routesAvailable=true(修正前為 false)、routesError=null、17 筆路由且 service 由 routeId 正確推導(authorization → authorization-service) |
| 連線維度(未受影響) | 17 筆、UP 7/17——與「7 個領域服務在跑」一致(§9.10.4 #1 兩維度獨立成立) |
⚠️ 驗證方式之誠實揭露:取 Keycloak token 之指令遭安全審批攔截(逾時未授權),
故上表第 2 列改以暫時關閉 SSO 完成(CARDCORE_SECURITY_ENABLED=false之 JVM 覆寫,
不動sso.env檔);測畢已還原,並確認sso.sh status為「同調(兩側皆 true)」。
處置與#A-102 ⑦ (iii)相同(截圖期間暫時關閉 SSO)。
教訓:「actuator endpoint 存在」是需實測的假設,非查文件即可斷定;
且 Spring Cloud 版本演進會改變設定前綴與 endpoint 註冊條件。
9.10.3 端點規格
GET /api/v1/bff/endpoints(回應 BffEndpoints,200)
| 欄位 | 說明 |
|---|---|
checkedAt |
本次查詢時刻 |
routes[] |
routeId/uri/predicates[]/service(自 routeId 對映之服務名) |
connections[] |
service/status(UP/DOWN)/latencyMs/error
|
routesAvailable |
gateway 路由是否取得成功(false 時 routes[] 為空並附 routesError) |
9.10.4 行為要求
-
兩部分獨立:gateway 路由取不到不影響連線狀態之呈現(反之亦然)——
分開回報,不使整體失敗。 - 並行探測連線(逾時 2 秒,同 §9.8);gateway 路由查詢逾時 3 秒。
- 不快取。
- ⚠️
service之對映:由routeId推導(如authorization→authorization-service);
無法對映者保留routeId原文並標示,不臆造。 - ⚠️ 本頁不含「容錯切換」操作——僅呈現狀態;
切換屬維運程序(gateway 之refresh/部署流程),不由作業台直接觸發(避免誤觸造成路由中斷)。
9.10.5 未竟
-
predicates之呈現為原文透傳(如Path=/auth-api/**)——不解析為結構化欄位(避免過度設計)。 - 本端點不涵蓋非 HTTP 之連線(DB/Kafka/Redis)——那屬各服務之
/actuator/health細部
與 Prometheus 指標範疇(§9.8.5同一原則)。 - gateway 端點回傳之
filters(過濾器原文)未由bff-web呈現——§9.10.3之 BFF 契約
未含該欄位(作業台僅需「入口在哪」)。若日後需於頁面顯示改寫規則,須同步擴充
§9.10.3契約與SDD-007 §5.4.27,不逕行加欄位。 - gateway 端點無自訂錯誤碼:
SDD-006 §3.4之CC-GW-*僅 5 碼(1001/1002/1003/4290/5001),
無「路由查詢失敗」之語意,且CSP-003禁止 Coder 自創碼——故不設錯誤映射,
失敗時由bff-web以routesAvailable=false降級呈現(§9.10.4 #1)。
9.11 營運總覽聚合端點(bff-web)與作業台總覽頁(2026-10-01)
緣由:
SDD-007 §5.1.2.3之桶 C「營運總覽」(/dashboard)——原記
「跨服務聚合——須先於bff-web立聚合端點(§5.4.24之健康聚合為其子集)」。
依「規格先行」:先立本節 → 改tools/gen_openapi.py→ 重生契約 → 實作 →
SDD-007 §5.4.28逐鈕定義 → 頁面。
BEAM PMO 2026-10-01 裁定採「挑投報率最高者,先補後端、再規格先行、再實作」——本頁為選定目標。
9.11.1 ★ 範圍界定(務必先讀)
| 面向 | 本版 | 說明 |
|---|---|---|
| 系統營運面 | ✅ 本版 | 服務健康摘要/批次作業摘要/端點連線摘要——bff-web 自足,不需新增領域服務端點
|
| 業務 KPI 面 | ⚪ 第二階段 | 發卡量/授權量/待辦件數等,需各領域服務新增統計端點 |
⚠️ 回訪查證(2026-10-01):掃
contracts/openapi/*.yaml全數 17 服務,
皆無「統計/摘要」端點(僅 CRUD 與列表)。故業務 KPI 無法由既有端點推得,
不得以「服務有端點」推斷「已能呈現業務指標」。
→ 本版明載為系統營運面;業務 KPI 另立 CR(見§9.11.5)。
9.11.2 端點規格
GET /api/v1/bff/dashboard(回應 BffDashboard,200)
| 欄位 | 型別 | 說明 |
|---|---|---|
checkedAt |
date-time |
本次彙整時刻 |
services |
object |
total/up/down/overall(UP/DEGRADED/DOWN,判定同 §9.8.2) |
batch |
object | 依統一狀態分組(§9.9.2)計數:pending/running/succeeded/warning/failed/unknown/unavailable。⚠️ unknown 必須計入——服務回傳未定義狀態時如實歸此,不得略過(略過會使計數總和對不上,且掩蓋異常狀態) |
endpoints |
object |
routes(gateway 路由數)/connectionsUp/connectionsDown
|
各區塊皆可為 null(該區塊聚合失敗時)——前端須如實顯示「不可用」,不得以 0 充數。
9.11.3 行為要求
-
複用既有聚合服務(
HealthAggregationService/BatchMonitorService/
EndpointManagementService,同一 JVM)——不重複實作探測邏輯;
三者並行執行,避免回應時間線性累加(三區塊各自之逾時語意沿用原服務)。 -
區塊降級:任一區塊失敗 → 該區塊為
null,不拖垮整體——
與BffSection之「單一下游失敗不拖垮整體」同一政策(SAD-004 §2.2)。 -
不快取——與
§9.8.3 #3同(快取會使畫面顯示過期狀態)。 -
fail-closed:無目標 →
services.overall回DOWN(§9.8.6),不以樂觀預設值掩蓋。
9.11.4 與 §9.8~§9.10 之關係(非重複造頁)
本端點為摘要層(一眼看全貌);/system/health//system/batch//system/endpoints 為明細層(逐項檢視)。
→ /dashboard 不取代該三頁,亦不重複其探測邏輯(複用,見 §9.11.3 #1)。
9.11.5 未竟(誠實揭露)
-
業務 KPI 未納入(見
§9.11.1)——需各領域服務提供統計端點,建議另立 CR。 - 本端點不聚合歷史趨勢與告警規則——仍由 Prometheus/Grafana 負責(同
§9.8.4之定位說明)。
9.11.6 業務 KPI 區塊(BffDashboard.business,CR-026)
CR-026 補實作「業務 KPI 第二階段」——BffDashboard 新增 business 欄位(oneOf BffDashboardBusiness 或 null)。
聚合來源:各領域服務之統計端點(card-service cards/stats+card-applications/stats、authorization-service authorizations/stats、dispute-service disputes/stats),由 BusinessStatsService(application)並行 4 路 CompletableFuture 呼叫(沿用 token-relay 之 DelegatingSecurityContextExecutor 跨執行緒傳遞 SecurityContext),逾時 STATS_TIMEOUT_MS=5000。
business 之 4 子塊(各 oneOf schema-or-null,獨立降級):
| 子塊 | 來源端點 | 欄位 |
|---|---|---|
cards |
GET /cards/stats |
total/byStatus/byBlockCode
|
applications |
GET /card-applications/stats |
total/byStatus/pendingReview
|
authorizations |
GET /authorizations/stats |
total/today/byDecision/pendingUpload
|
disputes |
GET /disputes/stats |
total/byStatus/openCases
|
⚠️ 獨立降級:任一統計端點取不到(逾時/非 200/解析失敗)→ 該子塊為
null,不以 0 充數;其餘子塊不受影響。DashboardAggregationService之business區塊失敗 → 整體business=null(區塊級降級),與§9.11.3 #2同政策。
9.12 分流比例管理端點(api-gateway)與作業台分流頁(2026-10-01)
緣由:
SDD-007 §5.1.2.3桶 C 之「分流比例管理(/system/split-ratio)」原記
「僅 actuator」——即機制已實作(SAD-004 §2.4:CycleSplitDecision+
CycleSplitGlobalFilter+CycleSplitProperties,POC-13 已驗收),
惟只有POST /actuator/refresh,無管理端點:維運無法從作業台讀取當前比例,
亦無法在畫面調整/回退——必須登入機器改config/cycle-split.yml。
本節為該缺口之規格先行補建(比照#A-101/#A-102/#A-103模式)。
9.12.1 選頁理由(投報率)
| 比較項 | 說明 |
|---|---|
| 機制是否已存在 | ✅ 完整存在(SAD-004 §2.4;POC-13 七項驗收通過)——缺的只是管理面
|
| 是否需新領域模型 | ❌ 不需要(組態即資料;CycleSplitProperties 已為 @ConfigurationProperties) |
| 是否會造空殼/假資料 | ❌ 不會——讀的是真實生效值,寫的是真實熱更新檔 |
| 業務價值 | ★ 灰度放量與回退為遷移期最高頻之維運操作(BRD-001 §10.3 C-03 波次 W0→W4);回退速度直接對應事故處置時間 |
9.12.2 端點規格(api-gateway 自供;同 §9.10.2 之既有慣例)
| 方法 | 路徑 | 說明 |
|---|---|---|
GET |
/api/v1/gateway/split-ratio |
讀取當前生效之分流組態 |
PUT |
/api/v1/gateway/split-ratio |
調整 enabled/percentage 並觸發熱更新
|
SplitRatioView(回應):
| 欄位 | 型別 | 說明 |
|---|---|---|
enabled |
boolean | 是否啟用分流干預 |
percentage |
integer | 分流比例 0–100(新系統承接比例) |
legacyUri |
string | 舊系統(Legacy ECS)轉接目標 |
merchantHeader |
string | 承載商店代號之標頭(分流維度) |
pathPrefix |
string | 分流作用之路徑前綴 |
configFile |
string | null | 熱更新來源檔之實際路徑(解析 spring.config.import 之 file: 條目);未設定時為 null
|
configFileWritable |
boolean | 該檔是否存在且可寫(false 時 PUT 必失敗——先揭露,不讓維運白試) |
effectiveMatchesRequest |
boolean | null |
僅 PUT 回應:套用後重讀之值是否與請求一致(誠實驗證);GET 為 null
|
warning |
string | null |
僅 PUT 回應:effectiveMatchesRequest=false 時之說明;其餘為 null
|
SplitRatioUpdateRequest(請求):enabled(boolean,選填)、percentage(integer,必填、0..100)。
9.12.3 行為要求
-
驗證:
percentage必填且須為整數 0–100 → 不符回400CC-PLT-4003(不自行另立錯誤碼,CSP-003 §2)。⚠️ 由控制器自行驗證並組裝錯誤封套——本模組無common-spring之全域例外處理器(該類別非 auto-configuration,且本模組未掃描其套件),故不使用 Bean Validation 之自動例外轉譯。 -
定點行替換寫檔:僅改寫
cycle-split.yml之enabled:/percentage:兩行——保留檔內註解與其餘鍵;不重寫整檔(避免抹掉維運註記)。 -
觸發熱更新:呼叫
ContextRefresher.refresh()(等同POST /actuator/refresh,SAD-004 §2.4之既有機制)——不重啟。 -
回讀驗證(★ 誠實原則):由重綁後之
CycleSplitProperties讀回實際生效值;與請求不符時effectiveMatchesRequest=false+warning(不以請求值冒充生效值)。 -
稽核:記
INFO日誌(traceId、before→after、目標檔)——對應SAD-004 §2.4之「每一筆轉接均須有可追溯之 LOG」之同源要求。 -
失敗不靜默:檔不可寫/refresh 拋錯 →
500CC-PLT-5000並帶原因(不得回 200 假成功)。 - 不快取。
-
安全性:端點不另設白名單——沿用閘道之既有認證鏈(
GatewaySecurityConfig:啟用時除健康探測外一律須驗 JWT)。⚠️ 角色級授權未實作(見 §9.12.5)。
9.12.4 前端(/system/split-ratio)
依 SDD-007 §5.4.30 之逐鈕定義實作;開發期由 Vite 代理 /gateway-api → api-gateway:8100。
9.12.5 未竟(誠實揭露)
-
角色級授權未實作:任何通過閘道認證之使用者皆可調整比例——正式環境應限維運角色(待
SAD-005 §4之角色矩陣落地)。 -
未寫入稽核表:僅記日誌(無
audit_trail落庫)——閘道無資料庫(SAD-001 AP-02)。 - 不支援回退歷史:不保存比例變更歷程(需查日誌)。
-
僅單一組態檔:多實例部署時各實例之
cycle-split.yml須自行同步(本端點只改本實例之檔)——⚠️ 正式環境應改為集中組態來源(如 Config Server)。 -
請求主體非合法 JSON 時之回應非本封套:該情境由 WebFlux 於進入控制器前擋下,回應為框架預設格式(非
ApiResponse)——此為已知落差(CSP-003 §3要求所有回應一律套用封套;本模組無全域例外處理器,故無法覆蓋此路徑)。 -
GET讀的是生效值、非檔原文:若組態檔與生效值不一致(例如改檔後未 refresh),GET呈現的是生效值——此為刻意(維運要問的是「現在怎麼分流」),惟畫面不呈現檔內原文。
9.5 已知限制(本次未解決,誠實揭露)
-
service_name標籤恆為unknown_service——實測三種寫法
(move to: resource[...]、add field: resource[...] value: EXPR(...)、OTTL 括號鏈式)
皆無法將service.name寫入 resource,且不報錯。
替代:Loki 自動萃取 body 之service.name為service_name_extracted,仍可依服務辨識。
根治方案:改為「每服務一個 filelog receiver + 靜態 resource」,或改採 Grafana Alloy。 -
ECS 欄位名與→ ✅ 已解除(BEAM PMO 裁定「改規格」,CSP-004 §3不一致CSP-004升 1.1)。
原落差:規格要求ts/level/msg/correlationId/event,
實測輸出為@timestamp/log.level/message,且無event。
處置(規格遷就實測,非放寬要求):CSP-004 §3.1欄位對照改為實測值;
§3.2 明載event事件碼本期不實作(專案尚未定義事件碼表,且CSP-003 §2禁止 Coder 自創碼,
故不設恆為空值之欄位);§6 廢除第二套correlationId,統一以 OTeltraceId為準。
★ 2026-09-21 複核實測(真實posting-service.json,137 筆):
@timestamp137/log(含level)137/service(含name)137/message137/
traceId4/spanId4/ecs.version137——與CSP-004 §3.1完全一致,且無event欄位(符合 §3.2)。
traceId僅 4 筆係取樣率TRACING_SAMPLING預設 0.1 所致,非缺漏。
樣本:{"@timestamp":"…","log":{"level":"DEBUG",…},"service":{"name":"posting-service",…}, "message":"…","traceId":"ce98fa43…","spanId":"ed89ddf5…","ecs":{"version":"8.11"}} - ✅ API 回應之
traceId與日誌之traceId為不同值 → 已修(2026-09-20,C-29程式面)。
實測同一請求:回應10b8a469…、日誌894ff808…,兩套 ID 無法相互對照。
根因比「有兩套 ID」更嚴重(本次實測盤點才發現):common-spring之
CorrelationIdFilter從未被註冊為 bean——AutoConfiguration.imports只登錄
Security×2 + ServiceClient,該類別亦無@Component,故 17 個服務之 component scan
皆不拾取。結果MDC.get("trace_id")恆為null,TraceContext.currentTraceId()
每次呼叫都現產一個新的亂數——回應永遠帶著一個「看似合法」之 32 碼,
實則與任何日誌都對不起來。屬**「靜默不做」型缺陷**(不報錯、不為空、功能卻不存在)。
處置:依CSP-004 §6(BEAM PMO 2026-09-20 裁定「以 OTel trace ID 為準」),
TraceContext改為優先取 OTel 當前 span 之 traceId,其次 MDC,最後才產生;
OTel 缺席時降級並只記一次 WARN(使「刻意不接」與「忘了接」可區分)。
新增TraceContextTest7 例,並以負樣本驗證判定力(停用 OTel 優先 → 3 例精準轉紅)。
common-spring53 → 60 測試、0 失敗。
✅ 已裁定並實作(BEAM PMO 2026-09-21 18:57:保留並註冊):CorrelationIdFilter由
CorrelationIdAutoConfiguration(已登錄AutoConfiguration.imports)以FilterRegistrationBean
註冊,並顯式指定ORDER(LOWEST_PRECEDENCE - 100)以確保晚於 OTel 觀測範圍——
故 MDC 與 OTel 不會出現兩個值(resolve()之優先序:OTel span →traceparent→X-Correlation-Id→ 產生)。
測試:CorrelationIdFilterTest/TraceContextTest。啟動日誌可見Filter 'correlationIdFilter' configured for use。
⚠️ 原「待裁定/未擅自註冊」之記載係過期(早於 09-21 之裁定),已更正。 -
僅→ 已於 §9.6 推廣至 19/19 並逐模組實測party-service完成接線(觀測 1/17) -
結構化日誌無輪轉設定→ ✅ 已查明為誤載(2026-09-21 實測):
原記載「logging.file.name未配置max-size/max-history,長時間執行會持續增長」——
實測顯示輪轉正常發生:ops/local/logs/內確有
posting-service.json.2026-09-20.0.gz/statement-service.json.…/api-gateway.json.…等
5 個輪轉檔,logs 目錄總量僅 1.4M。
原因:Spring Boot 於啟用logging.file.name時即帶有預設輪轉組態
(max-size10MB、max-history7、檔名樣式${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz),
毋須於application.yml顯式宣告即生效——「組態檔沒寫」≠「沒啟用」。
⚠️ 仍存之真實顧慮(非「無輪轉」):total-size-cap預設為 0(無上限);
19 個服務之最壞情況約為7 × 10MB × 19 ≈ 1.3GB。
✅ 已裁定並實作(BEAM PMO 2026-10-01:5GB;GOV-007 §3):19 個application.yml均設
logging.logback.rollingpolicy.total-size-cap: 5GB(並同步產生器模板)。
⚠️ 併更正本節原建議之屬性名:原寫logging.file.total-size-cap,該屬性不存在;
Spring Boot 4.1.1 之正確屬性為logging.logback.rollingpolicy.total-size-cap
(預設0B=無上限;實查spring-configuration-metadata.json確認)。
⚠️ per-service 語意(誠實揭露):Logback 之total-size-cap係 per-appender;
本地 19 模組共用ops/local/logs/時,理論上限為「19 × 5GB」——惟max-history: 7×max-file-size: 10MB
先約束單服務約 70MB,故本地實測總量(~1.3GB)遠低於 5GB;設 5GB 為硬天花板
(供日後放寬max-history時仍受約束)。若需「本地目錄總量 ≤ 5GB」之硬保證,應改設每服務 ≈ 256MB。 - Grafana 儀表板尚未建立(§8 已列為待 SRE 凍結)。