This is an automated email from the ASF dual-hosted git repository.
fanjia pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/seatunnel.git
The following commit(s) were added to refs/heads/dev by this push:
new 170acece40 [Fix][engine-server] Fix the issue of garbled characters in
log output (#9594)
170acece40 is described below
commit 170acece40ba318342535e632c8c1ee6040de98c
Author: liucongjy <[email protected]>
AuthorDate: Thu Jul 31 20:56:23 2025 +0800
[Fix][engine-server] Fix the issue of garbled characters in log output
(#9594)
---
.../engine/server/rest/servlet/BaseServlet.java | 22 ++--
.../engine/server/rest/BaseServletTest.java | 113 +++++++++++++++++++++
.../src/test/resources/log4j2-test.properties | 24 +++++
3 files changed, 152 insertions(+), 7 deletions(-)
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/rest/servlet/BaseServlet.java
b/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/rest/servlet/BaseServlet.java
index ba9727dab7..b373211f28 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/rest/servlet/BaseServlet.java
+++
b/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/rest/servlet/BaseServlet.java
@@ -46,47 +46,55 @@ public class BaseServlet extends HttpServlet {
}
protected void writeJson(HttpServletResponse resp, Object obj) throws
IOException {
- resp.setContentType("application/json");
+ resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
+ resp.setContentType("application/json; charset=UTF-8");
resp.getWriter().write(new Gson().toJson(obj));
}
protected void writeJson(HttpServletResponse resp, JsonArray jsonArray)
throws IOException {
- resp.setContentType("application/json");
+ resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
+ resp.setContentType("application/json; charset=UTF-8");
resp.getWriter().write(jsonArray.toString());
}
protected void writeJson(HttpServletResponse resp, JsonObject jsonObject)
throws IOException {
- resp.setContentType("application/json");
+ resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
+ resp.setContentType("application/json; charset=UTF-8");
resp.getWriter().write(jsonObject.toString());
}
protected void writeJson(HttpServletResponse resp, JsonArray jsonArray,
int statusCode)
throws IOException {
- resp.setContentType("application/json");
+ resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
+ resp.setContentType("application/json; charset=UTF-8");
resp.setStatus(statusCode);
resp.getWriter().write(jsonArray.toString());
}
protected void writeJson(HttpServletResponse resp, JsonObject jsonObject,
int statusCode)
throws IOException {
- resp.setContentType("application/json");
+ resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
+ resp.setContentType("application/json; charset=UTF-8");
resp.setStatus(statusCode);
resp.getWriter().write(jsonObject.toString());
}
protected void writeJson(HttpServletResponse resp, Object obj, int
statusCode)
throws IOException {
- resp.setContentType("application/json");
+ resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
+ resp.setContentType("application/json; charset=UTF-8");
resp.setStatus(statusCode);
resp.getWriter().write(new Gson().toJson(obj));
}
protected void write(HttpServletResponse resp, Object obj) throws
IOException {
- resp.setContentType("text/plain");
+ resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
+ resp.setContentType("text/plain; charset=UTF-8");
resp.getWriter().write(obj.toString());
}
protected void writeHtml(HttpServletResponse resp, Object obj) throws
IOException {
+ resp.setCharacterEncoding(StandardCharsets.UTF_8.name());
resp.setContentType("text/html; charset=UTF-8");
resp.getWriter().write(obj.toString());
}
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/BaseServletTest.java
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/BaseServletTest.java
new file mode 100644
index 0000000000..3fec682dba
--- /dev/null
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/java/org/apache/seatunnel/engine/server/rest/BaseServletTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.seatunnel.engine.server.rest;
+
+import org.apache.seatunnel.engine.common.config.SeaTunnelConfig;
+import org.apache.seatunnel.engine.common.config.server.HttpConfig;
+import org.apache.seatunnel.engine.common.runtime.ExecutionMode;
+import org.apache.seatunnel.engine.common.utils.PassiveCompletableFuture;
+import org.apache.seatunnel.engine.core.dag.logical.LogicalDag;
+import org.apache.seatunnel.engine.core.job.JobImmutableInformation;
+import org.apache.seatunnel.engine.server.AbstractSeaTunnelServerTest;
+import org.apache.seatunnel.engine.server.SeaTunnelServer;
+import org.apache.seatunnel.engine.server.SeaTunnelServerStarter;
+import org.apache.seatunnel.engine.server.TestUtils;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import com.hazelcast.config.Config;
+import com.hazelcast.internal.serialization.Data;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.util.Collections;
+
+class BaseServletTest extends AbstractSeaTunnelServerTest {
+
+ private static final int HTTP_PORT = 18080;
+
+ private static final Long JOB_1 = System.currentTimeMillis() + 1L;
+
+ @BeforeEach
+ void setUp() {
+ String name = this.getClass().getName();
+ Config hazelcastConfig = Config.loadFromString(getHazelcastConfig());
+
hazelcastConfig.setClusterName(TestUtils.getClusterName("RestApiServletTest_" +
name));
+ SeaTunnelConfig seaTunnelConfig = loadSeaTunnelConfig();
+ seaTunnelConfig.setHazelcastConfig(hazelcastConfig);
+ seaTunnelConfig.getEngineConfig().setMode(ExecutionMode.LOCAL);
+
+ HttpConfig httpConfig =
seaTunnelConfig.getEngineConfig().getHttpConfig();
+ httpConfig.setEnabled(true);
+ httpConfig.setPort(HTTP_PORT);
+
+ instance =
SeaTunnelServerStarter.createHazelcastInstance(seaTunnelConfig);
+ nodeEngine = instance.node.nodeEngine;
+ server = nodeEngine.getService(SeaTunnelServer.SERVICE_NAME);
+ LOGGER = nodeEngine.getLogger(AbstractSeaTunnelServerTest.class);
+ }
+
+ @Test
+ void testWriteJsonWithObject() throws IOException {
+ startJob(JOB_1, "fake_to_console.conf");
+ testLogRestApiResponse("html");
+ testLogRestApiResponse("JSON");
+ }
+
+ public void testLogRestApiResponse(String format) throws IOException {
+ HttpURLConnection conn = null;
+ try {
+ java.net.URL url =
+ new java.net.URL("http://localhost:" + HTTP_PORT +
"/logs?format=" + format);
+ conn = (HttpURLConnection) url.openConnection();
+
+ Assertions.assertEquals(200, conn.getResponseCode());
+ Assertions.assertTrue(
+ conn.getHeaderFields()
+ .get("Content-Type")
+ .toString()
+ .contains("charset=utf-8"));
+ } finally {
+ if (conn != null) {
+ conn.disconnect();
+ }
+ }
+ }
+
+ private void startJob(Long jobId, String path) {
+ LogicalDag testLogicalDag = TestUtils.createTestLogicalPlan(path,
jobId.toString(), jobId);
+
+ JobImmutableInformation jobImmutableInformation =
+ new JobImmutableInformation(
+ jobId,
+ "Test",
+ nodeEngine.getSerializationService(),
+ testLogicalDag,
+ Collections.emptyList(),
+ Collections.emptyList());
+
+ Data data =
nodeEngine.getSerializationService().toData(jobImmutableInformation);
+
+ PassiveCompletableFuture<Void> voidPassiveCompletableFuture =
+ server.getCoordinatorService()
+ .submitJob(jobId, data,
jobImmutableInformation.isStartWithSavePoint());
+ voidPassiveCompletableFuture.join();
+ }
+}
diff --git
a/seatunnel-engine/seatunnel-engine-server/src/test/resources/log4j2-test.properties
b/seatunnel-engine/seatunnel-engine-server/src/test/resources/log4j2-test.properties
index d40ba6afd7..cb58829e4f 100644
---
a/seatunnel-engine/seatunnel-engine-server/src/test/resources/log4j2-test.properties
+++
b/seatunnel-engine/seatunnel-engine-server/src/test/resources/log4j2-test.properties
@@ -18,8 +18,15 @@
rootLogger.level = INFO
+property.file_path = ${sys:seatunnel.logs.path:-logs}
+property.file_name = ${sys:seatunnel.logs.file_name:-seatunnel}
+property.file_split_size = 100MB
+property.file_count = 100
+property.file_ttl = 7d
+
rootLogger.appenderRef.consoleStdout.ref = consoleStdoutAppender
rootLogger.appenderRef.consoleStderr.ref = consoleStderrAppender
+rootLogger.appenderRef.file.ref = routingAppender
appender.consoleStdout.name = consoleStdoutAppender
appender.consoleStdout.type = CONSOLE
@@ -40,3 +47,20 @@ appender.consoleStderr.filter.acceptGteWarn.type =
ThresholdFilter
appender.consoleStderr.filter.acceptGteWarn.level = WARN
appender.consoleStderr.filter.acceptGteWarn.onMatch = ACCEPT
appender.consoleStderr.filter.acceptGteWarn.onMismatch = DENY
+
+appender.routing.name = routingAppender
+appender.routing.type = Routing
+appender.routing.purge.type = IdlePurgePolicy
+appender.routing.purge.timeToLive = 60
+appender.routing.purge.checkInterval = 1
+appender.routing.route.type = Routes
+appender.routing.route.pattern = $${ctx:ST-JID}
+appender.routing.route.system.type = Route
+appender.routing.route.system.key = $${ctx:ST-JID}
+appender.routing.route.system.ref = fileAppender
+appender.routing.route.job.type = Route
+appender.routing.route.job.appender.type = File
+appender.routing.route.job.appender.name = job-${ctx:ST-JID}
+appender.routing.route.job.appender.fileName =
${file_path}/job-${ctx:ST-JID}.log
+appender.routing.route.job.appender.layout.type = PatternLayout
+appender.routing.route.job.appender.layout.pattern = %d{yyyy-MM-dd
HH:mm:ss,SSS} %-5p [%-30.30c{1.}] [%t] - %m%n