现象:CmsSiteDirectoryImpl 两处 catch 只打 e.getMessage(),而平台 SDK 统一把 传输层失败包成 "OpenAPI request failed: ioError",底层 ConnectException / SocketTimeoutException / SSLException 全部丢失,日志分不清"对端没起"还是"读超时"。 改动: - 新增 rootCauseBrief():沿 cause 链取最内层原因追加为"(根因 X: msg)"; 无 cause 时不追加,避免与 e.getMessage() 重复。 - resolveSite / listSites 两处 catch 日志接入该方法。 契约不变:路径 /oapi/site/list、per-target basePath /cms、fail-closed 语义均未动。 测试:CmsSiteDirectoryImplTest 新增 1 例断言摘要形态;mvn test 82/82 绿。
This commit is contained in:
@@ -65,7 +65,7 @@ public class CmsSiteDirectoryImpl implements SiteDirectory {
|
||||
// 由 CmsSiteDirectoryImplTest 冻结)。仅用于 siteCode 反解,权限判定仍走 requireSchoolMember。
|
||||
sites = fetchSites(null);
|
||||
} catch (Exception e) {
|
||||
log.warn("[svc-hall] CMS 站点目录不可达:{}", e.getMessage());
|
||||
log.warn("[svc-hall] CMS 站点目录不可达:{}{}", e.getMessage(), rootCauseBrief(e));
|
||||
return null;
|
||||
}
|
||||
if (sites != null) {
|
||||
@@ -93,7 +93,8 @@ public class CmsSiteDirectoryImpl implements SiteDirectory {
|
||||
try {
|
||||
return fetchSites(schId);
|
||||
} catch (Exception e) {
|
||||
log.warn("[svc-hall] CMS 站点列表获取失败(schId={}):{}", schId, e.getMessage());
|
||||
log.warn("[svc-hall] CMS 站点列表获取失败(schId={}):{}{}",
|
||||
schId, e.getMessage(), rootCauseBrief(e));
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "站点服务不可达,请稍后重试");
|
||||
}
|
||||
});
|
||||
@@ -124,6 +125,20 @@ public class CmsSiteDirectoryImpl implements SiteDirectory {
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* 最内层原因摘要。SDK 的 "OpenAPI request failed: ioError" 只说明请求没拿到响应,
|
||||
* 真正的原因是 cause 链末端的 ConnectException/SocketTimeoutException/SSLException;
|
||||
* 不补这一段,日志里永远分不清"对端没起"和"读超时"。
|
||||
* 包可见:契约测试直接断言摘要形态。
|
||||
*/
|
||||
static String rootCauseBrief(Throwable e) {
|
||||
Throwable cur = e;
|
||||
while (cur.getCause() != null && cur.getCause() != cur) {
|
||||
cur = cur.getCause();
|
||||
}
|
||||
return cur == e ? "" : "(根因 " + cur.getClass().getSimpleName() + ": " + cur.getMessage() + ")";
|
||||
}
|
||||
|
||||
/** resolveSite 的全量缓存键(与真实 schId 不冲突的哨兵) */
|
||||
private static final String ALL_SITES_KEY = "-1";
|
||||
}
|
||||
|
||||
@@ -123,4 +123,20 @@ class CmsSiteDirectoryImplTest {
|
||||
void listSites_rejectsNullSchId() {
|
||||
assertThrows(BizException.class, () -> directory.listSites(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("rootCauseBrief:ioError 包装层要透出最内层原因(区分\"对端没起\"与\"读超时\")")
|
||||
void rootCauseBrief_exposesInnermostCause() {
|
||||
// 无 cause:不追加,避免与 e.getMessage() 重复
|
||||
assertEquals("", CmsSiteDirectoryImpl.rootCauseBrief(
|
||||
new RuntimeException("OpenAPI request failed: ioError")));
|
||||
|
||||
// 实际形态:OpenApiException(ioError) ← ResourceAccessException ← ConnectException
|
||||
java.net.ConnectException refused = new java.net.ConnectException("Connection refused: connect");
|
||||
Exception wrapped = new RuntimeException("OpenAPI request failed: ioError",
|
||||
new RuntimeException("I/O error on GET request", refused));
|
||||
String brief = CmsSiteDirectoryImpl.rootCauseBrief(wrapped);
|
||||
assertTrue(brief.contains("ConnectException"), brief);
|
||||
assertTrue(brief.contains("Connection refused: connect"), brief);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user