This is an automated email from the ASF dual-hosted git repository.
dspavlov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite-teamcity-bot.git
The following commit(s) were added to refs/heads/master by this push:
new 293dc2c1 Show root cause for server errors (#218)
293dc2c1 is described below
commit 293dc2c18d9cb511e415d870226a5f345804b407
Author: ignitetcbot <[email protected]>
AuthorDate: Sat May 9 14:13:30 2026 +0300
Show root cause for server errors (#218)
Codex co-authored-by: Dmitriy Pavlov <[email protected]>
---
.../web/rest/exception/ExeptionsTraceLogger.java | 90 +++++++++++++++++++++-
.../src/main/webapp/js/common-1.7.js | 6 +-
.../rest/exception/ExeptionsTraceLoggerTest.java | 47 +++++++++++
3 files changed, 140 insertions(+), 3 deletions(-)
diff --git
a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/exception/ExeptionsTraceLogger.java
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/exception/ExeptionsTraceLogger.java
index bf37b809..a0ed8218 100644
---
a/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/exception/ExeptionsTraceLogger.java
+++
b/ignite-tc-helper-web/src/main/java/org/apache/ignite/ci/web/rest/exception/ExeptionsTraceLogger.java
@@ -16,6 +16,10 @@
*/
package org.apache.ignite.ci.web.rest.exception;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Set;
+import javax.xml.bind.JAXBException;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.core.Response;
@@ -31,6 +35,9 @@ public class ExeptionsTraceLogger implements
ExceptionMapper<Throwable> {
/** Logger. */
private static final Logger logger =
LoggerFactory.getLogger(ExeptionsTraceLogger.class);
+ /** Max causes to include in HTTP response. */
+ private static final int MAX_CAUSE_DEPTH = 8;
+
/** {@inheritDoc} */
@Override public Response toResponse(Throwable t) {
logger.error("Error during processing request (Internal Server Error
[500]). Caused by: ", t);
@@ -38,6 +45,85 @@ public class ExeptionsTraceLogger implements
ExceptionMapper<Throwable> {
if
(Boolean.valueOf(System.getProperty(TcBotSystemProperties.DEV_MODE)))
t.printStackTrace();
- return Response.serverError().entity(t.getMessage()).build();
+ return Response.serverError().entity(errorMessage(t)).build();
+ }
+
+ /**
+ * @param t Exception.
+ * @return Short user-facing error text with the useful root cause
preserved.
+ */
+ private static String errorMessage(Throwable t) {
+ StringBuilder res = new StringBuilder("Internal Server Error [500].");
+ Throwable root = rootCause(t);
+ String rootMsg = formatCause(root);
+
+ if (!rootMsg.isEmpty())
+ res.append("\nReason: ").append(rootMsg);
+
+ res.append("\n\nCause chain:");
+
+ Set<Throwable> seen = Collections.newSetFromMap(new
IdentityHashMap<>());
+ Throwable cur = t;
+ int depth = 0;
+
+ while (cur != null && seen.add(cur) && depth < MAX_CAUSE_DEPTH) {
+ res.append("\n- ").append(formatCause(cur));
+
+ cur = nextCause(cur);
+ depth++;
+ }
+
+ if (cur != null)
+ res.append("\n- ...");
+
+ return res.toString();
+ }
+
+ /**
+ * @param t Exception.
+ * @return Root cause, including JAXB linked exceptions.
+ */
+ private static Throwable rootCause(Throwable t) {
+ Set<Throwable> seen = Collections.newSetFromMap(new
IdentityHashMap<>());
+ Throwable cur = t;
+ Throwable next;
+
+ while (cur != null && seen.add(cur) && (next = nextCause(cur)) != null)
+ cur = next;
+
+ return cur == null ? t : cur;
+ }
+
+ /**
+ * @param t Exception.
+ * @return Next cause.
+ */
+ private static Throwable nextCause(Throwable t) {
+ Throwable cause = t.getCause();
+
+ if (cause != null)
+ return cause;
+
+ if (t instanceof JAXBException)
+ return ((JAXBException)t).getLinkedException();
+
+ return null;
+ }
+
+ /**
+ * @param t Exception.
+ * @return Cause text.
+ */
+ private static String formatCause(Throwable t) {
+ if (t == null)
+ return "";
+
+ String msg = t.getMessage();
+ String cls = t.getClass().getSimpleName();
+
+ if (msg == null || msg.trim().isEmpty())
+ return cls;
+
+ return cls + ": " + msg.replace('\r', ' ').replace('\n', ' ').trim();
}
-}
\ No newline at end of file
+}
diff --git a/ignite-tc-helper-web/src/main/webapp/js/common-1.7.js
b/ignite-tc-helper-web/src/main/webapp/js/common-1.7.js
index 7d440a5c..1773bd17 100644
--- a/ignite-tc-helper-web/src/main/webapp/js/common-1.7.js
+++ b/ignite-tc-helper-web/src/main/webapp/js/common-1.7.js
@@ -110,7 +110,11 @@ function showErrInLoadStatus(jqXHR, exception) {
} else if (jqXHR.status === 424) {
$("#loadStatus").html('Dependency problem: [424]: ' +
jqXHR.responseText);
} else if (jqXHR.status === 500) {
- $("#loadStatus").html('Internal Server Error [500].');
+ var serverMsg = isDefinedAndFilled(jqXHR.responseText)
+ ? jqXHR.responseText
+ : 'Internal Server Error [500].';
+
+ $("#loadStatus").text(serverMsg);
} else if (exception === 'parsererror') {
$("#loadStatus").html('Requested JSON parse failed.');
} else if (exception === 'timeout') {
diff --git
a/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/web/rest/exception/ExeptionsTraceLoggerTest.java
b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/web/rest/exception/ExeptionsTraceLoggerTest.java
new file mode 100644
index 00000000..b75fedee
--- /dev/null
+++
b/ignite-tc-helper-web/src/test/java/org/apache/ignite/ci/web/rest/exception/ExeptionsTraceLoggerTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.ci.web.rest.exception;
+
+import java.net.SocketException;
+import javax.ws.rs.core.Response;
+import javax.xml.bind.UnmarshalException;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ *
+ */
+public class ExeptionsTraceLoggerTest {
+ /** */
+ @Test
+ public void responseContainsRootCauseFromJaxbLinkedException() {
+ SocketException socketException = new SocketException("Connection
reset");
+ UnmarshalException unmarshalException = new UnmarshalException("Failed
to parse TeamCity XML", socketException);
+ RuntimeException exception = new RuntimeException("TeamCity request
failed", unmarshalException);
+
+ Response response = new ExeptionsTraceLogger().toResponse(exception);
+
+ String msg = (String)response.getEntity();
+
+ assertTrue(msg.contains("Internal Server Error [500]."));
+ assertTrue(msg.contains("Reason: SocketException: Connection reset"));
+ assertTrue(msg.contains("RuntimeException: TeamCity request failed"));
+ assertTrue(msg.contains("UnmarshalException: Failed to parse TeamCity
XML"));
+ }
+}