You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@seata.apache.org by ji...@apache.org on 2026/09/28 07:04:42 UTC

(incubator-seata) branch 2.x updated: test: verify Logstash appender delivery through a local TCP sink (#8271)

This is an automated email from the ASF dual-hosted git repository.

funky-eyes pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/incubator-seata.git


The following commit(s) were added to refs/heads/2.x by this push:
     new 69bde83bc8 test: verify Logstash appender delivery through a local TCP sink (#8271)
69bde83bc8 is described below

commit 69bde83bc890f3279e50e67a867cc913b0bcd9ac
Author: jimin <sl...@163.com>
AuthorDate: Mon Sep 28 15:04:36 2026 +0800

    test: verify Logstash appender delivery through a local TCP sink (#8271)
---
 changes/en-us/2.x.md                               |   2 +-
 changes/zh-cn/2.x.md                               |   4 +-
 .../apache/seata/server/logging/AppenderTest.java  | 103 ++++++++++++++++++++-
 3 files changed, 105 insertions(+), 4 deletions(-)

diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md
index fd4a3873e8..4d16d4cdb2 100644
--- a/changes/en-us/2.x.md
+++ b/changes/en-us/2.x.md
@@ -52,8 +52,8 @@ Add changes here for all PR submitted to the 2.x branch.
 
 ### test:
 
+- [[#8271](https://github.com/apache/incubator-seata/pull/8271)] verify Logstash appender delivery with a local TCP test sink
 - [[#8261](https://github.com/apache/incubator-seata/pull/8261)] isolate TM bootstrap option tests from external server connections
-
 - [[#8146](https://github.com/apache/incubator-seata/pull/8146)] test: cover rm-datasource resource id initializers
 - [[#8175](https://github.com/apache/incubator-seata/pull/8175)] fix flaky testCachedConfigurationChangeListener due to async race condition
 - [[#8181](https://github.com/apache/incubator-seata/pull/8181)] fix AuthControllerWithRandomPasswordTest flaky failure caused by Spring Boot test context caching
diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md
index cbc3911b82..38c3c962ac 100644
--- a/changes/zh-cn/2.x.md
+++ b/changes/zh-cn/2.x.md
@@ -30,7 +30,7 @@
 - [[#8278](https://github.com/apache/incubator-seata/pull/8278)] 保护 Fastjson2 JSONB 并发解码,避免 RPC 与 UndoLog 引用字段丢失
 - [[#8138](https://github.com/apache/incubator-seata/pull/8138)] 修复 TCC fence 清理时误删同一全局事务中仍在进行(TRIED)或未过期的分支记录的问题
 - [[#8191](https://github.com/apache/incubator-seata/pull/8191)] 修复默认本地地址缓存后 NetUtil 参数化查询绕过 ignoredInterfaces 和 preferredNetworks 的问题
-- [#8145](https://github.com/apache/incubator-seata/pull/8145) 修复达梦(DM)数据库下全局锁批量获取被误判为失败的问题
+- [[#8145](https://github.com/apache/incubator-seata/pull/8145)] 修复达梦(DM)数据库下全局锁批量获取被误判为失败的问题
 - [[#8157](https://github.com/apache/incubator-seata/pull/8157)] 修复 Spring Boot 4.x 下由于硬编码 `DataSourceAutoConfiguration` 类引用导致 Saga 自动配置被跳过的问题
 - [[#8160](https://github.com/apache/incubator-seata/pull/8160)] 修复 fastjson2 JSONB 并发反序列化问题
 - [[#8179](https://github.com/apache/incubator-seata/pull/8179)] 将 console 模块的 java.version 设置为 17 以修复编译错误
@@ -51,8 +51,8 @@
 
 ### test:
 
+- [[#8271](https://github.com/apache/incubator-seata/pull/8271)] 使用本地 TCP 接收端校验 Logstash appender 的实际日志投递
 - [[#8261](https://github.com/apache/incubator-seata/pull/8261)] 隔离 TM 启动参数测试对外部服务连接的依赖
-
 - [[#8146](https://github.com/apache/incubator-seata/pull/8146)] test: 覆盖 rm-datasource resource id initializers
 - [[#8175](https://github.com/apache/incubator-seata/pull/8175)] 修复 testCachedConfigurationChangeListener 因异步竞态条件导致的偶发失败
 - [[#8181](https://github.com/apache/incubator-seata/pull/8181)] 修复 AuthControllerWithRandomPasswordTest 因 Spring Boot 测试上下文缓存导致的偶发失败
diff --git a/server/src/test/java/org/apache/seata/server/logging/AppenderTest.java b/server/src/test/java/org/apache/seata/server/logging/AppenderTest.java
index bf9a1a3fd2..c885617eb2 100644
--- a/server/src/test/java/org/apache/seata/server/logging/AppenderTest.java
+++ b/server/src/test/java/org/apache/seata/server/logging/AppenderTest.java
@@ -16,32 +16,120 @@
  */
 package org.apache.seata.server.logging;
 
+import ch.qos.logback.classic.Level;
 import ch.qos.logback.classic.LoggerContext;
 import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.classic.spi.LoggingEvent;
 import ch.qos.logback.core.Appender;
 import com.github.danielwegener.logback.kafka.KafkaAppender;
 import net.logstash.logback.appender.LogstashTcpSocketAppender;
 import org.apache.seata.server.BaseSpringBootTest;
 import org.apache.seata.server.logging.logback.appender.MetricLogbackAppender;
+import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.slf4j.LoggerFactory;
 import org.springframework.test.context.TestPropertySource;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
 import java.lang.reflect.Field;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
 import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 @TestPropertySource(
         properties = {
             "logging.extend.logstash-appender.enabled=true",
+            "logging.extend.logstash-appender.destination=${seata.test.logstash.destination}",
             "logging.extend.kafka-appender.enabled=true",
             "logging.extend.kafka-appender.topic=test",
             "logging.extend.metric-appender.enabled=true"
         })
 public class AppenderTest extends BaseSpringBootTest {
+    private static final String DESTINATION_PROPERTY = "seata.test.logstash.destination";
+    private static final String ORIGINAL_DESTINATION = System.getProperty(DESTINATION_PROPERTY);
+    private static final BlockingQueue<String> RECEIVED = new LinkedBlockingQueue<>();
+    private static final AtomicReference<Throwable> SINK_FAILURE = new AtomicReference<>();
+    private static final Set<Socket> CONNECTIONS = ConcurrentHashMap.newKeySet();
+    private static final ExecutorService SINK_THREADS = Executors.newCachedThreadPool(task -> {
+        Thread thread = new Thread(task, "logstash-test-sink");
+        thread.setDaemon(true);
+        return thread;
+    });
+    private static final ServerSocket SINK = startSink();
+
+    private static ServerSocket startSink() {
+        try {
+            ServerSocket server = new ServerSocket(0, 50, InetAddress.getByName("127.0.0.1"));
+            System.setProperty(DESTINATION_PROPERTY, "127.0.0.1:" + server.getLocalPort());
+            SINK_THREADS.submit(() -> {
+                try {
+                    while (!server.isClosed()) {
+                        Socket connection = server.accept();
+                        CONNECTIONS.add(connection);
+                        SINK_THREADS.submit(() -> {
+                            try (Socket socket = connection;
+                                    BufferedReader reader = new BufferedReader(
+                                            new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))) {
+                                String line;
+                                while ((line = reader.readLine()) != null) {
+                                    RECEIVED.add(line);
+                                }
+                            } catch (IOException error) {
+                                if (!server.isClosed()) {
+                                    SINK_FAILURE.compareAndSet(null, error);
+                                }
+                            } finally {
+                                CONNECTIONS.remove(connection);
+                            }
+                        });
+                    }
+                } catch (IOException error) {
+                    if (!server.isClosed()) {
+                        SINK_FAILURE.compareAndSet(null, error);
+                    }
+                }
+            });
+            return server;
+        } catch (IOException error) {
+            throw new IllegalStateException("Cannot start Logstash test sink", error);
+        }
+    }
+
+    @AfterAll
+    static void closeSink() throws Exception {
+        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+        Appender<ILoggingEvent> appender = context.getLogger("ROOT").getAppender("LOGSTASH");
+        if (appender != null) {
+            appender.stop();
+        }
+        SINK.close();
+        for (Socket connection : CONNECTIONS) {
+            connection.close();
+        }
+        SINK_THREADS.shutdownNow();
+        Assertions.assertTrue(SINK_THREADS.awaitTermination(5, TimeUnit.SECONDS));
+        if (ORIGINAL_DESTINATION == null) {
+            System.clearProperty(DESTINATION_PROPERTY);
+        } else {
+            System.setProperty(DESTINATION_PROPERTY, ORIGINAL_DESTINATION);
+        }
+    }
 
     @Test
-    public void testAppenderEnabled() {
+    public void testAppenderEnabled() throws InterruptedException {
         LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
         Iterator<Appender<ILoggingEvent>> appenderIterator =
                 lc.getLogger("ROOT").iteratorForAppenders();
@@ -85,6 +173,19 @@ public class AppenderTest extends BaseSpringBootTest {
         Assertions.assertTrue(kafkaFound);
         Assertions.assertTrue(metricFound);
         Assertions.assertTrue(logstashFound);
+        String marker = "logstash-fixture-marker";
+        lc.getLogger("ROOT")
+                .getAppender("LOGSTASH")
+                .doAppend(new LoggingEvent(
+                        getClass().getName(), lc.getLogger(getClass()), Level.INFO, marker, null, null));
+        long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(5);
+        boolean delivered = false;
+        while (!delivered && System.nanoTime() < deadline) {
+            String line = RECEIVED.poll(100, TimeUnit.MILLISECONDS);
+            delivered = line != null && line.contains(marker);
+        }
+        Assertions.assertNull(SINK_FAILURE.get());
+        Assertions.assertTrue(delivered, "The configured Logstash appender must deliver the event to the local sink");
     }
 
     private static Field getDeclaredFieldRecursive(Class<?> clazz, String fieldName) throws NoSuchFieldException {


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@seata.apache.org
For additional commands, e-mail: notifications-help@seata.apache.org