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

Reply via email to